草庐IT

MySQL事务详解

搬长你好 2023-03-28 原文

什么是事务?

  • 事务是一个不可分割的工作单元,工作单元要么工作完成,要么什么也不做。
  • 从应用层面来说一个事务对应了一个完整的业务功能。
  • 从数据库层面的来讲事务就是由一批DML语句构成。

事务的分类

MySQL的InnoDB存储引擎支持扁平事务、带有保存点的事务、链事务、分布式事务。

  • 扁平事务(Flat Transactions)
扁平事务应用最为广泛,实现最为简单,扁平事务的所有操作都是在同一个层级,这些操作要么全部成功,要么全部回滚,不能存在部分提交或者部分回滚的的场景。

扁平事务

  • 带保存点的扁平事务(Flat Transactions with Sacepoint)
扁平事务的限制就在于不能部分回滚或者提交,而有的场景是这么做是代价非常大的。比如我们举个例子:

我们玩生存类游戏,如果我们意外失败就必须从出生地开始玩,那么这会是让人崩溃的,我们希望有一个游戏存档,如果游戏失败我们可以从最近的一个存档重新加载游戏。

带保存点的扁平事务就是,除了支持扁平事务的操作外,允许事务执行过程中回滚到该事务较早的一个状态,而这个较早的状态就是保存点来记录的。

带保存点的扁平事务

  • 链事务(Chained Transaction)
链事务是一种保存点事务的变种,两者的最大区别是,带保存点的事务可以回滚到较早前的任意保存点,而链式事务只能回滚到最近一个保存点;带保存点的事务因为需要回滚到任意保存点,固其事务执行期间所占用的资源是不会被释放的,而链事务则在执行完成当前节点后会释放掉不需要的资源,并将下一个节点需要的资源隐士传递下去。链事务可以参考Flink流式计算的Checkpoint机制,两者非常的相似。

链事务

  • 嵌套事务(Nested Transaction)
嵌套事务顾名思义,事务结构看上去就像一棵树,根节点就是一个顶层事务,所有的叶子节点都是扁平事务(也就是说叶子节点才是真正干活儿的),事务的嵌套层级不受限制。子事务可以提交也可以回滚,但是其提交不会立即生效,只有在顶层事务提交之后所有子事务才会被真正的提交。

嵌套事务

  • 分布式事务(Distributed Transactions)
分布式事务是指一个在分布式环境下运行的扁平事务,在本章中主要介绍本地事务,分布式事务我会在后续章节是介绍。

事务的ACID特性

  • A(Atomicity)原子性:整个事务操作是一个完整的不可分割的整体,只有事务中的所有操作都执行成功,事务才算执行成功,否则就要回滚到事务执行前的状态,即要么全部都做,要么全都不做。
例如:转账场景,自己账户扣除转账额度与对方账户收到转账这两个操作必须是原子的。

  • C(Consistency)一致性:事务将数据库从一种状态转变为另一种状态,在事务执行前和事务执行后,数据库的完整性约束没有被破坏。
例如:用户表的用户ID列有unique约束,即用户ID不可重复,如果事务执行插入了一样的用户ID,那么就产生了不一致的状态。

  • I(Isolation)隔离性:隔离性(又称并发控制)非常好理解,就是两个事务之间不能相互影响,即当前事务提交之前所作出的修改对其他事务都不可见,上一章我们讲到了MySQL锁,它就可以起到控制并发的作用。
  • D(Durability)持久性:持久性是指事务一旦提交,其结果就是永久性的,即使是服务器宕机,数据也必须能够得到恢复,除了硬件故障,数据物理损坏,否则必须保证事务执行结果的永久性,也就是保证高可靠性(High Reliablility)。

事务如何实现

事务的原子性、一致性、持久性通过redo log与undo log来完成,事务的隔离性由锁与MVCC来完成。

Redo log(重做日志)

Redo log是用来实现事务的持久性,为了更好的读写性能,InnoDB会将数据缓存在内存中,对磁盘数据的修改也会落后于内存,如果进程或系统崩溃,则数据面临丢失的风险,这时重做日志就起到了保证数据的一致性与持久性作用。重做日志主要记录了以页为单位的数据修改信息,其结构如下:

redo log 结构

  • 重做日志在Buffer中是连续写入的,Buffer中的数据会适时地刷新到物理文件中;
  • 文件顺序写入,每个事务的重做日志追加到文件末尾;
  • 单个文件大小固定,写满以后会切回到文件组的下一个文件;
  • 重做日志文件组的文件个数是固定的,写完最后一个文件则继续回到第一个文件开始写入;
  • 每个重做文件有固定2K的文件头,文件头的之后是以一个个512bytes的Block,每个Block有16bytes的头尾信息;
  • 重做日志有一个全局的日志序列号(LSN:Log Sequence Number),单调递增,表示事务写入的重做日志的字节总量,也就是一个日志偏移量。

Undo log(回滚日志)

重做日志记录了事务的行为,可以在需要的时候对页进行“重做”,但是事务有时是需要被回滚的,当语句执行失败或者用户请求回滚,就可以通过undo log将数据回滚到修改前的样子,undo log是存储了行记录的变更。其主要包含两类undo log:

两种undo log结构

  • insert undo log:insert操作时产生,只对当前事务本身可见,在事务提交之后可直接删除。
  • update undo log:delete与update操作产生,需要提供历史版本,为后续章节要讲到的MVCC服务,其交由purge线程统一删除。
  • undo log需要通过group commit 操作将数据fsync到磁盘,以保证事务的持久性。
下面是一个事务与undo log的关系结构:

事务与undo log关系结构

事务隔离

事务在并发场景下很难保证事务的隔离性一致性,主要有以下一些事务的并发一致性问题。

事务并发问题

  • 脏读(Dirty Read):事务A读取了另外一个并行事务B未提交的数据。
  • 不可重复读(Non-Repeatable Read:在解决脏读问题之后,能够保证读事务读取到的数据都是持久的数据,如果事务A多次读取同一数据,正好在两次读取之间,另外一个并行事务B提交了这一数据的修改,这就导致事务A多次读取到的同一数据内容不一样。
  • 幻读(Phantom):与不可重复读类似,事务A多次查询一个范围,另外一个并行事务B向该范围内插入或删除了数据并提交,当事务A再次查询时发现记录变多或者丢失。
  • 更新丢失(Lost Updates):两个事务A和B修改了同一数据,由于未提交事务之间看不到对方的修改,因此都以一个旧的前提去更新了同一数据。
  • 写偏差(Write Skew):与更新丢失类似,都是写前提被改变,写偏差则是事务A读取某些数据,作为另一些写入的前提条件(更新丢失是针对同一数据),但这时另外一个事务B对事务A已读取的数据做了修改并提交,从而导致事务A做了错误的commit操作。
  • 读偏差(Read Skew):如事务A读取某两个数据求和,事务B在事务A读取期间对已读取数据做了增减,此时事务A求和得到的结果就会与实际的结果不一致。
针对上面的并发问题,InnoDB存储引擎通过MVCC(当然MVCC本质上也是一种乐观锁)与锁(关于锁的介绍可以阅读我的上一篇文章)来解决事务的隔离性一致性问题。

事务隔离级别

事务隔离级别是MySQL对ACID的实现程度上的分级,分为了四个等级,等级越高数据库越安全,每种隔离级别解决了不同事务并发一致性的问题,具体如下:

  • READ UNCOMMITTED(读未提交):这是一个最差的隔离级别,该级别下事务可以读到其它事务未提交的数据,也就是说在该事务隔离级别下会发生上述的所有并发一致性问题。
  • READ COMMITTED(读已提交):事务只能读取到已提交的修改,也就是说多个并发的事务之间的修改是相互不可见的,该事务隔离级别解决了脏读问题。
  • REPEATABLE READ(可重复读):该级别保证同一个事务中多次读取同一数据的结果是一致的,该级别是InnoDB默认的隔离级别,该隔离级别解决了脏读与不可重复读问题,但是仍可能出现幻读的情况(InnoDB存储引擎在该隔离级别下使用了Next-Key Lock解决了幻读问题)。
  • SERIALIZABLE(串行化):强制事务串行化执行,没有并发,那么并发问题自然就不存在了,当然在该级别下的事务性能非常低。
关于事务隔离的实现会在后续文章详细讲解,本文不在展开。

事务的执行过程

事务的执行过程

  • 查询数据,若数据不存在于buffer,则从磁盘加载;
  • 数据更新前,先将当前数据记录到undo log重,以便后续可能出现的回滚做准备;
  • 更新Buffer Pool中的数据;
  • 将更新的数据写入到Redo Log Buffer中;
  • 准备提交事务,调用fsync将Redo Log Buffer的数据写入到redo log文件中,状态记为prepared;
  • 准备提交事务,binlog写入到磁盘中;
  • binlog写入成功后,将redo log的状态更新为commit;
binlog的开启时会存在一个内部XA的问题(binlog是在MySQL层,而redo log在存储引擎层),这里引入了2PC(二阶段提交):

  • prepare阶段:redo log持久化到磁盘,同时设置状态为prepared,binlog此时不错任何操作。
  • commit阶段:存储引擎释放锁,是否回滚段,然后binlog持久化到磁盘,然后存储引擎层提交,更改redo log的状态为commit。

有关MySQL事务详解的更多相关文章

  1. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  2. ruby - 分布式事务和队列,ruby,erlang,scala - 2

    我有一个涉及多台机器、消息队列和事务的问题。因此,例如用户点击网页,点击将消息发送到另一台机器,该机器将付款添加到用户的帐户。每秒可能有数千次点击。事务的所有方面都应该是容错的。我以前从未遇到过这样的事情,但一些阅读表明这是一个众所周知的问题。所以我的问题。我假设安全的方法是使用两阶段提交,但协议(protocol)是阻塞的,所以我不会获得所需的性能,我是否正确?我通常写Ruby,但似乎Redis之类的数据库和Rescue、RabbitMQ等消息队列系统对我的帮助不大——即使我实现某种两阶段提交,如果Redis崩溃,数据也会丢失,因为它本质上只是内存。所有这些让我开始关注erlang和

  3. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  4. ruby-on-rails - 无法安装 mysql2 0.3.14 gem - 2

    我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby​​目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin

  5. ruby - 如何使用 ruby​​ mysql2 执行事务 - 2

    我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi

  6. ruby-on-rails - 在 rails 中提交后回滚事务 - 2

    保存成功后可以回滚吗?让我有一个带有属性名称、电子邮件等的用户模型。例如u=User.newu.name="test_name"u.email="test@email.com"u.save现在记录将成功保存在数据库中,之后我想回滚我的事务(不是销毁或删除)。有什么想法吗? 最佳答案 您可以通过交易来做到这一点,请参阅http://markdaggett.com/blog/2011/12/01/transactions-in-rails/例子:User.transactiondoUser.create(:username=>'Nemu

  7. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  8. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

  9. ruby-on-rails - 当我通过 rvm 使用 rails3 时,如何在 ubuntu 上安装 mysql2 gem? - 2

    我正在尝试绕过rails配置这个极其复杂的迷宫。到目前为止,我设法在ubuntu上设置了rvm(出于某种原因,ruby在ubuntu存储库中已经过时了)。我设法建立了一个Rails项目。我希望我的测试项目使用mysql而不是mysqlite。当我尝试“rakedb:migrate”时,出现错误:“!!!缺少mysql2gem。将其添加到您的Gemfile:gem'mysql2'”当我尝试“geminstallmysql”时,出现错误,告诉我需要为安装命令提供参数。但是,参数列表很大,我不知道该选择哪些。如何通过在ubuntu上运行的rvm和mysql获取rails3?谢谢。

  10. ruby - 如何使用 PG Ruby Gem 有条件地回滚事务 - 2

    我目前正在上一门数据库类(class),其中一个实验室问题让我困惑于如何实现上述内容,事实上,如果可能的话。我试过搜索docs但是定义的交易方式比较模糊。这是我第一次尝试在没有Rails的情况下进行任何数据库操作,所以我有点迷茫。我已经成功地创建了一个到我的postgresql数据库的连接并且可以执行语句,我需要做的最后一件事是根据一个简单的条件回滚一个事务。请允许我向您展示代码:require'pg'@conn=PG::Connection.open(:dbname=>'db_15_11_labs')@conn.prepare('insert','INSERTINTOhouse(ho

随机推荐