深度解析 MySQL 事务、隔离级别和 MVCC 机制:构建高效并发的数据交响乐(二)

简介: 深度解析 MySQL 事务、隔离级别和 MVCC 机制:构建高效并发的数据交响乐(二)

MVCC

MVCC 全称 Multi-Version Concurrency Control,多版本并发控制,主要是为了提高数据库的并发性能

平时,在同一行数据上同时发生读写请求时,会上锁阻塞住,但 MVCC 提供更好的方式去处理读-写请求,可以做到在发生读-写请求冲突时不用加锁

这个读是快照读,不是当前读,当前读是一种加锁的操作,是悲观锁 > FOR UPDATE

前面在介绍 隔离级别 时,说到了 MySQL 在 REPEATABLE READ 隔离级别下,可以很大程度上避免幻读问题的发生,从以下几个概念及实操来说明是如何去避免的

版本链

当对一条数据并发执行多次操作时,对该条数据会形成版本链

对于使用 InnoDB 存储引擎的表来说,它的聚簇索引记录中包含了两个必要的隐藏列,不包括 row_id

row_id 并不是必要的,在创建的表中有主键或非 NULL 的 UNIQUE 键时,row_id 就是主键或唯一键,若没有主键或唯一键,会默认生成 row_id

必要的两个隐藏列,如下:

  1. trx_id:一个事务对某条聚簇索引记录进行改动时,都会把该事务的 id 赋值给 trx_id 隐藏列
  2. roll_pointer:在对某条聚簇索引记录进行改动时,都会把旧的版本写入到 undo 日志中,然后 roll_pointer 这个隐藏列就相当于一个指针,可以通过它来溯源该记录修改前的信息

undo 日志:为了实现事务的原子性操作,InnoDB 存储引擎在实际进行 DML 操作记录时,都先要把对应的 undo 日志记下来。一般对一条记录进行一次改动,就对应一条 undo 日志,但在某些更新操作中,也可能会对应两条 undo 日志;一个事务在执行过程中可能会新增、删除、更新若干条记录,也就是说会记录很多条 undo 日志,这些 undo 日志会从 0 开始编号,依此按顺序生成:1、2、…、N,此编号被称之为 undo no

undo 日志是 MySQL 三大日志其中之一,还包含了 redo log、bin log,日志这方面的内容后续文章再详细分析

为了说明【MySQL 在 REPEATABLE READ 隔离级别下,可以很大程度上避免幻读问题的发生】此问题,创建一张演示表,如下:

create table technology_column(
  id BIGINT(10) not null primary key auto_increment COMMENT '主键',
  category_name varchar(30) not null COMMENT '专栏名称'
) Engine=InnoDB CHARSET=utf8 COMMENT '技术专栏表';

往这张表中插入一条数据,如下:

INSERT INTO technology_column VALUES(1, 'Spring');

假设插入该条数据的事务 id > trx_id = 80,那么此条记录的示意图如下:

假设之后有两个事务:trx_id 分别为 100、120,对这条记录进行了 UPDATE 操作,操作流程如下:

对记录每次进行改动,都会记录一条 undo 日志,每条 undo 日志都有一个 roll_pointer 属性

(INSERT INTO 操作对应的 undo 日志没有该属性,因为该记录没有更早的版本)可以将这些 undo 日志连起来,串成一个链表,如上图右侧所示~

对 INSERT INTO 操作后的记录,每次更新后,都会将旧值放到一条 undo 日志中,就当是该记录的一个旧版本,便于作事务回滚、数据溯源,随着更新的次数增多,所有的版本都会被 roll_pointer 属性连接成一个链表,将这个链表称为版本链,版本链的头节点就是当前记录最新的值;另外,每个版本中还包含了生成该版本对应的事务 id

基于此,利用该记录的版本链来控制并发事务同时访问该记录的行为,那么这种机制就称之为多版本并发控制 MVCC

ReadView

读取视图 > 作用于 SQL 查询语句

对于使用 READ UNCOMMITTED 读未提交隔离级别的事务来说,由于可以读取到未提交的事务修改过的信息,所以直接读取记录的最新版本即可,由此读未提交就会出现脏读、不可重复读、幻读

对于使用 SERIALIZABLE 可串行化读隔离级别的事务来说,InnoDB 采用加锁的方式来访问记录,当事务正在执行时,其他事务就会阻塞住直到前面的事务提交或回滚后才会执行,所以不会出现脏读、不可重复读、幻读

引入版本链的机制主要是为了解决:已提交读、可重复读的事务隔离级别

对于使用 READ COMMITTED、REPEATABLE READ 隔离级别事务来说,都必须保证读取到的数据是已经事务已提交修改过的记录,也就是说:假如事务已经修改了记录但尚未提交,是不能直接读取到最新版本记录的

核心问题:READ COMMITTED、REPEATABLE READ 隔离级别在不可重复读、幻读上的区别是从何而来,基于前面所介绍的版本链,主要关键是需要判断这两种级别在版本链中哪个版本是当前事务可见的,为此 InnoDB 提出了 ReadView 概念

ReadView 主要包含了四个比较重要的内容,如下:

  1. m_ids:表示在生成 ReadView 时当前系统正在活跃的读写事务的事务 id 集合
  2. min_trx_id:表示在生成 ReadView 时当前系统中获取的读写事务中的最小事务 id,也就是 m_ids 中的最小值
  3. max_trx_id:表示在生成 ReadView 时当前系统中应该分配给下一个事务的 id 值

max_trx_id 并不是 m_ids 集合中的最大值,事务 id 是递增分配的;比如:现在有 id > 1、2、3 三个活跃事务,之后 id=3 的事务提交了,那么新的读事务在生成 ReadView 时, m_ids 集合中还有 1、2,min_trx_id 值就为 1,max_trx_id 值就为 4

  1. creator_trx_id:表示生成该 ReadView 读取视图的事务 id

下面来具体介绍,READ COMMITTED、REPEATABLE READ 隔离级别是如何分别处理脏读、不可重复读、幻读问题的.

在 MySQL 中,READ COMMITTED、REPEATABLE READ 隔离级别非常大的一个区别就是它们生成 ReadView 的时机不同

READ COMMITTED

READ COMMITTED 隔离级别的事务在每次查询开始时都会生成一个 ReadView

以上面的 technology_column 表为例,现在只有一条事务 id 为 80 插入的一条记录

比如:现有系统中有两个事务 > Trx_id 100、120 在执行,事务 > Trx_id 100、120 SQL 语句如下:

Trx_id-100、120 Begin;
Trx_id-100、120:select * from technology_column where id = 1;
Trx_id-100:update technology_column set category_name ='MySQL' where id = 1;   
Trx_id-100:update technology_column set category_name ='Redis' where id = 1;   
Trx_id-100:Commit;
Trx_id-120:update technology_column set category_name ='分布式' where id = 1;   
Trx_id-120:update technology_column set category_name ='Linux' where id = 1;   
Trx_id-120:Commit;

脏读问题

在 Trx_id-100:Commit; 语句执行之前,technology_column.id =1 记录得到的版本链表,如下所示:

# 查询语句
select * from technology_column where id = 1;

以上使用 READ COMMITTED 隔离级别的事务,Trx_id 100、120 的事务均未提交,所以此时查询的数据仍然为 Spring!在 Trx_id-100:Commit; 语句执行之前,整个的执行过程如下:

  1. 在执行语句时,会先生成一个 ReadView
  2. ReadView 活跃集合 m_ids 内容为 100,120,min_trx_id 为 100,max_trx_id 为 121,creator_id 为 0
  3. 从版本链中挑选可见的记录,从上图中可以看出,最新版本的 category_name 值内容为 Redis;该版本的 trx_id 为 100,在 m_ids 集合内,所以不符合可见性要求

trx_id 属性值在 ReadView 中 min_trx_id、max_trx_id 之间,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;若不在 min_trx_id、max_trx_id 之间,说明创建 ReadView 时生成该版本的事务已经被提交,该版本才可以被访问

  1. 通过 Roll_ptr 指针跳到下一版本继续访问,下一个版本 category_name 值内容为 MySQL,该版本的 trx_id 为 100,仍然在 m_ids 集合内,也不符合可见性要求
  2. 通过 Roll_ptr 指针跳到下一版本继续访问,下一个版本 category_name 值内容为 Spring,该版本的 trx_id 为 80,小于 ReadView 中 min_trx_id 值,所以这个版本是符合要求的,最后返回给客户端的版本就是这条 category_name 值内容为 Spring 的记录

所以有这种机制存在,就不会发生脏读问题!因为会去判断活跃的事务版本,必须是不在活跃中的事务版本才能使用,也就不可能读到没有 commit 提交的记录


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
存储 SQL 关系型数据库
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
|
数据采集 JSON 数据可视化
JSON数据解析实战:从嵌套结构到结构化表格
在信息爆炸的时代,从杂乱数据中提取精准知识图谱是数据侦探的挑战。本文以Google Scholar为例,解析嵌套JSON数据,提取文献信息并转换为结构化表格,通过Graphviz制作技术关系图谱,揭示文献间的隐秘联系。代码涵盖代理IP、请求头设置、JSON解析及可视化,提供完整实战案例。
970 4
JSON数据解析实战:从嵌套结构到结构化表格
|
JSON 监控 网络协议
Bilibili直播信息流:连接方法与数据解析
本文详细介绍了自行实现B站直播WebSocket连接的完整流程。解析了基于WebSocket的应用层协议结构,涵盖认证包构建、心跳机制维护及数据包解析步骤,为开发者定制直播数据监控提供了完整技术方案。
2802 9
|
机器学习/深度学习 JSON 算法
淘宝拍立淘按图搜索API接口系列的应用与数据解析
淘宝拍立淘按图搜索API接口是阿里巴巴旗下淘宝平台提供的一项基于图像识别技术的创新服务。以下是对该接口系列的应用与数据解析的详细分析
|
缓存 监控 搜索推荐
【实战解析】smallredbook.item_get_video API:小红书视频数据获取与电商应用指南
本文介绍小红书官方API——`smallredbook.item_get_video`的功能与使用方法。该接口可获取笔记视频详情,包括无水印直链、封面图、时长、文本描述、标签及互动数据等,并支持电商场景分析。调用需提供`key`、`secret`和`num_iid`参数,返回字段涵盖视频链接、标题、标签及用户信息等。同时,文章提供了电商实战技巧,如竞品监控与个性化推荐,并列出合规注意事项及替代方案对比。最后解答了常见问题,如笔记ID获取与视频链接时效性等。
|
存储 缓存 监控
如何高效爬取天猫商品数据?官方API与非官方接口全解析
本文介绍两种天猫商品数据爬取方案:官方API和非官方接口。官方API合法合规,适合企业长期使用,需申请企业资质;非官方接口适合快速验证需求,但需应对反爬机制。详细内容涵盖开发步骤、Python实现示例、反爬策略、数据解析与存储、注意事项及扩展应用场景。推荐工具链包括Playwright、aiohttp、lxml等。如需进一步帮助,请联系作者。
|
JSON API 数据格式
淘宝商品评论API接口系列的应用与数据解析
在电商平台中,用户评论是了解商品质量、服务水平和用户满意度的重要数据来源。淘宝作为中国最大的电商平台,提供了商品评论API接口,帮助开发者获取和分析用户评价数据。本文将介绍淘宝商品评论API接口系列的作用、使用方法,并通过示例展示如何调用API并解析返回的JSON数据。
|
缓存 关系型数据库 BI
使用MYSQL Report分析数据库性能(下)
使用MYSQL Report分析数据库性能
682 158
|
关系型数据库 MySQL 数据库
自建数据库如何迁移至RDS MySQL实例
数据库迁移是一项复杂且耗时的工程,需考虑数据安全、完整性及业务中断影响。使用阿里云数据传输服务DTS,可快速、平滑完成迁移任务,将应用停机时间降至分钟级。您还可通过全量备份自建数据库并恢复至RDS MySQL实例,实现间接迁移上云。
|
关系型数据库 MySQL 数据库
阿里云数据库RDS费用价格:MySQL、SQL Server、PostgreSQL和MariaDB引擎收费标准
阿里云RDS数据库支持MySQL、SQL Server、PostgreSQL、MariaDB,多种引擎优惠上线!MySQL倚天版88元/年,SQL Server 2核4G仅299元/年,PostgreSQL 227元/年起。高可用、可弹性伸缩,安全稳定。详情见官网活动页。
1805 152

推荐镜像

更多