在Hyperledger Fabric中,MVCC(Multi-Version Concurrency Control,多版本并发控制)是一个重要的机制,用于管理区块链上的数据版本。MVCC的目标是允许多个事务并发执行,而不会导致数据一致性问题。以下是有关Fabric中MVCC问题的详细介绍:
MVCC冲突(Multi-Version Concurrency Control)在Hyperledger Fabric或其他使用MVCC机制的区块链系统中,通常在以下情况下会发生:
需要注意的是,MVCC冲突并不是一种错误,而是一个分布式系统中需要处理的正常情况。系统通过检测和解决这些冲突,确保事务的一致性和正确性。在出现冲突时,通常需要有一定的机制来处理,例如回滚事务并重新执行,以确保所有的事务都基于最新的数据状态。
在Hyperledger Fabric中,出块前背书节点会对接收到的交易进行验证,最新(2023年12月2日)代码的实现位于core/ledger/kvledger/txmgmt/validation
目录下的validator.go
中,具体实现如下:
// validateKVRead performs mvcc check for a key read during transaction simulation.
// i.e., it checks whether a key/version combination is already updated in the statedb (by an already committed block)
// or in the updates (by a preceding valid transaction in the current block)
func (v *validator) validateKVRead(ns string, kvRead *kvrwset.KVRead, updates *privacyenabledstate.PubUpdateBatch) (bool, error) {
readVersion := rwsetutil.NewVersion(kvRead.Version)
if updates.Exists(ns, kvRead.Key) {
logger.Warnw("Transaction invalidation due to version mismatch, key in readset has been updated in a prior transaction in this block",
"namespace", ns, "key", kvRead.Key, "readVersion", readVersion)
return false, nil
}
committedVersion, err := v.db.GetVersion(ns, kvRead.Key)
if err != nil {
return false, err
}
logger.Debugw("Comparing readset version to committed version",
"namespace", ns, "key", kvRead.Key, "readVersion", readVersion, "committedVersion", committedVersion)
if !version.AreSame(committedVersion, readVersion) {
logger.Warnw("Transaction invalidation due to version mismatch, readset version does not match committed version",
"namespace", ns, "key", kvRead.Key, "readVersion", readVersion, "committedVersion", committedVersion)
return false, nil
}
return true, nil
}
!version.AreSame(committedVersion, readVersion)
会验证读写集中的版本与世界状态中的是否一致,不一致会导致交易交验失败。committedVersion
和readVersion
类型为version.Height
,其定义位于core/ledger/internal/version
:
// Height represents the height of a transaction in blockchain
type Height struct {
BlockNum uint64
TxNum uint64
}
// NewHeight constructs a new instance of Height
func NewHeight(blockNum, txNum uint64) *Height {
return &Height{blockNum, txNum}
}
// NewHeightFromBytes constructs a new instance of Height from serialized bytes
func NewHeightFromBytes(b []byte) (*Height, int, error) {
blockNum, n1, err := util.DecodeOrderPreservingVarUint64(b)
if err != nil {
return nil, -1, err
}
txNum, n2, err := util.DecodeOrderPreservingVarUint64(b[n1:])
if err != nil {
return nil, -1, err
}
return NewHeight(blockNum, txNum), n1 + n2, nil
}
// ToBytes serializes the Height
func (h *Height) ToBytes() []byte {
blockNumBytes := util.EncodeOrderPreservingVarUint64(h.BlockNum)
txNumBytes := util.EncodeOrderPreservingVarUint64(h.TxNum)
return append(blockNumBytes, txNumBytes...)
}
// Compare returns -1, zero, or +1 based on whether this height is
// less than, equals to, or greater than the specified height respectively.
func (h *Height) Compare(h1 *Height) int {
switch {
case h.BlockNum < h1.BlockNum:
return -1
case h.BlockNum > h1.BlockNum:
return 1
case h.TxNum < h1.TxNum:
return -1
case h.TxNum > h1.TxNum:
return 1
default:
return 0
}
}
// String returns string for printing
func (h *Height) String() string {
if h == nil {
return "<nil>"
}
return fmt.Sprintf("{BlockNum: %d, TxNum: %d}", h.BlockNum, h.TxNum)
}
// AreSame returns true if both the heights are either nil or equal
func AreSame(h1 *Height, h2 *Height) bool {
if h1 == nil {
return h2 == nil
}
if h2 == nil {
return false
}
return h1.Compare(h2) == 0
}
我正在参与2023腾讯技术创作特训营第四期有奖征文,快来和我瓜分大奖!
声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。