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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 深度解析 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 提交的记录


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
19天前
|
存储 关系型数据库 MySQL
MySQL MVCC全面解读:掌握并发控制的核心机制
【10月更文挑战第15天】 在数据库管理系统中,MySQL的InnoDB存储引擎采用了一种称为MVCC(Multi-Version Concurrency Control,多版本并发控制)的技术来处理事务的并发访问。MVCC不仅提高了数据库的并发性能,还保证了事务的隔离性。本文将深入探讨MySQL中的MVCC机制,为你在面试中遇到的相关问题提供全面的解答。
55 2
|
6天前
|
监控 关系型数据库 MySQL
MySQL自增ID耗尽应对策略:技术解决方案全解析
在数据库管理中,MySQL的自增ID(AUTO_INCREMENT)属性为表中的每一行提供了一个唯一的标识符。然而,当自增ID达到其最大值时,如何处理这一情况成为了数据库管理员和开发者必须面对的问题。本文将探讨MySQL自增ID耗尽的原因、影响以及有效的应对策略。
21 3
|
7天前
|
存储 关系型数据库 MySQL
MySQL 字段类型深度解析:VARCHAR(50) 与 VARCHAR(500) 的差异
在MySQL数据库中,`VARCHAR`类型是一种非常灵活的字符串存储类型,它允许存储可变长度的字符串。然而,`VARCHAR(50)`和`VARCHAR(500)`之间的差异不仅仅是长度的不同,它们在存储效率、性能和使用场景上也有所不同。本文将深入探讨这两种字段类型的区别及其对数据库设计的影响。
17 2
|
11天前
|
存储 关系型数据库 MySQL
PHP与MySQL动态网站开发深度解析####
本文作为技术性文章,深入探讨了PHP与MySQL结合在动态网站开发中的应用实践,从环境搭建到具体案例实现,旨在为开发者提供一套详尽的实战指南。不同于常规摘要仅概述内容,本文将以“手把手”的教学方式,引导读者逐步构建一个功能完备的动态网站,涵盖前端用户界面设计、后端逻辑处理及数据库高效管理等关键环节,确保读者能够全面掌握PHP与MySQL在动态网站开发中的精髓。 ####
|
8天前
|
SQL 关系型数据库 MySQL
go语言数据库中mysql驱动安装
【11月更文挑战第2天】
22 4
|
6天前
|
SQL 关系型数据库 MySQL
12 PHP配置数据库MySQL
路老师分享了PHP操作MySQL数据库的方法,包括安装并连接MySQL服务器、选择数据库、执行SQL语句(如插入、更新、删除和查询),以及将结果集返回到数组。通过具体示例代码,详细介绍了每一步的操作流程,帮助读者快速入门PHP与MySQL的交互。
19 1
|
1月前
|
存储 关系型数据库 MySQL
Mysql(4)—数据库索引
数据库索引是用于提高数据检索效率的数据结构,类似于书籍中的索引。它允许用户快速找到数据,而无需扫描整个表。MySQL中的索引可以显著提升查询速度,使数据库操作更加高效。索引的发展经历了从无索引、简单索引到B-树、哈希索引、位图索引、全文索引等多个阶段。
61 3
Mysql(4)—数据库索引
|
15天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
77 1
|
17天前
|
关系型数据库 MySQL Linux
在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。
本文介绍了在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。同时,文章还对比了编译源码安装与使用 RPM 包安装的优缺点,帮助读者根据需求选择最合适的方法。通过具体案例,展示了编译源码安装的灵活性和定制性。
59 2
|
20天前
|
存储 关系型数据库 MySQL
MySQL vs. PostgreSQL:选择适合你的开源数据库
在众多开源数据库中,MySQL和PostgreSQL无疑是最受欢迎的两个。它们都有着强大的功能、广泛的社区支持和丰富的生态系统。然而,它们在设计理念、性能特点、功能特性等方面存在着显著的差异。本文将从这三个方面对MySQL和PostgreSQL进行比较,以帮助您选择更适合您需求的开源数据库。
80 4

推荐镜像

更多