MYSQL的多版本并发控制MVCC(Multi-Version Concurrency Control)

1、概述

MVCC 是一种用于数据库管理系统的并发控制技术,允许多个事务同时访问数据库,而不会导致读写冲突。也就是说在读写的时候,线程不用去争抢读写锁。因为加锁的过程比较耗性能。

当然很多时候还是必须的,不能避免,比如说,去ATM机取钱的时候,同时又在手机APP上进行提现,这种操作就需要加锁,不能让其同时提现,一次只能一个操作,而且只有在ATM取钱这个事务被提交之后才能做其他操作。

处理并发的场景无外乎三种:

读、读 :这个不需要做控制,因为数据没有变化
读、写 :存在线程安全问题,可能出现脏读、幻读,不可重复读
写、写:存在线程安全问题,可能出现更新丢失的情况

这里介绍的MVCC 是在存储引擎为InnoDB实现的,目的也是为了提高数据库的并发性能,不使用加锁的方式去处理读、写并发。

2、MVCC特点

这里的读操作,有两种方式:
**快照读:**SELECT语句,在读写的时候不用加锁,所以效率很高,但也存在读取的时候有更新操作,可能会读到历史数据。

**当前读:**读取的是最新数据,是一种悲观锁的操作。它会对当前读取的数据进行加锁,避免其他事物对其进行写操作。主要包括以下几种操作:

select lock in share mode(共享锁)

select for update(排他锁)

update(排他锁)

insert(排他锁)

delete(排他锁)

3、准备数据

在介绍之前,没有安装MYSQL的,可以先进行安装,下载地址:https://dev.mysql.com/downloads/

安装好了之后,我们就新建库与表,插入一些数据来做个测试

sql 复制代码
CREATE DATABASE mydb;
USE mydb;

CREATE TABLE `tb1` (
  `id` int(12) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8;

INSERT INTO tb1(name) VALUES ('XIAO1'),('XIAO2'),('XIAO3');

CREATE TABLE `tb2` (
  `id` int(12) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=myisam;

INSERT INTO tb2(name) VALUES ('LAO1'),('LAO2'),('LAO3');

这里我特地创建了两张表,分别是表tb1 对应的是InnoDB引擎,表tb2 对应的是myisam引擎,创建之后,也可以看到两者的数据格式也是不一样的,我们先查询下,新建的数据库mydb以及保存的数据在什么地方:

bash 复制代码
mysql> show variables like '%datadir%';
+---------------+---------------------------------------------+
| Variable_name | Value                                       |
+---------------+---------------------------------------------+
| datadir       | C:\ProgramData\MySQL\MySQL Server 8.0\Data\ |
+---------------+---------------------------------------------+
1 row in set, 1 warning (0.00 sec)

然后可以看到,InnoDB 引擎对应的是:tb1.ibdmyisam 对应的是:tb2.MYD (数据)、tb2.MYI (索引)、tb2_402.sdi (表结构)[这个在以前的版本没有出现]

MySQL5.5 之后都是默认为InnoDB引擎

4、MVCC原理

MYSQL 存储的数据中,除了我们显式定义的字段,还隐含着两个字段。
trx_id :事务id,每进行一次事务操作,就会自增1。
roll_pointer :回滚指针,用于找到上一个版本的数据,结合undolog进行回滚。

我们用SELECT 读数据时,这一时刻的数据会有很多个版本【比如上图四个版本】,但我们并不知道读取哪个版本,依赖ReadView 来对我们进行版本的选择,通过ReadView 我们就能够知道读取哪个版本。我们来看下这个ReadView的定义:

cpp 复制代码
class ReadView {
/* ... */
private:
trx_id_t m_low_limit_id; /* 大于等于这个 ID 的事务均不可见 */

trx_id_t m_up_limit_id; /* 小于这个 ID 的事务均可见 */

trx_id_t m_creator_trx_id; /* 创建该 Read View 的事务ID */

trx_id_t m_low_limit_no; /* 事务 Number, 小于该 Number 的 Undo Logs 均可以被 Purge */

ids_t m_ids; /* 创建 Read View 时的活跃事务列表 */

m_closed; /* 标记 Read View 是否 close */
}

字段的解释:

m_low_limit_id:目前出现的最大事务ID+1(下一个将被分配的事务ID)。大于等于这个ID的数据版本均不可见,也就访问不到。

m_up_limit_id:活跃事务列表m_ids中最小的事务ID,如果为空,则m_up_limit_id为m_low_limit_id。小于这个ID的数据版本均可见。

m_ids :ReadView创建时其他未提交的活跃事务ID列表。创建ReadView时,将当前未提交事务ID记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的。m_ids 不包括当前事务自己和已提交的事务(正在内存中)

m_creator_trx_id:创建该ReadView的事务ID

5、实践操作

开四个终端,新建三个事务,两个写操作,一个读操作,还有一个就是单纯的查询。大家可以根据不同的进入时间来了解这个过程,会产生未提交和已提交事务状态,对应的查询信息都是不一样的,具体代码分别如下:

sql 复制代码
BEGIN;
UPDATE tb1 SET  name='XXX' WHERE id=1;
UPDATE tb1 SET  name='YYY' WHERE id=1;
COMMIT;

BEGIN;
UPDATE tb1 SET  name='ZZZ' WHERE id=1;
UPDATE tb1 SET  name='QQQ' WHERE id=1;
DELETE FROM tb1 WHERE id=5;
COMMIT;

BEGIN;
SELECT * FROM tb1 WHERE id=1;
COMMIT;

SELECT * FROM tb1;

6、小结

对于这种读写并发,以及对性能的要求,大家需要看实际业务情况来做决定,其中这里主要是介绍InnoDB 引擎,这个要高效很多,在以前的旧版本可能大家使用MyISAM 这个更多,因为性能很好,不过不支持事务操作,所以很多场景也就不适应,MYSQL5.5 版本之后就是默认InnoDB 引擎了。

最后大家也可以尝试在表tb2 中去尝试下,看下是什么结果,因为这张表使用的是MyISAM 引擎,就起不到作用了。

另外需要注意的是,事务的提交是默认自动的,有些时候需要关闭,将默认的1修改为0:

sql 复制代码
SET AUTOCOMMIT=0;
SELECT @@AUTOCOMMIT;

比如说对于脏读的情况,我们需要当前读,也就是需要排它锁:

sql 复制代码
SET AUTOCOMMIT=0;
BEIGIN;
DELETE FROM tb1 WHERE id=2;

这种情况如果我们的事务在这个时候进去,对这个id=2进行读写操作,就会出现脏读的情况,这个时候就需要使用SELECT FOR UPDATE,等待事务处理完毕之后再做相应的操作。

对于脏读这种情况,很常见,比如说事务在做删除操作,这个时候记录已被删除但是还没有提交事务,如果进行查询操作就会出现脏读,如下:

sql 复制代码
SET AUTOCOMMIT=0;
BEGIN;
SELECT * FROM tb1 WHERE id=2 FOR UPDATE;
UPDATE tb1 SET  name='QQQ' WHERE id=1;
COMMIT;
相关推荐
难以触及的高度19 分钟前
mysql中between and怎么用
数据库·mysql
小技与小术2 小时前
数据库表设计范式
数据库·mysql
安迁岚2 小时前
【SQL Server】华中农业大学空间数据库实验报告 实验三 数据操作
运维·服务器·数据库·sql·mysql
安迁岚2 小时前
【SQL Server】华中农业大学空间数据库实验报告 实验九 触发器
数据库·sql·mysql·oracle·实验报告
无敌岩雀3 小时前
MySQL中的索引
数据库·mysql
东阳马生架构3 小时前
MySQL原理简介—1.SQL的执行流程
mysql
安迁岚4 小时前
【SQL Server】华中农业大学空间数据库实验报告 实验六 视图
数据库·sql·mysql·oracle·实验报告
xoxo-Rachel4 小时前
(超级详细!!!)解决“com.mysql.jdbc.Driver is deprecated”警告:详解与优化
java·数据库·mysql
JH30735 小时前
Oracle与MySQL中CONCAT()函数的使用差异
数据库·mysql·oracle
路有瑶台7 小时前
MySQL数据库学习(持续更新ing)
数据库·学习·mysql