概述
谈到MySQL事务,必然离不开InnoDB和MVCC机制。MVCC: Multiversion Concurrency Control,则翻译是多版本并发控制,其目标就是为了提高数据库在高并发场景下的性能。
- 优点
读不加锁,读写不冲突。在读多写少的场景下极大的增加了系统的并发性能
MySQL基本架构
在了解MVCC之前我们可以认识一下MySQL的基本架构
MySQL事务
MySQL的事务是在存储引擎层实现的,在MySQL中,最常用的就是InnoDB
和MyISAM
,我们都知道,MYISAM并不支持事务,所以InnoDB实现了MVCC的事务并发处理机制,也是我们这篇文章的主要研究内容。
可能我们都看到过,MVCC只在RC和RR下,为了分析这个问题,我们先回顾一下SQL标准事务隔离级别隔离性。
-
read uncommitted
读未提交: 一个事务还没提交时,它做的变更就能被别的事务看到。 -
read committed
读提交:一个事务提交之后,它做的变更才会被其他事务看到。 -
repeatable read
可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。在可重复读隔离级别下,未提交变更对其他事务也是不可见的。 -
serializable
串行化 :对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。
我们通过两个事务提交流程来说明事务隔离级别的具体效果:
我们假设有一个表,仅有一个字段field:
DROP TABLE IF EXISTS `mvcc_test`;
CREATE TABLE `mvcc_test`( `field` INT)ENGINE=InnoDB;
INSERT INTO `mvcc_test` VALUES(1); -- 插入一条数据
根据事务隔离级别的定义,我们可以来推测,事务A提交前后,事务B的两次读取3和4分别读取的值:
若事务B的隔离级别为 read uncommitted,事务B的两次读取都读取到了20,即修改后的值
若事务B的隔离级别是read committed,那么,事务B的操作3读取到的值为1,而4读取到的值为20,因为4时事务A已经完成了提交
若事务B的隔离级别是repeatable read或serializable,那么操作3和4读取的值都是1。
MVCC的必要性
MySQL中MYISAM并不支持事务,同样的, MVCC也就和他没有半毛钱关系了,InnoDB相比与MYISAM的提升就是对于行级锁的支持和对事务的支持,而应对高并发事务, MVCC 比单纯的加行锁更有效, 开销更小。
但是单纯的并发也会带来十分严重的问题:
-
Lost Update
更新丢失: 多个事务对同一行数据进行读取初值更新时,由于每个事务对其他事务都未感知,会造成最后的更新覆盖了其他事务所做的更新。 -
dirty read
脏读: 事务一个正在对一条记录进行修改,在完成并提交前事务二也来读取该条记录,事务二读取了事务一修改但未提交的数据,如果事务一回滚,那么事务二读取到的数据就成了“脏”数据。 -
non-repeatable read
不可重复读: 多个事务在读取某些数据后的某个时间再次读取之前读取过的数据,发现读出的数据已经发生了改变或者删除,这种现象称为“不可重复读” -
phantom read
幻读: 多个事务按相同的查询条件重新读取以前检索过的数据,发现其他事务插入了满足查询条件的新数据,这种现象称为“幻读”
不可重复读与幻读的现象是比较接近的,也有人直接就说幻读就是不可重复读,我比较倾向与他两就是他两个: 不可重复读针对的是值的不同,幻读指的是数据条数的不同。同样的对于幻读,单纯的MVCC机制并不能解决幻读问题,InnoDB也是通过加间隙锁来防止幻读。
从本质上来说,事务隔离级别就是系统并发能力和数据安全性间的妥协,我们在刚开始学习数据库时就在说: 隔离性越高,数据库的性能就越差,就是这个结果,只是我们当时只知其然罢了。
解决并发带来的问题,最通常的就是加锁,但锁对于性能也是腰斩性的,所以MVCC就显得十分重要了。
抄大佬的一句话: 在不同的隔离级别下,数据库通过 MVCC 和隔离级别,让事务之间并行操作遵循了某种规则,来保证单个事务内前后数据的一致性。
InnoDB 下的 MVCC 实现原理
在InnoDB中MVCC的实现通过两个重要的字段进行连接:DB_TRX_ID
和DB_ROLL_PT
,在多个事务并行操作某行数据的情况下,不同事务对该行数据的UPDATE
会产生多个版本,数据库通过DB_TRX_ID
来标记版本,然后用DB_ROLL_PT
回滚指针将这些版本以先后顺序连接成一条 Undo Log
链。
对于一个没有指定PRIMARY KEY的表,每一条记录的组织大致如下:
MVCC模拟流程
面试快答
-
请你介绍一下mysql的MVCC机制
MVCC是一种多版本并发控制机制,是MySQL的InnoDB存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别。MVCC是通过保存数据在某个时间点的快照来实现该机制,其在每行记录后面保存两个隐藏的列,分别保存这个行的创建版本号和删除版本号,然后Innodb的MVCC使用到的快照存储在Undo日志中,该日志通过回滚指针把一个数据行所有快照连接起来。
-
请问SQL优化方法有哪些?
通过建立索引对查询进行优化
对查询进行优化,应尽量避免全表扫描 -
请你说一下mysql引擎以及其区别
在Mysql数据库中,常用的引擎为Innodb和MyIASM,其中Innodb是一个事务型的存储引擎,有行级锁定和外键约束,提供了对数据库ACID事物的支持,实现了SQL标准的四种隔离级别,即读未提交,不可重复读,可重复读以及串行,其涉及目标就是处理大数据容量的数据库系统。而MyIASM引擎是Mysql默认的引擎,不提供数据库事务的支持,也不支持行级锁和外键,因此当写操作时需要锁定整个表,效率较低。不过其保存了表的行数,当金星select count(*)form table时,可直接读取已经保存的值,不需要进行全表扫描。因此当表的读操作远多于写操作,并且不需要事务支持时,可以优先选择MyIASM
-
请你来说一说Redis的定时机制怎么实现的?
Redis服务器是一个事件驱动程序,服务器需要处理以下两类事件:文件事件(服务器对套接字操作的抽象)和时间事件(服务器对定时操作的抽象)。Redis的定时机制就是借助时间事件实现的。
一个时间事件主要由以下三个属性组成:id:时间事件标识号;when:记录时间事件的到达时间;timeProc:时间事件处理器,当时间事件到达时,服务器就会调用相应的处理器来处理时间。一个时间事件根据时间事件处理器的返回值来判断是定时事件还是周期性事件
一个时间事件主要由以下三个属性组成:id:时间事件标识号;when:记录时间事件的到达时间;timeProc:时间事件处理器,当时间事件到达时,服务器就会调用相应的处理器来处理时间。一个时间事件根据时间事件处理器的返回值来判断是定时事件还是周期性事件。
-
请你来说一说Redis是单线程的,但是为什么这么高效呢?
虽然Redis文件事件处理器以单线程方式运行,但是通过使用I/O多路复用程序来监听多个套接字,文件事件处理器既实现了高性能的网络通信模型,又可以很好地与Redis服务器中其他同样以单线程运行的模块进行对接,这保持了Redis内部单线程设计的简单性。