前言

上一篇文章MySQL-InnoDB行锁中,提到过一致性锁定读和一致性非锁定读,这篇文章会详细分析一下在事务中时,具体是如何实现一致性的。

一致性读原理

start transaction和begin语句,并不是立即开启一个事务,事务是在第一条读语句执行时才建立的。如果需要立即开启事务,可以使用这个语句:start transaction with comsistent snapshot。

每一个事务都有一个唯一的事务id,在mysql系统中,这个事务id是唯一且递增的。每一条数据库记录也有一个版本号,这个版本号记录了修改记录的事务id,如图:

最新的版本是V4,修改它的事务id为25,依次往前为V3,事务id17,一直到V1,事务id为10。

数据库中并不是真的有这些V1~V4的物理实体,是根据当前最新版本号和undolog往前计算出来每一个版本的。另外,数据库记录中除了保存修改它的事务id以外,还会记录这条修改是否已经提交。

在事务建立的一瞬间,当前事务会生成一个数组,保存了当前时刻系统中所有的活跃事务id(未提交事务),按照从小到大顺序排列,其中最小的id为低水位,最大的id为高水位。

那么在读操作和更新操作的时候,具体是如何使用这个版本号的呢?

我们知道,读分为一致性锁定读和一致性非锁定读;更新操作,其实可以拆解为两步,一步是一致性锁定读,一步是更新。我们只需要分析 一致性锁定读和一致性非锁定读就可以了。

  • 如果是一致性非锁定读,能读到的是低水位下的最近一个事务更新后的记录。
  • 如果是一致性锁定读,如果当前记录被锁定,需要等待锁释放;如果没被锁定,能读到最新一个已提交记录或者当前事务版本号对记录的修改。
实验验证

准备一张表

create table t(id int,k int,primary key(id));
insert into t(id,k) values(1,1),(2,2),(3,3),(4,4);

事务的时间线图如下

事务A:

mysql> start transaction with consistent snapshot;
Query OK, 0 rows affected (0.00 sec) mysql> select * from t where id = 1;
+----+------+
| id | k |
+----+------+
| 1 | 1 |
+----+------+
1 row in set (0.00 sec)

事务B:

mysql> start transaction with consistent snapshot;
Query OK, 0 rows affected (0.00 sec) mysql> update t set k=k+1 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from t where id = 1;
+----+------+
| id | k |
+----+------+
| 1 | 3 |
+----+------+
1 row in set (0.00 sec)
实验结果分析

假设实验开始前记录的最新已提交版本事务id为90,事务A的id为99,事务B的id为100,事务C的id为101。

先分析B

在B查询的时候,id=1记录的最新版本为事务C更新的并且已经提交,事务B做的update操作,会被拆分成两步:

  1. select * from t for update;
  2. update t set k=k+1;

第一步会在当前行上加X锁,并且读最新已提交的版本,虽然C记录的事务id大于B,但是B会去读取它,所以在第一步,B拿到了已经被事务C更新为2的数据。

第二步,事务B会在2的基础上加一,把当前记录更新为3,并且未提交,且事务版本号为事务B的100。

再分析A

在A查询的时候,id=1记录的最新版本为事务B更新的,并且未提交,所以事务A继续往前找,直到找到事务id为90的已提交记录读取出来,所以事务A读取到的为事务id=90更新的1。

场景实战

并发减库存的场景,目前库存num=200,初始代码逻辑如下:

select num from t where t > 0;

update t set num = num -200;

有两个并发的事务,事务A和事务B,在事务A执行到select语句后,事务B也执行到select,两个事务都拿到了num=200,按照上面的语句继续做更新操作,事务B结束后就会发现库存num变成了负值,如何修改呢?

可以改成只写一个update语句

update t set num = num - 200 where num >= 200

然后根据返回的影响行数做判断,如果影响行数为0,说明库存已经为0,需要做相关的后续业务处理。

MySQL-事务中的一致性读和锁定读的具体原理的更多相关文章

  1. mysql事务隔离级别、脏读、幻读

    Mysql事务隔离级别本身很重要,再加上可能是因为各大公司面试必问的缘故,在博客中出现的概率非常高,但不幸的是,中国的技术博客要么是转载,要么是照抄,质量参差不齐,好多结论都是错的,对于心怀好奇之心想 ...

  2. mysql 事务中如果有sql语句出错,会导致自动回滚吗?

    事务,我们都知道具有原子性,操作要么全部成功,要么全部失败.但是有可能会造成误解. 我们先准备一张表,来进行测试 CREATE TABLE `name` ( `id` int(11) unsigned ...

  3. 一步一步学MySQL-一致性非锁定读和锁定读

    一致性非锁定读(consistent nonlocking read) 一致性非锁定读是值InnoDB存储引擎通过多版本控制(multi versioning)的方式来读取当前执行时间数据库中的数据. ...

  4. MySQL事务隔离级别的实现原理

    回顾 在MySQL的众多存储引擎中,只有InnoDB支持事务,所有这里说的事务隔离级别指的是InnoDB下的事务隔离级别. 读未提交:一个事务可以读取到另一个事务未提交的修改.这会带来脏读.幻读.不可 ...

  5. MySQL事务实现原理

    MySQL事务隔离级别的实现原理 知识储备 只有InnoDB支持事务,所以这里说的事务隔离级别是指InnoDB下的事务隔离级别 隔离级别 读未提交:一个事务可以读取到另一个事务未提交的修改.这会带来脏 ...

  6. mysql事务之一:MySQL数据库事务隔离级别(Transaction Isolation Level)及锁的实现原理

    一.数据库隔离级别 数据库隔离级别有四种,应用<高性能mysql>一书中的说明: 然后说说修改事务隔离级别的方法: 1.全局修改,修改mysql.ini配置文件,在最后加上 1 #可选参数 ...

  7. 深度剖析 MySQL 事务隔离

    概述 今天主要分享下MySQL事务隔离级别的实现原理,因为只有InnoDB支持事务,所以这里的事务隔离级别是指InnoDB下的事务隔离级别. 隔离级别 读未提交:一个事务可以读取到另一个事务未提交的修 ...

  8. mysql事务隔离级别详解和实战

    A事务做了操作 没有提交 对B事务来说 就等于没做 获取的都是之前的数据 但是 在A事务中查询的话 查到的都是操作之后的数据 没有提交的数据只有自己看得到,并没有update到数据库. 查看InnoD ...

  9. 粗谈MySQL事务的特性和隔离级别

    网上对于此类的文章已经十分饱和了,那还写的原因很简单--作为自己的理解笔记. 前言 ​  此篇文章作为自己学习MySQL的一些个人理解,使用的引擎是InnoDb.首先先讲讲事务的概念,在<高性能 ...

  10. MySQL事务(二)事务隔离的实现原理:一致性读

    今天我们来学习一下MySQL的事务隔离是如何实现的.如果你对事务以及事务隔离级别还不太了解的话,这里左转. 好的,下面正式进入主题.事务隔离级别有4种:读未提交.读提交.可重复读和串行化.首先我们来说 ...

随机推荐

  1. 深入理解ASP.NET Core 管道的工作原理

    在 .NET Core 中,管道(Pipeline)是处理 HTTP 请求和响应的中间件组件的有序集合.每个中间件组件都可以对请求进行处理,并将其传递给下一个中间件组件,直到请求到达最终的处理程序.管 ...

  2. C# Windsor Castle 简单例子

    Windsor是Castle的IOC框架.需要用到两个dll(Castle.Core.dll和Castle.Windsor.dll). 1.接口以及接口实现类: public interface IT ...

  3. Elasticsearch-总结列表

    1.lucene基本概念 目前以 Lucene 为基础建立的开源可用全文搜索引擎主要是 Solr 和 Elasticsearch. Solr 和 Elasticsearch 都是比较成熟的全文搜索引擎 ...

  4. 微服务实战系列(一)-注册中心Springcloud Eureka服务端-copy

    1. 场景描述 springcloud提供了一整套可行的构建分布式系统的方案,使的企业/开发人员能够快速沟通分布式系统,今天快速构建下springcloud的注册中心Eureka. 2. 解决方案 2 ...

  5. Android性能测试(内存、cpu、fps、流量、GPU、电量)——adb篇

    adb 常用命令 获取连接设备号:adb devices     列出设备所有已安装的包名 (不需root权限) adb shell "pm list packages",可以加上 ...

  6. w3cschool-OpenResty 最佳实践

    https://www.w3cschool.cn/openresty1/ OpenResty 简介 OpenResty(也称为 ngx_openresty)是一个全功能的 Web 应用服务器.它打包了 ...

  7. canal源码分析简介-4

    7.0 driver模块  2018-11-10 22:30:19  6,053 4 driver,顾名思义为驱动.熟悉jdbc编程的同学都知道,当项目中需要操作数据库(oracle.sqlserve ...

  8. Flu PG walkthrough Intermediate

    nmap ┌──(root㉿kali)-[/home/ftpuserr] └─# nmap -p- -A 192.168.192.41 Starting Nmap 7.94SVN ( https:// ...

  9. manim边做边学--交替变换

    今天,我们将介绍 Manim 中两个用于交替变换的动画类:CyclicReplace 和 Swap. 无论是在展示数学概念的动态变化,还是在图形设计中呈现元素的巧妙交互,这两个动画类都扮演着重要角色. ...

  10. 在没有网关的IP地址上编写路由,实现另一个网段可以访问到该主机

    应用场景:该主机两个网卡分别对应两个IP地址 192.168.1.2网段为医保网,并且主机使用了改网段的的网关.192.168.100.99网段地址为互联网地址,没有使用该网段的网关. 我们开发小组通 ...