Mysql中事务的读一致性问题,以及如何用MVCC解决

事务四大特性的实现:

原子性事务具有回滚的能力,InnoDB引擎使用undo log日志表来进行回滚操作。

持久性InnoDB引擎使用redo log日志表来保证数据的持久性。

事务的隔离性产生的问题:

**脏读:**一个事务读取到了另一个事务未提交的数据。

**不可重复读:**一个事务读取到了另一个事务已提交的数据,在同一个事务内,不管进行多少次查询,查询到的结果都是相同的。

**虚读(幻读):**一个事务读取了另一个事务提交的新增数据。

事务并发的三大问题其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。

mysql数据库的隔离级别:

Read Uncommitted (读未提交),未解决任何并发问题,事务未提交的数据对其他事务也是可见的,会出现脏读。

Read Committed (已提交读),解决脏读问题,一个事务开始之后,只能看到已提交的事务所做的修改,会出现不可重复读。

Repeatable Read (可重复读),解决不可重复读问题,在同一个事务中多次读取同样的数据结果是一样的,这种隔离级别未定义解决幻读的问题。

Serializable (串行化),解决所有问题,最高的隔离级别,通过强制事务的串行执行。

InnoDB对事务隔离级别的支持程度:

|-----------------------------|-----|-------|------------|
| 事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
| Read Uncommitted (读未提交) | 可能 | 可能 | 可能 |
| Read Committed (已提交读) | 不可能 | 可能 | 可能 |
| Repeatable Read (可重复读) | 不可能 | 不可能 | 对InnoDB不可能 |
| Serializable (串行化) | 不可能 | 不可能 | 不可能 |

**思考:**如果要解决读一致性的问题,保证一个事务中前后两次读取数据结果一致,实现事务隔离,应该怎么做?

**第一种解决方式:**在读取数据前,对其加锁,阻止其他事务对数据进行修改

(LBCC) Lock BasedConcurrency Control

**第二种解决方式:**生成一个数据请求时间点的一致性数据快照(Snapshot),并用这个快照来提供一定级别(语句级或事务级)的一致性读取

(MVCC) Multi VersionConcurrency Control。MVCC只在RC RR中使用。

MVCC解决读一致性问题原理:

测试使用的环境:mysql数据,采用引擎InnoDB,采用隔离级别Repeatable Read (可重复读)。

MVCC的概念:

(MVCC) Multi VersionConcurrency Control。MVCC只在ReadCommitted (已提交读)和Repeatable Read (可重复读)中使用。

MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。

一句话讲,MVCC就是用 同一份数据临时保留多版本的方式 的方式,实现并发控制。

Mysql会在表中添加2个隐藏的字段,DB_TRX_ID(插入或者更新行的最后一个事务的ID) 和 DB_ROLL_PTR (回滚指针)。每开始新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询每行记录的版本号进行比较。回滚指针用来查找历史版本的数据。

准备环境 | MVCC效果演示:

创建表结构,添加数据

sql 复制代码
CREATE TABLE `t_mvcc` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

添加数据后如下:

创建测试表,用来生成事务ID的作用:

sql 复制代码
CREATE TABLE `t_mvcc_demo`(
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

数据如下:

MVCC 识别规则

当执行查询sq|时会生成一致性视图read-view,它由执行查询时所有未提交事务id数组(数组里最小的id为min id)和已创建的最大事务id (max id)组成,查询的数据结果需要跟read-view做比对从而得到快照结果。

版本链比对规则:

1.如果落在绿色部分( trx jid<min. id),表示这个版本是已提交的事务生成的,这个数据是可见的;

2.如果落在红色部分( trx id>max. id),表示这个版本是由将来启动的事务生成的,是肯定不可见的;

3.如果落在黄色部分(min. id <=tnx id<=max_ id),那就包括两种情况

a.若row的trx_ id在数组中,表示这个版本是由还没提交的事务生成的,可见,当前自己的事务是可见的;

b.若row的trx. id不在数组中,表示这个版本是已经提交了的事务生成的,可见。

图解

相关推荐
Fᴏʀ ʏ꯭ᴏ꯭ᴜ꯭.1 分钟前
MySQL半同步复制与GTID实战详解
android·mysql·adb
疯狂成瘾者8 分钟前
后端系统、服务稳定性里核心的指标有哪些
数据库
SPC的存折37 分钟前
openEuler 24.03 MariaDB Galera 集群部署指南(cz)
linux·运维·服务器·数据库·mysql
仲芒38 分钟前
[24年单独笔记] MySQL 常用的 DML 命令
数据库·笔记·mysql
SPC的存折1 小时前
MySQL 8.0 分库分表
linux·运维·服务器·数据库·mysql
蓦然乍醒1 小时前
使用 DBeaver 还原 PostgreSQL 备份文件 (.bak) 技术文档
数据库·postgresql
XDHCOM1 小时前
Redis节点故障自动恢复机制详解,如何快速抢救故障节点,确保数据不丢失?
java·数据库·redis
QCzblack1 小时前
BugKu BUUCTF ——Reverse
java·前端·数据库
cyber_两只龙宝1 小时前
【Oracle】Oracle之DQL中WHERE限制条件查询
linux·运维·数据库·云原生·oracle
luis的妙妙屋1 小时前
主流数据库数据类型对比分析
数据库