事务(五)

MySQL之MVVC原理

什么是MVVC

MVVC (Multi-Version Concurrency Control) (注:与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control)是一种基于多版本的并发控制协议,只有在InnoDB引擎下存在。MVCC是为了实现事务的隔离性,通过版本号,避免同一数据在不同事务间的竞争,你可以把它当成基于多版本号的一种乐观锁。当然,这种乐观锁只在事务级别提交读和可重复读有效。MVCC最大的好处,相信也是耳熟能详:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能。
不仅是MySQL,包括Oracle,PostgreSQL等其他数据库系统也都实现了MVCC,但各自的实现机制不尽相同,因为MVCC没有一个统一的实现标准。

可以认为MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。

MVCC的实现方式有多种,典型的有乐观(optimistic)并发控制 和 悲观(pessimistic)并发控制。

MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作。其他两个隔离级别和MVCC不兼容,因为 READ UNCOMMITTED 总是读取最新的数据行,而不是符合当前事务版本的数据行。而 SERIALIZABLE 则会对所有读取的行都加锁。

MVVC的实现机制

InnoDB在每行数据都增加三个隐藏字段,一个唯一行号,一个记录创建的版本号,一个记录回滚的版本号。

在多版本并发控制中,为了保证数据操作在多线程过程中,保证事务隔离的机制,降低锁竞争的压力,保证较高的并发量。在每开启一个事务时,会生成一个事务的版本号,被操作的数据会生成一条新的数据行(临时),但是在提交前对其他事务是不可见的,也就是说:事务提交后才会将对应数据行中的隐藏字段事务id(DB_TRX_ID)进行修改,对于数据的更新(包括增删改)操作成功,会将这个版本号(即事务id)更新到数据的行中,事务提交成功,将新的版本号更新到此数据行中,这样保证了每个事务操作的数据,都是互不影响的,也不存在锁的问题。

实验

实验1:

实验2:

上面两个实验很好的说明了 start transaction 和 start tansaction with consistent snapshot的区别。第一个实验说明,start transaction执行之后,事务并没有开始,所以insert发生在session A的事务开始之前,所以可以读到session B插入的值。第二个实验说明,start transaction with consistent snapshot已经开始了事务,所以insert语句发生在事务开始之后,所以读不到insert的数据。

所以事务开始时间点,分为两种情况:

START TRANSACTION 时,是第一条语句的执行时间点,就是事务开始的时间点,第一条select语句建立一致性读的snapshot;
START TRANSACTION  WITH consistent snapshot 时,则是立即建立本事务的一致性读snapshot,当然也开始事务了;
 一致性读肯定是读取在某个时间点已经提交了的数据,有个特例:本事务中修改的数据,即使未提交的数据也可以在本事务的后面部分读取到。

mysql做实验常用sql:

begin、start transaction 和 start tansaction with consistent snapshot:开启事务

commit:提交事务

SELECT * FROM information_schema.INNODB_TRX:查询目前活跃的所有事务,即已开启未提交的事务

SET AUTOCOMMIT=0 禁止自动提交

SET AUTOCOMMIT=1 开启自动提交

mysql默认是开启自动提交的,即如果没有手动开启事务,直接写sql语句执行,是会自动提交事务的。

MySQL的InnoDB存储引擎默认事务隔离级别是RR(可重复读), 是通过 “行排他锁+MVCC” 一起实现的。

这里扩展一下mysql数据库锁的知识:

转载:https://www.cnblogs.com/mr-wuxiansheng/p/7044733.html

mysql锁机制分为表级锁和行级锁,行级锁中的有共享锁与排他锁。

共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。

排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

对于共享锁大家可能很好理解,就是多个事务只能读数据不能改数据,对于排他锁大家的理解可能就有些差别,我当初就犯了一个错误,以为排他锁锁住一行数据后,其他事务就不能读取和修改该行数据,其实不是这样的。排他锁指的是一个事务在一行数据加上排他锁后,其他事务不能再在其上加其他的锁。mysql InnoDB引擎默认的修改数据语句,update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,如果加排他锁可以使用select ...for update语句,加共享锁可以使用select ... lock in share mode语句。所以加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据(即执行修改数据的sql或者for update、lock in share mode等当前读时,必须等待拥有排他锁的事务提交/回滚后,释放排他锁后,才能执行;可以通过上面手动开启事务来做实验验证。),但可以直接通过select ...from...查询数据(但是查询的不是当前读数据,可以看上一篇博客理解什么是当前读),因为普通查询没有任何锁机制。

快照读: 普通的select,不加锁,读取记录的可见版本。
当前读:select...lock in share mode/for update、update、insert、delete,读取记录最新版本,并加锁。

在RR级别下,快照读是通过MVVC(多版本控制)和undo log来实现的,当前读是通过加record lock(记录锁)和gap lock(间隙锁)来实现的。

提交事务或回滚事务才会释放锁,并不是影响该条数据行的sql语句结束就会释放锁。这里不列出实验了,可以参考转载:https://www.cnblogs.com/mr-wuxiansheng/p/7044733.html的实验。

转载:https://blog.csdn.net/huaishu/article/details/89924250

带着疑问去思考,然后串联,进而归纳总结,不断追问自己,进行自我辩证,像侦查嫌疑案件一样看待技术问题,漆黑的街道,你我一起寻找线索,你就是技术界大侦探福尔摩斯
原文地址:https://www.cnblogs.com/cainiao-Shun666/p/14531268.html