MVCC在不同的数据库之中的应用
MVCC
高并发情况, 应对数据冲突问题,一般两种方式:
- 见招拆招,避免冲突,使用 悲观锁 确保同时刻只能一个线程对数据进行修改。(Read/Write Locks、Two-Phase Locking)
- 开门迎客,允许冲突,但发生冲突的时候,要有能力检测到。(乐观锁)先认为不会发生冲突、除非检测到当前确实冲突了。(Logical Clock、MVCC : Multi-Version Concurrent Control )
MVCC
核心思想: 一个数据多个历史版本,从而解决事务管理中数据隔离的问题。
版本 —> 一般选择时间戳 或者 事务ID 标识
在处理写请求的时候, MVCC 是为数据添加一个新版本的数据;在读取数据的时候,先确定读取的版本,根据版本找到对应的数据。
https://dbdb.io/browse?concurrency-control=multi-version-concurrency-control-mvcc&q=
MYSQL中 MVCC
InnoDB 内部为每一行记录添加 三个隐藏的列,
- DB_TRX_ID 创建记录/最后更新记录的事务ID,(删除也被视作更新,设置行的特殊位标记位删除)
- DB_ROLL_PTR 回滚指针,执行这条记录的上一个版本(rollback segment : undo log,记录了更新前重建该记录的所需的信息)
- DB_ROW_ID 隐藏自增ID, 如果没有主键,就会以DB_ROW_ID 产生聚簇索引,随着插入新行,自增的,
rollback segment 分为两种
- inert undo log ,仅在事务回滚的时候需要,事务提交后立即丢弃
- update undo log ,用于一致性读取,只有在 没有事务存在,且
头信息里面还有一个 delete_flag (bit) 标记为该记录是否删除
[待丰富]…..
ETCD 中 MVCC
ETCD 使用 revision 作为一条记录的版本,revision 对于 etcd 的集群来说就像 逻辑时钟,每个数据发生更改做更新。
// A revision indicates modification of the key-value space.
// The set of changes that share same main revision changes the key-value space atomically.
type revision struct {
// 同一个事务 共享 main
// main is the main revision of a set of changes that happen atomically.
main int64
// 同一个事务下,每次操作(Put、Delete)sub 会递增,从 0 开始
// sub is the the sub revision of a change in a set of changes that happen
// atomically. Each change has different increasing sub revision in that
// set.
sub int64
}
etcd 的 TreeIndex 其实是一个 BTree,
type treeIndex struct {
sync.RWMutex
tree *btree.BTree
}
里面的内存索引 keyIndex
type keyIndex struct {
// 用户原始的key
key []byte
// 记录了最后一次修改对应的revision
modified revision // the main rev of the last modification
// 存储了多版本信息
generations []generation
}
// generation contains multiple revisions of a key.
type generation struct {
ver int64
created revision // when the generation is created (put in first revision).
// 当在创建完毕后持续操作这个key的时候,这边 会追加 revision
revs []revision
}
好,查询内存 Btree,根据Key找到 keyIndex.generations[i].revs 找到最后一条 revision
ok 回过头再来看 revision
存储到 bolt 的结果是PB格式
type KeyValue struct {
// key is the key in bytes. An empty key is not allowed.
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// create_revision is the revision of last creation on this key.
CreateRevision int64 `protobuf:"varint,2,opt,name=create_revision,json=createRevision,proto3" json:"create_revision,omitempty"`
// mod_revision is the revision of last modification on this key.
ModRevision int64 `protobuf:"varint,3,opt,name=mod_revision,json=modRevision,proto3" json:"mod_revision,omitempty"`
// version is the version of the key. A deletion resets
// the version to zero and any modification of the key
// increases its version.
Version int64 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"`
// value is the value held by the key, in bytes.
Value []byte `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"`
// lease is the ID of the lease that attached to key.
// When the attached lease expires, the key will be deleted.
// If lease is 0, then no lease is attached to the key.
Lease int64 `protobuf:"varint,6,opt,name=lease,proto3" json:"lease,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
结合 revision 和 KeyValue ,以 revision 为索引,以 B+ Tree 存储 KeyValue 的所有的变化, 这样就可以找到 一个用户指定的 key 之前的历史数据。