insert undo只在事件回滚时起作用,当事件提交后,该类型的undo日志就没用了,它占用的Undo Log Segment也会被系统回收(也就是该undo日志占用的Undo页面链表要么被重用,要么被释放)。
假设之后两个事件id分别为10、20的事件对这条记载举行UPDATE 利用,利用流程如下:
发生时间次序事件10事件201BEGIN;2BEGIN;3UPDATE student SET name=“李四”
WHERE id=1;4UPDATE student SET name=“王五”
WHERE id=1;5COMMIT;6UPDATE student SET name=“钱七”
WHERE id=1;7UPDATE student SET name=“宋八”
WHERE id=1;8COMMIT; 能不能在两个事件中交错更新同一条记载呢?
不能!这不就是一个事件修改了另一个未提交事件修改过的数据,脏写。
InnoDB利用锁来保证不会有脏写环境的发生,也就是在第一个事件更新了某条记载后,就会给这条记载加锁,另一个事件再次更新时就必要等待第一个事件提交了,把锁释放之后才可以继续更新。 InnoDB增删改默认加x锁,查默认不加锁
每次对记载举行改动,都会记载一条undo日志,每条undo日志也都有一个roll_pointer属性(INSERT利用对应的undo日志没有该属性,因为该记载并没有更早的版本),可以将这些undo日志都连起来,串成一个链表:
update写的时间是默认加了X锁的,20会等待10
假设如今有事件 A 和事件 B 并发实行, 事件 A 的事件 id 为 20 , 事件 B 的事件 id 为 30 。
步骤1:事件 A 开始第一次查询数据,查询的 SQL 语句如下
select * from student where id >= 1;
复制代码
在开始查询之前,MySQL 会为事件 A 产生一个 ReadView,此时 ReadView 的内容如下: trx_ids=[20,30] ,up_limit_id=20 , low_limit_id=31 , creator_trx_id=20 。
由于此时表 student 中只有一条数据,且符合 where id>=1 条件,因此会查询出来。然后根据 ReadView机制,发现该行数据的trx_id=10,小于事件 A 的 ReadView 里 up_limit_id,这表示这条数据是事件 A 开启之前,其他事件就已经提交了的数据,因此事件 A 可以读取到。
结论:事件 A 的第一次查询,能读取到一条数据,id=1。
步骤2:接着事件 B(trx_id=30),往表 student 中新插入两条数据,并提交事件
insert into student(id,name) values(2,'李四');
insert into student(id,name) values(3,'王五');
复制代码
此时表student 中就有三条数据了,对应的 undo 如下图所示:
步骤3:接着事件 A 开启第二次查询,根据可重复读隔离级别的规则,此时事件 A 并不会再重新生成ReadView。此时表 student 中的 3 条数据都满意 where id>=1 的条件,因此会先查出来。然后根据ReadView 机制,判定每条数据是不是都可以被事件 A 看到。
1)首先 id=1 的这条数据,前面已经说过了,可以被事件 A 看到。
2)然后是 id=2 的数据,它的 trx_id=30,此时事件 A 发现,这个值处于 up_limit_id 和 low_limit_id 之间,因此还必要再判定 30 是否处于 trx_ids 数组内。由于事件 A 的 trx_ids=[20,30],因此在数组内,这表示 id=2 的这条数据是与事件 A 在同一时候启动的其他事件提交的,所以这条数据不能让事件 A 看到
3)同理,id=3 的这条数据,trx_id 也为 30,因此也不能被事件 A 看见
结论:最终事件 A 的第二次查询,只能查询出 id=1 的这条数据。这和事件 A 的第一次查询的效果是一样的,因此没有出现幻读征象,所以说在 MySQL 的可重复读隔离级别下,不存在幻读问题。
6. 总结