InnoDB事务与锁
话不多说,直接上知识点:
事务
特性
1、原子性(Atomicity)
2、一致性(Consistency)
3、隔离性(Isolation)
4、持久性(Durability)
InnoDB中引擎对隔离级别的支持
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(Read Uncommitted) | 可能 | 可能 | 可能 |
读已经提交(Read Committed) | 不可能 | 可能 | 可能 |
可重复读(Repeatable Read) | 不可能 | 不可能 | 对InnoDB不可能 |
串行化(Serilalizable) | 不可能 | 不可能 | 不可能 |
为什么InnoDB不可能?请继续看(记得打开自己的mysql工具实际操作一波)
锁
锁是用于管理不同事务对共享资源的并发访问
行锁VS表锁
锁定粒度:行锁<表锁
加锁效率:行锁>表锁
概率冲突:行锁<表锁
并发性能:行锁>表锁
InnoDB支持行锁和表锁(特殊的行锁 )
InnoDB锁类型
共享锁(S)排他锁(X)
共享锁: 读锁,S锁。多个事务对同一数据都可以访问,共享一把锁,但是只能读不能改。
加锁方式: select * from sku where id =1 LOCK IN SHARE MODE;
排他锁: 写锁,X锁。不与其他锁共存,如果一个事务获取了该行的排他锁,则其他事务不能再获取该行的锁(S锁或X锁),只有此事务可以进行读取和修改。那其他事务一定不能读吗?(不一定,可进行快照读)
加锁方式:delete/insert/update默认加x锁。
select * from sku where id = 1 FOR UPDATE;
意向共享锁(IS)意向排他锁(IX)
意向共享锁: 事务准备给数据行加S锁,加之前必须获得IS锁,锁之间可兼容。
意向排他锁: 事务准备给数据行加X锁,加之前必须获得IX锁,锁之间可兼容.
加锁方式:InnoDB在数据操作前自动加的,不需要用户干预。
那有什么意义:当事务想去锁表,先去判断是否该锁已经存在,如果存在则快速返回不能锁表。
满足了事务隔离性!
临键锁&间隙锁&记录锁
临键锁: RR级别。锁记录+区间**(左开右闭)**
按照索引检索,范围查找(bewteen, < ,>)有数据命中
(-∞,1],(1,4],(4,7],(7,10],(10,+∞)
select * from tb where id >5 and id <9 for update;
锁住:(4,7],(7,10]
间隙锁: RR级别。 锁住索引不存在的区间**(左开右开)**
临键锁记录不存在退化成间隙锁
(-∞,1),(1,4),(4,7),(7,10),(10,+∞)
select * from tb where id >4 and id < 6 for update;
select * from tb where id = 6 for update;
锁住:(4,7)
select * from tb where id > 30 for update;
锁住 : (10,+∞)
记录锁: 按照唯一性(主键或者唯一)索引检索。锁住具体的索引项
如何避免脏读?
更新增加X锁,防止其他事务读或者写
-- --事务1 解决脏读
START TRANSACTION;
select * from user where id = 1;
COMMIT;
ROLLBACK;
-- 事务2 解决脏读
START TRANSACTION;
update user set age = 16 where id = 1; -- 加X锁 防止其他事务读或者写
COMMIT;
ROLLBACK;
如何避免不可重复读?
查询加S锁,防止其他事务update
-- 事务1 解决不可重复读
START TRANSACTION;
select * from user where id = 1 ; -- 加S锁 结果??
COMMIT;
ROLLBACK;
-- 事务2 解决不可重复读(读取数据不允许其他事务进行update)
update user set age = 20 where id = 1;
如何避免幻读?
临键锁,防止其他事务insert/delete
-- 事务1 解决幻读
START TRANSACTION;
select * from user where id >0;
COMMIT;
ROLLBACK;
-- 事务2 解决幻读(读取数据不允许其他事务进行insert/delete)
START TRANSACTION;
insert into user values(2,'Two',22);
COMMIT;
ROLLBACK;
MVCC
Multi-Version Concurrency Control 多版本并发控制,通过在每行记录后面保存两个隐藏的列来实现的。
insert
当前版本号作为数据行版本号
delete
当前版本号作为行删除版本号
update
当前版本号作为数据行版本号,同时保持当前版本号作为原来的行的删除版本号
select
1 、查询小于等于当前事务版本号
2、 要么删除版本号为null ,
要么大于当前版本号
只适用于RR、RC级别
如图示:后面两隐藏列实际中并不可见。
举个例子:(最好实际跑一下)
insert into mvccuser(name,age) value ('One',18);
insert into mvccuser(name,age) value ('Two',20);
-- 事务1
START TRANSACTION; -- 1
select * from mvccuser; -- 2
COMMIT;
ROLLBACK;
-- 事务2
START TRANSACTION; -- 3
update mvccuser set age = 28 where id = 1; -- 4
COMMIT;
ROLLBACK;
执行顺序:12342
执行前:
执行12,此时txid=2时,查找结果:
id | name | age |
---|---|---|
1 | One | 18 |
2 | Two | 20 |
执行3、4,此时txid=3,此时表结果:
再执行2,此时txid=2,查找结果:
id | name | age |
---|---|---|
1 | One | 18 |
2 | Two | 20 |
执行顺序:3412
执行34,此时txid=2,结果:
再执行12此时txid=3,再得到结果之前我们先猜测一下结果:
根据mvcc的select规则查询结果,应该是:
id | name | age |
---|---|---|
1 | One | 28 |
2 | Two | 20 |
然而实际结果确是:
id | name | age |
---|---|---|
1 | One | 18 |
2 | Two | 20 |
为什么??这就是和mysql的另一机制有关了。
接下来我们简单介绍下Undo Log
Undo Log
事务开始之前,操作任何数据之前,将需要操作的数据备份到一个地方(Undo Log),当事务执行过程中出现错误或者ROLLBACK,MySQL利用备份将数据恢复到开始事务之前。
Undo Log保证事务的原子性.
在InnoDB引擎中实现多版本并发控制。
事务未提交前,undolog中数据可以作为数据旧版本提供对其他并发事务实现快照读
除了Undo log之外,MySQL还有另外一种日志redo log
Redo Log
在事务执行过程中,将最新的数据备份到一个地方(Redo Log),
在发生故障的时候,还有脏页未刷到磁盘,在重启mysql的时候,根据Redo log 重做,从而达到事务的未落磁盘数据进行持久化的这一特性。
Redo Log实现事务的持久性。
、
留给读者一个思考题:请问Redo Log和binglog有什么区别你能说出来吗?
以上乃个人学习总结,若有不对之处,欢迎指出,谢谢阅读!