MySQL相关文章
并发问题
- 脏读
- 当前事务读到了其他事务保存但未提交的数据
- 不可重复读
- 当前事务读到了其他事务update的数据
- 与脏读的区别是,不可重复读读到的是已提交的事务,脏读读到的是未提交的数据。
- 比如:开一个事务修改了某条数据,提交事务的时候发现这条数据已经被别的事务修改了
- 幻读
- 当前事务读到了其他事务提交的insert、delete数据
隔离级别
- 读未提交,会产生脏读、不可重复读、幻读
- 读已提交,会产生不可重复读、幻读
- 可重复度,会产生幻读
- 串行化,不会发生并发问题
按照八股文里mysql隔离级别默认情况下为可重复读
,可重复读隔离机制下避免了脏读,不可重复读,日常开发里却并没有出现过幻读,看似是MVCC多版本并发控制帮我们避开了幻读。
其实另有隐情,从一个小实验中就可以看出来,新建一个dept表存id和name字段。
分别进行下面两组事务操作:
开始两个表里都没有数据,右边事务里插入一条数据,显而易见因为mvcc特性左边的select数据一直不变。
左边事务虽然查不到,向表中插入id=29的数据会被锁住。
这是因为在select时会使用到mvcc机制的快照读,读取的是某个当前事务对应版本内的内容,但是在insert时会进行当前读,访问数据表中“最新”的数据,这时尽管右边的insert未提交,但当前读还是会读到,根据报错的信息,lock,得知在右边事务insert一行之后会进行加锁 (X锁),不允许其他事务修改,但能读取当前数据(当前读)即获取当前行的S锁,左边insert需要在右边事务释放锁之后才能进行insert/update操作(commit或者rollback)
那么尝试对其他id进行操作应该不会有问题吧
结果还是会被锁住,也就是说并不是只对id=99一条数据上了锁,仔细想想写锁可以限制住当前数据条数不被修改避免不可重复读,但依旧限制不了幻读。
mysql会对(-无穷大, 99) 和 (99, +无穷大)加锁,也就是所谓的间隙锁。
锁
- X锁称为排他锁(悲观锁):对目标行添加当前事务的X锁,即可修改或删除操作
- S锁称为共享锁(共享锁):对目标行添加当前事务的S锁,即可读当前行的数据
- S锁是可以兼容的,即多个事务都可以持有对某行的读权限, 而X锁是不兼容的,即最多只有一个事务能获取当前行的修改或删除权限, 若其他事务需要获取X锁,必须等当前拥有X锁权限的事务释放X锁才能获取。
- GAP锁成为间隙锁:当我们用范围条件检索数据而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合范围条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在 的记录,叫做“间隙(GAP)”。
总结
可重复度隔离级别下还是会存在幻读问题,mysql通过使用the-next锁避免出现幻读(行锁+gap锁)。