MySQL事务与乐观锁
- 2019 年 11 月 26 日
- 笔记
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sxllllwd/article/details/102892055
最近感觉自己好像干了一件蠢事,写了一个事务包含A和B两个操作,然后又在A中加了乐观锁,导致失败率特别高。因此重新看了事务与乐观锁的资料。
一次封锁 两段锁
一次封锁法,就是方法的开始阶段,已经预先知道会用到哪些数据,然后全部锁住,在方法运行之后,再全部解锁。可以有效避免循环死锁。
两段锁协议
加锁阶段和解锁阶段。
加锁阶段:在任何数据进行读操作之前都有申请获得S锁,在进行写操作之前要申请并获得X锁,加锁不成功,则事务进入等待状态,直到加锁成功才继续。
解锁阶段:当事务释放了一个封锁之后,事务进入解锁阶段,在该阶段只能进行解锁操作而不能再加锁。
两段锁协议可以保证事务的并发调度串行化(串行化很重要,尤其是在数据恢复和备份的时候),但是无法避免死锁。
Update加行锁
如果update更新的where语句中的筛选条件没有索引,会导致MYSQL给整张表的所有数据加行锁。在SQL运行过程中,mysql并不知道哪些数据行是符合where条件的(没有索引)。如果一个条件无法通过索引快速过滤,存储引擎层面就会将所有记录加锁后返回,再由MYSQL层进行过滤。
但是实际使用过程中,mysql做了一些改进,在MYSQL过滤条件,发现不满足之后,会调用unlock_row方法,把不满足条件的纪录释放锁(违背了二段锁协议的约束)。这样做,保证了最后只会持有满足条件纪录上的锁。但是每条记录的加锁操作还是不能省略的。
这种情况同样适用于MYSQL的默认隔离级别可重复读。对一个数据量很大的表做批量修改的时候,如果无法使用相应的索引,MYSQL 过滤数据的时候特别慢,就会出现虽然没有修改某些行的数据,但是它们还是被锁住了。
快照读与当前读
快照读很可能读取的是历史数据,而不是数据库当前数据。
在MVCC中:
- 快照读:就是select
- select * from table ….;
- 当前读:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁。
- select * from table where ? lock in share mode;
- select * from table where ? for update;
- insert;
- update ;
- delete;
Next-Key锁
行锁防止别的事务修改或删除,GAP锁防止别的事务新增,行锁和GAP锁结合形成的的Next-Key锁共同解决了RR级别在写数据时的幻读问题。
参考文档: