MySQL锁机制
上文总结了MySQL的隔离级别,而隔离级别是通过锁机制实现的,所以针对锁机制展开详细描述。
首先介绍MySQL的锁可以分为:
- 享/读/共享锁Shared Locks:针对同一份数据,读操作可以同时执行,但不能写。
- 排它/写锁Exclusive Locks:若当前写操作没有执行完,那么会阻断其它的读写操作。
- 间隙锁Gap/Next-Key locks:锁住两个索引之间的区间,前者为开区间后者为闭区间。
- 行锁Record Locks:锁的粒度小,加锁慢,锁定当前行,发生锁冲突的概率小,并发度高。(行锁也是InnoDB和MyISAM的区别之一,前者支持行锁与事务)
- 表锁Table Locks:锁的粒度大,加锁快,开销小,锁定当前表,发生锁冲突的概率大,并发度低。
MySQL中,对于数据库的增删改的操作,会使用写锁;查询默认并不会加锁,可以通过lock in share mode 或 for update来加共享锁或排它锁。
举例:
假如两个事务A和B都要执行update操作,事务A先执行update时,先获取行锁锁定数据,此时若事务B也执行update,也要获取行锁锁定数据,但此时就会显示已经被事务A占有,只能wait;若事务A长时间没有释放锁那么事务B就会报错,超时异常。
既然涉及锁,那就必然会带来性能方面的问题,比如加锁解锁等性能开销。而读未提交隔离级别是不加锁的,因此其性能最好,但也没有任何隔离效果;而对于串行化来说,读的时候加了共享锁,不能写。写的时候加了写锁,阻塞其它事务的读取与写入,所以性能最差;读提交和可重复读介于它俩之间,既要考虑高并发实现,又能兼顾解决数据问题,基于MVCC实现。
问题又来了,什么是MVCC?
MVCC是多版本并发控制,用来实现读提交和可重复读。实现方式用到了一致性视图,又称为快照。
对于可重复读来说,只需要在事务开始的时候创建快照,之后的查询都共用这一个快照,后续事务的更改对当前快照来说是不可见的。
对于读提交来说,每条语句执行之前都会重新计算出一个新的快照。
问题又来了,什么是快照?
先介绍一下快照在MVCC底层的工作流程:在InnoDB中每个事务都有自己的唯一ID,并且是递增的。在事务每次更新数据的时候,就会生成一个数据版本并将当前事务的ID赋给row trx_id。
其次介绍一下快照需要遵循的规则:一共有四种情况,对于事务视图来说,对自己的更新总是可见的;对于版本未提交的都是不可见的;对于版本已经提交,但在视图创建之后的提交仍是不可见的;版本已经提交,并且在创建视图之前的提交都是可见的。