1怎么利用锁解决并发事务带来的问题?
1. 解决脏读:
解释: 事务B在更新数据时,加上了X锁,则其他事务就不能读取和操作这条数据,事务A的select是不能直接从原表中查的,这时事务A会从快照中查询,因为快照中的数据是事务B 更新前保留的原始数据,所以事务A查到的结果不是18,而是16。
2. 解决不可重复读
解释: 事务A第一次查询的时候加了一把共享锁,此时其他事务只能读取,不能操作数据,所以事务B的update操作会等待事务A的锁释放了才能执行,所以事务A第二次查询的时候还是读取到16
3. 解决幻读
Innodb在默认隔离级别RR级别是,select 索引范围查找会加上临键锁,临键锁能解决幻读问题,详见临键锁
4. 死锁
多个并发事务(2个或者以上);
每个事务都持有锁(或者是已经在等待锁);
每个事务都需要再继续持有锁;
事务之间产生加锁的循环等待,形成死锁。
4.1. 案例:
BEGIN;
update users set lastUpdate = NOW() where id =1; --A: 拿到users表的id=1这行的排他锁,下一步想拿到t2表id=1的排他锁,需要等B释放锁
update t2 set `name`='test' where id =1;
-- 其他会话
BEGIN;
update t2 set `name`='test' where id =1; --B: 拿到了t2表的id=1这行的排他锁,下一步想拿到users表id=1的排他锁,需要等A释放锁
update users set lastUpdate = NOW() where id =1;
上面的请求就出现了A等B 释放锁,B 等A释放锁。一直处于等待状态。可以通过kill连接id的方式解决。
4.2. 死锁的避免
1)类似的业务逻辑以固定的顺序访问表和行。
2)大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
3)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
4)降低隔离级别,如果业务允许,将隔离级别调低也是较好的选择
5)为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添 加上锁(或者说是表锁)
参考书籍:高性能MySQL(第3版)、高性能mysql