Innodb的锁与MVCC

事务的四个特性,ACID。来聊一聊一致性。

并发与一致性的关系

并发的任务对同一个临界资源进行操作,如果不采取措施,可能导致不一致,故必须进行并发控制

常用手段

通过并发控制保证数据一致性的常见手段有:

  • 锁(Locking)
  • 数据多版本(Multi Versioning)

锁的演化思路

  1. 串行的排它锁,并发性能不高
  2. 锁的读写分离,X锁和S锁,即排它锁和共享锁,提高并发性能。但这里读还是会受到写锁的影响,并没有真正的将读和写分离,而是实现的读和读的并发。

共享锁与排他锁的玩法

  1. 共享锁之间不互斥,所以可认为读读可以并行
  2. 排他锁与任何锁互斥,所以可认为写读,写写不可以并行

可以看到,一旦写数据的任务没有完成,数据是不能被其他任务读取的,这对并发度有较大的影响。对应到数据库,可以理解为,写事务没有提交,读相关数据的select也会被阻塞。

此时,提高性能的关键就是,如何让读和写可以并行,以此来提高并发性能。

多版本并发控制

可以想到的一个操作是,当一个事务进行写操作的时候,把要操作的数据存一份,作为一个版本,其他事务进行读操作的时候,读取的数据是原有数据的版本,这样读操作并没有没已存在的写操作阻塞住,从而提高了并发度。

这样带来的一个结果就是,一个读事务,读到的要么是其他写事务提交之后的数据,要么是其他写事务未提交时修改前的数据。这样在提高并发性能的同时也避免了脏读。

那么多版本并发控制是怎么做呢?

  • 数据库事务未提交时,会将事务修改数据的镜像(即修改前的旧版本)存放到undo日志里,当事务回滚时,或者数据库奔溃时,可以利用undo日志,即旧版本数据,撤销未提交事务对数据库产生的影响。

  • undo日志存在回滚段里,多版本并发控制就是通过读取回滚段的旧版本数据来实现的。

  • 回滚段里的数据,其实是历史数据的快照(snapshot),这些数据是不会被修改,select可以肆无忌惮的并发读取他们。

  • 快照读(Snapshot Read),这种一致性不加锁的读(Consistent Nonlocking Read),就是InnoDB并发如此之高的核心原因之一。

这里的一致性是指,事务读取到的数据,要么是事务开始前就已经存在的数据(当然,是其他已提交事务产生的),要么是事务自身插入或者修改的数据。

总结

  • 常见并发控制保证数据一致性的方法有锁,数据多版本;
  • 普通锁串行,读写锁读读并行,数据多版本读写并行;
  • redo日志保证已提交事务的ACID特性,设计思路是,通过顺序写替代随机写,提高并发;
  • undo日志用来回滚未提交的事务,它存储在回滚段里;
  • InnoDB是基于MVCC的存储引擎,它利用了存储在回滚段里的undo日志,即数据的旧版本,提高并发;
  • InnoDB之所以并发高,快照读不加锁;
  • InnoDB所有普通select都是快照读;

参考文章:

https://mp.weixin.qq.com/s/R3yuitWpHHGWxsUcE0qIRQ

坚持原创技术分享,您的支持将鼓励我继续创作!
0%