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;
相关推荐
Ren_xixi3 小时前
redis和mysql的区别
数据库·redis·mysql
追逐时光者4 小时前
免费、简单、直观的数据库设计工具和 SQL 生成器
后端·mysql
drebander5 小时前
MySQL 查询优化案例分享
数据库·mysql
小林coding7 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
18号房客8 小时前
高级sql技巧进阶教程
大数据·数据库·数据仓库·sql·mysql·时序数据库·数据库架构
翔云1234569 小时前
MySQL purged gtid是如何生成和维护的
数据库·mysql
平行线也会相交11 小时前
云图库平台(三)——后端用户模块开发
数据库·spring boot·mysql·云图库平台
恒辉信达12 小时前
hhdb客户端介绍(53)
数据库·mysql·hhdb·数据库可视化界面客户端
Hello.Reader13 小时前
Redis热点数据管理全解析:从MySQL同步到高效缓存的完整解决方案
redis·mysql·缓存
是程序喵呀13 小时前
MySQL备份
android·mysql·adb