【MySQL系列 03】事务隔离:为什么你改了我还看不见?

news2024/9/22 22:36:32

事务

今天的文章里,我会以 InnoDB 为例,剖析 MySQL 在事务支持方面的特定实现,并基于原理给出相应的实践建议,希望这些案例能加深你对 MySQL 事务原理的理解。

说到事务,银行应用是解释事务必要性的一个经典例子,假设一个银行的数据库有两张表:支票(checking)表和储蓄(savings)表。现在要从用户 Jane 的支票账户转移 200 美元到她的储蓄账户,那么需要至少三个步骤:

  1. 检查支票账户的余额高于 200 美元。
  2. 从支票账户余额中减去 200 美元。
  3. 在储蓄账户余额中增加 200 美元。

上述三个步骤的操作必须打包在一个事务中,任何一个步骤失败,则必须回滚所有的步骤。

可以用 START TRANSACTION 语句开始一个事务,然后要么使用 COMMIT 提交事务将修改的数据持久保留,要么使用 ROLLBACK 撤销所有的修改。事务 SQL 的样本如下:

mysql> START TRANSACTION;
mysql> SELECT balance FROM checking WHERE customer_id = 1;
mysql> UPDATE checking SET balance = balance - 200.00 WHERE customer_id = 1;
mysql> UPDATE savings SET balance = balance + 200.00 WHERE customer_id = 1;
mysql> COMMIT;

试想一下,如果执行到第四条语句时服务器崩溃了,会发生什么?天知道,用户可能会损失 200 美元。再假如,在执行到第三条语句和第四条语句之间时,另外一个进程要删除支票账户的所有余额,那么结果可能是银行在不知道这个逻辑的情况下白白给了 Jane 200 美元。

简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在 MySQL 中,事务支持是在引擎层实现的。

一个运行良好的事务处理系统,必须具备 ACID 这些标准特征。ACID 表示原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。

  • 原子性(atomicity):一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
  • 一致性(consistency):数据库总是从一个一致性的状态转换到另外一个一致性的状态。在前面的例子中,一致性确保了,即使在执行第三、四条语句之间时系统崩溃,支票账户中也不会损失 200 美元,因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。
  • 隔离性(isolation):通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。在前面的例子中,当执行完第三条、第四条语句还未开始时,此时有另外一个账户汇总程序开始运行,则其看到的支票账户的余额并没有被减去 200 美元。后面我们讨论隔离级别(Isolation level)的时候,会发现为什么我们要说“通常来说”是不可见的。
  • 持久性(durability):一旦事务提交,则其所做的修改就会永久保存在数据库中。此时即使系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且不可能有能做到 100% 的持久性保证的策略(如果数据库本身就能做到真正的持久性,那么备份又怎么能增加持久性呢)。在后面其他文章,我们会继续讨论 MySQL 中持久性的真正含义。

隔离级别

在 SQL 标准中定义了四个隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。

下面简单介绍一下四种隔离级别:

  • READ UNCOMMITTED(读未提交):最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读幻读不可重复读
  • READ COMMITTED(读提交):允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。

        大多数数据库系统的默认隔离级别都是 READ COMMITTED,如 Oracle。

  • REPEATABLE READ(可重复读):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(串行化):最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
隔离级别脏读不可重复读幻读加锁读
READ UNCOMMITTED×
READ COMMITTED××
REPEATABLE READ×××
SERIALIZABLE×××

MySQL InnoDB 存储引擎默认的事务隔离级别就是 REPEATABLE-READ(可重复读)

你可以用 show variables 来查看当前的值。

mysql> show variables like 'transaction_isolation';

+-----------------------+----------------+

| Variable_name | Value |

+-----------------------+----------------+

| transaction_isolation | READ-COMMITTED |

+-----------------------+----------------+

:MySQL InnoDB 存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读的问题。 

事务隔离的实现

理解了事务的隔离级别,我们再来看看事务隔离具体是怎么实现的。这里我们展开说明“可重复读”。

在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。

假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录。

当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的 多版本并发控制(MVCC)。对于 read-view A,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到。

同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的。

你一定会问,回滚日志总不能一直保留吧,什么时候删除呢?答案是,在不需要的时候才删除。也就是说,系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除。

什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。

基于上面的说明,我们来讨论一下为什么建议你尽量不要使用长事务。

长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。

在 MySQL 5.5 及以前的版本,回滚日志是跟数据字典一起放在 ibdata 文件里的,即使长事务最终提交,回滚段被清理,文件也不会变小。我见过数据只有 20GB,而回滚段有 200GB 的库。最终只好为了清理回滚段,重建整个库。

除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库,这个我们会在后面讲锁的时候展开。

事务的启动方式

如前面所述,长事务有这些潜在风险,我当然是建议你尽量避免。其实很多时候业务开发同学并不是有意使用长事务,通常是由于误用所致。MySQL 的事务启动方式有以下几种:

  1. 显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。
  2. set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接。

有些客户端连接框架会默认连接成功后先执行一个 set autocommit=0 的命令。这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务。

因此,我会建议你总是使用 set autocommit=1, 通过显式语句的方式来启动事务

但是有的开发同学会纠结“多一次交互”的问题。对于一个需要频繁使用事务的业务,第二种方式每个事务在开始时都不需要主动执行一次 “begin”,减少了语句的交互次数。如果你也有这个顾虑,我建议你使用 commit work and chain 语法。

在 autocommit 为 1 的情况下,用 begin 显式启动的事务,如果执行 commit 则提交事务。如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行 begin 语句的开销。同时带来的好处是从程序开发的角度明确地知道每个语句是否处于事务中。

你可以在 information_schema 库的 innodb_trx 这个表中查询长事务,比如下面这个语句,用于查找持续时间超过 60s 的事务。

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

小结

这篇文章里面,我介绍了 MySQL 的事务隔离级别的现象和实现,根据实现原理分析了长事务存在的风险,以及如何用正确的方式避免长事务。希望我举的例子能够帮助你理解事务,并更好地使用 MySQL 的事务特性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1464070.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STL - hash

1、unordered系列关联式容器 在C98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到O(),即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好 的查询是,进…

Java面试题之分布式/微服务篇

经济依旧不景气啊,如此大环境下Java还是这么卷,又是一年一次的金三银四。 兄弟们,你准备好了吗?冲冲冲!欧里给! 分布式/微服务相关面试题解 题一:CAP理论,BASE理论题二:…

智慧餐饮系统架构的设计与实现

随着科技的不断发展,智慧餐饮系统在餐饮行业中扮演着越来越重要的角色。智慧餐饮系统整合了信息技术,以提高餐饮企业的管理效率、客户服务质量和市场竞争力。本文将探讨智慧餐饮系统架构的设计与实现,并探讨其在餐饮行业中的应用前景。 架构…

Spring框架@Autowired注解进行字段时,使用父类类型接收子类变量,可以注入成功吗?(@Autowired源码跟踪)

一、 前言 平常我们在使用spring框架开发项目过程中,会使用Autowired注解进行属性依赖注入,一般我们都是声明接口类型来接收接口实现变量,那么使用父类类型接收子类变量,可以注入成功吗?答案是肯定可以的!…

springboot访问webapp下的jsp页面

一,项目结构。 这是我的项目结构,jsp页面放在WEB-INF下的page目录下面。 二,file--->Project Structure,确保这两个地方都是正确的,确保Source Roots下面有webapp这个目录(正常来说,应该本来就有&#…

职场数据汇总,数据汇报,动态大屏可视化制作与数据分析

大屏可视化模板。 动态大屏可视化的制作方法与详细操作步骤 如下操作: AIGC ChatGPT 职场案例 AI 绘画 与 短视频制作 PowerBI 商业智能 68集 Mysql 8.0 54集 Oracle 21C 142集 Office 2021实战应用 Python 数据分析实战, ETL Informatica 数据仓库案…

优化设备维修流程:易点易动设备管理系统的设备维修管理策略

在现代企业中,设备是生产运营的核心要素之一。然而,设备故障和维修常常给企业带来诸多困扰和成本。为了提高设备维修的效率和质量,许多企业开始采用先进的设备管理系统。在这方面,易点易动设备管理系统以其卓越的设备维修管理策略…

SpringBoot线上打包

背景: 1.我们打包时其实需要很多资源打到jar包之外,这样子修改了配置后,就可以生效了。 2.包的命名: 以mj为例子: 业务层: com.jn.mj // 这个是这个工程的总包名 com.jn.mj.gateway // web服集群 c…

【目标航迹管理(1)】基于d-s证据理论信息融合的多核目标跟踪方法

1 引言:从航机起始方法开始 我们为什么会有这个议题?因为航机起始方法。 处理目标航迹起始的方法主要分为两大类:批处理和序贯。 在杂波密度比较高的环境下,比如有红外卫星或地面雷达监视区域,则选用批处理方法&…

记录解决uniapp使用uview-plus在vue3+vite+ts项目中打包后样式不能显示问题

一、背景 从 vue2uview1 升级到 vue3vitetsuview-plus ,uview组件样式打包后不显示,升级前uview 组件是可以正常显示,升级后本地运行是可以正常显示,但是打包发布成H5后uview的组件无法正常显示,其他uniapp自己的组件可以正常显示…

jetson是什么?

jetson是NVIDIA推出的一系列嵌入式计算板,用于边缘计算。这些板卡专门设计用于加速机器学习和计算机视觉等人工智能应用,它们是低功耗设备,非常适合在无人机、机器人、智能相机和其他自动化设备中使用。 Jetson板卡包括多种型号,…

消息中间件之RocketMQ源码分析(十二)

Namesrv启动流程 Broker启动流程 BrokerStartup.java类主要负责为真正的启动过程做准备,解析脚本传过来的参数,初始化Broker配置,创建BrokerController实例等工作。BrokerController.java类是Broker的掌控者,它管理和控制Broker的…

【Docker实操】部署php项目

概述 最终达成的容器部署结构和原理如下图: 一、获取nginx、php官方镜像 docker pull nginx //拉取nginx官方镜像 docker pull php:7.4-fpm //拉取php官方镜像需要获取其他可用的php版本,可以上【docker hub】搜索【php】,所有的【xxx-fp…

抖店开通后的这些基础搭建,你了解吗?今天一文详解!

大家好,我是电商小布。 很多小伙伴在我们店铺开通后,接下来就会进行选品上架等工作。 但其实,在店铺刚开通时,小店的基础设置是并不完善的。 比如说:平台默认店铺是全地区包邮的。 想要让小店顺利运转,…

shiro 整合 springboot 实战

序言 前面我们学习了如下内容&#xff1a; 5 分钟入门 shiro 安全框架实战笔记 shiro 整合 spring 实战及源码详解 这一节我们来看下如何将 shiro 与 springboot 进行整合。 spring 整合 maven 依赖 <?xml version"1.0" encoding"UTF-8"?> …

思考:如何写出让同事难以维护的代码?

本文从【程序命名&注释】【数据类型&类&对象】【控制执行流程】和【程序/结构设计】四个方面梳理了一些真实案例&#xff0c;相信通过这些案例你能迅速get技能&#xff1a;如何写出让同事难以维护的代码doge。 比起什么程序员删库跑路&#xff0c;我更喜欢「写出让…

2024最佳住宅代理IP服务商有哪些?

跨境出海已成为了近几年的最热趋势&#xff0c;大批量的企业开始开拓海外市场&#xff0c;而海外电商领域则是最受欢迎的切入口。新兴的tiktok、Temu&#xff0c;老牌的Amazon、Ebay&#xff0c;热门的Etsy、Mecari等等都是蓝海一片。跨境入门并不难&#xff0c;前期的准备中不…

压缩感知常用的重建算法

重建算法的基本概念 在压缩感知&#xff08;Compressed Sensing, CS&#xff09;框架中&#xff0c;重建算法是指将从原始信号中以低于奈奎斯特率采集得到的压缩测量值恢复成完整信号的数学和计算过程。由于信号在采集过程中被压缩&#xff0c;因此重建算法的目标是找到最符合…

施华洛世奇 Swarovski EDI需求分析

施华洛世奇为全球首屈一指的光学器材及精确切割仿水晶制造商&#xff0c;为时尚服饰、首饰、灯饰、建筑及室内设计提供仿水晶元素。施华洛世奇有两个主要业务&#xff0c;分别负责制造及销售仿水晶元素&#xff0c;以及设计制造成品。 EDI传输协议 施华洛世奇 Swarovski 与合作…

《租车 App:畅享自由出行的新选择》

在现代社会&#xff0c;人们对于出行的需求越来越多样化。为了满足这些需求&#xff0c;租车行业应运而生。而随着智能手机的普及&#xff0c;租车 App 的开发成为了提升用户体验、提高租车效率的重要途径。 一、市场需求与发展趋势 随着人们生活水平的提高和出行观念的变化&am…