5. MVCC就是用undolog回退
Last updated
Was this helpful?
Last updated
Was this helpful?
有一种叫"视图" / "read-view"的新鲜玩意, 视图就是快照, 实现的本质是在当前状态下, 通过undo-log回到之前的某一瞬间. 因此针对某一条记录, 我们能通过不同的undo-log查到之前不同的值, 换句话说, 我们就能保存某一条记录好几个版本的值, 给它起个新名字叫多版本控制/MVCC
(可重复读) > 早上8:01 - tx1- 将k更新成10 早上8:02 - tx2- 将k更新成15 早上8:03 - tx3- 将k更新成20
举个例子来说, 三个事务分别有id1/2/3, 三个人分别做了三次更新, 当前buffer pool里的值已经被更新成20了, 如果此时tx1想查值, 在可重复读的前提下他会这样说: "帮我查k的值, 我的版本号是v1, 我只认比我小的, 已提交的记录, 不认比我大的" 那么数据库会先从buffer pool里查出现在的值20, 并借助undo-log开始回退:
查出k的更新记录v3, 将20回退至15
查出k的更新记录v2, 将15回退至10, 因此返回k=10
我们把上面划重点的句子展开一下:
任何事务在发生的时候都会记录这一时刻下所有正在运行中尚未提交的事务, 我们叫它活跃事务, 活跃事务所做的所有修改在他眼里看来都是无效的(因为你没提交), 在借助undo-log做回退的时候, 这些活跃事务的行为会被一并回退
而那些不在活跃数组里的, ID却更大的也会被回退, 因为我发生的时候还没有你, 因此你做的改动在我眼里当然是判无效的
因此任何人在查数据的时候, Buffer Pool里的都不一定是你现在就能用的, 拿着这个当前值, 对比所有的undo-log, 生成历史值, 开始回退:
如果这个ID比自己大, 不认, 因为我发生的时候还没有你, 回退
如果这个ID比自己小, 但在自己的活跃事务组里, 不认, 因为我发生的时候你还没提交, 因此你做的任何事在我看来都是不合法的, 回退
如果这个ID比自己小, 但并不在自己的活跃事务组里, 认, 因为这说明我发生的时候他已经提交了, 因此这个值就是合法值
这个时候我们就能回答上面的问题了: 事务的数据源, 更新方向全都是Buffer Pool, 只是在查的时候会配合undo-log一起用, 查到自己这个版本的值
如果你保存的undo-log足够多, 那么你就能回到无限久远之前的值, 但实际上我们显然不会保存那么久 ~~ (如果你真想回到很久比如几天之前, 你不应该依赖undolog, 而是应该依赖备份+binlog去实现, DBA就是靠这个去回滚到几天前的) ~~ , 因为undolog的定位是给事务做回滚用的, 即只要能保证当前事务, 能查到之前是什么样就够了:
事务想回滚 - 查一下事务开始之前是什么值
事务想查某时刻下快照的值 - 查一下这个时刻下是什么值
因此undo-log是为了事务而存在的, 只要事务认为有需要, undolog就会一直保留记录, 只要事务认为不需要了, undolog也不会保存那么多记录. 那什么情况下算是"事务有需要", 什么情况算是"事务没需要"?
还是上面的那个例子, tx1可能会查这一瞬间下的任何值, 那么我们就要记录下从8:01开始的所有改动记录, 以方便某条记录回到8:01, 那8:00的呢? 因为没有任何事务想知道8:00是什么值, 因此8:01之前所有的undo-log都被清除掉了.