MVCC是不加锁实现多个事务对数据库的并发访问
浅显原理:
在每一行除了本来的数据外附加了两列,一列是创建行时的版本号ID一列是删除时的版本号ID
id | name | create-version | delete-version |
---|---|---|---|
1 | tom |
- 如果现在事务ID为1的事务插入了一行数据就会有
id | name | create-version | delete-version |
---|---|---|---|
1 | tom |
将createversion修改为当前的事务id
- 如果现在事务ID为2的事务改了id = 1的name
id | name | create-version | delete-version |
---|---|---|---|
1 | tom | 1 | 2 |
1 | mike | 2 |
会把之前的删除版本号填上自己的,并且代替原来的创建版本号
查询过滤条件:
-
只有创建版本号小于或等于当前事务id,即事务id为2的事务只能读取到create version<=2的已提交的事务的数据集
-
删除版本号未指定,或者删除版本号大于当前事务id,就是说当前事务时,确保这个数据没有被删除。
底层原理(复杂原理)
首先在每个事务对数据库某一行进行修改的时候,在这一行上还会有附加的事务id(DB_TRX_ID)和回滚指针(DB_ROLL_PTR)
(先不用管隐式主键不重要)
当我们事务对数据库进行修改的时候,新的回滚指针指向上一次的版本,把老的放到undo日志里面,这样就会形成一条版本引用链
如下图
我们从上到下按时间顺序搞三个事务:
看select1的第一个select,因为事务100和事务200未对account表操作,很显然id = 1就是lilei300
再看第二次select,这时候就得用生成的readview结合一定的过滤条件去版本链里面找哪个版本的lilei合适
先说readview,MVCC只适用于可重复读和读已提交,可重复读是一直沿用第一次select生成的readview,读已提交是每一次查找都生成新的readview。那么readview是什么呢
readview
它由未提交的事务id数组(里面最小的事务id叫min_id)和已知的最大事务id(max_id)组成,每次都是拿这个readview按照一定的规则在版本链中去找哪一行数据合适,可见:
会根据min_id,max_id将整个事务版本号集合划分为:
版本链比对规则:
1.如果落在绿色部分(trx id<min id),表示个版本是已提交的事务生成的,这个数据是可见的
2.如果落在红色部分(trx id > max_id),表示这个版本是由将来启动的事务生成的,是肯定不可见的
3.如果落在黄色部分 max id),那就包括两种情况
a.若row的 trx id在数组中,表示这个版本是由还没提交的事务生成的,不可见,当前自己的事务是可见的
b.若rowtrx的 id不在数组中,表示这个版本是已经提交了的事务生成的,可见。
再来看select 1中的第二条查询:
readview = [100,200]300;
在版本链中按照此规则找出lilei300