写下这篇博客的原因
写下这篇文章是因为有一次有面试,面试官问到了mysql的默认隔离级别(可重复读)有没有什么方法可以解决SERIALIZABLE(串行化)才能解决的幻读问题,这一下子给我问到了,因为按照mysql的四个隔离级别来讲,只有串行化的这个隔离级别是可以解决幻读的。
下面我们详细看一下什么是间隙锁,mysql的可重复度隔离级别+间隙锁为什么可以解决幻读问题。
什么是间隙锁
间隙锁是mysql默认的存储引擎InnoDB的一个特性,在mysql引入InnoDB作为事务性存储引擎,就可以使用间隙锁了,也就是说在mysql4.0引入InnoDB后,mysql4.0以上的版本间隙锁就可使用。
但是在mysql4.0时,InnoDB还并不是mysql的默认存储引擎,在mysql5.1开始,将InnoDB作为的默认存储引擎。从此开始,间隙锁的使用更加普遍。
间隙锁是对表中数据一个区间的锁定,防止其他事务在这个区间插入新的记录。
举个例子:现在有一张student表,表中有age这个字段,表中数据有3-25岁的数据若干条。
我现在想要查询age大于10的所有记录,我这个查询的操作,称为事务A,这时候事务A间隙锁就会对所有大于10的所有记录进行加锁,同时也会对大于10后的数据,记录之间没有数据的区间,例如:10-20之间,13和15没有14这条数据,那么就会对13和15这个区间进行加锁,这时候如果有事务B尝试插入一个值为14的记录,就会和事务A持有的间隙锁冲突,事务B的这个操作将会阻塞到事务A执行成功,这就是间隙锁。
mysql的可重复读隔离级别+间隙锁为什么可以解决幻读
说白了,幻读就是在一个事务操作中,多次按照相同的where条件进行查询,由于其他的事务在并发事务中插入了符合条件的数据,使得每次查询的结果集不一样,这样做就违反了隔离原则,导致数据不一致,就造成了幻读。
这样看,我们上面说的间隙锁是完全可以解决幻读问题的。
使用间隙锁会造成什么问题?优缺点
优点:让我们可以在不修改mysql的默认隔离级别的情况下,解决了幻读问题。
缺点:
1. 性能问题:间隙锁可能锁定的范围比较大,会增加锁的数量和时间,会对并发性能有影响。
2. 可能导致死锁:多个事务进行并发操作,事务间可能会导致死锁,这需要开发或者数据库相关人员进行关注。
使用的场景
推荐使用的场景:
只推荐在数据一致性要求比较高的情况下使用,还有就是范围查询和多次读区需要的结果集需要一样的sql比较多的时候。
举个例子:有一个在线银行系统,用于执行金融贸易,例如用户转账,支付等,这种项目肯定不能出错,必须保证系统的所有操作一致和正确,不允许出现幻读。
例如,一个用户要转出一笔钱,但账户里不足以支付两倍金额。举例一个简单的场景:
- 事务A开始,并准备扣除用户账户的一定数额。
- 事务A查询账户余额,并确认余额充足。
- 同时,事务B也开始了,并尝试给同一个用户的账户打入一笔钱。
- 如果事务A不使用间隙锁,那么事务B可能成功插入一笔记录,影响余额。
- 事务A再次查询余额,发现账户里的钱多了,误以为还可以再转一次钱,可能会造成重复扣款。
不推荐使用的场景:
不推荐那种并发插入比较多的情况,单纯的CRUD项目或者对隔离性要求不高的项目。
举个例子:
设想一个在线票务系统,它支持用户购买演唱会门票。在热门活动的开票瞬间,可能有成千上万的用户尝试为同一个事件购买门票。
- 事务A开始并查询了剩余票数,这个查询操作涉及一定范围内的记录。
- 间隙锁锁住了这个范围的查询,防止其他用户同时插入新的订单。
- 事务B、C和D等等都在等待事务A完成以获取它们各自的锁。
在高并发的场景下,间隙锁会导致性能瓶颈
总之:是否使用间隙锁或者其他锁,要根据当前应用的特性和需求场景。