百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

详解MySQL事务原理

wptr33 2025-01-11 17:49 20 浏览

今天给大家分享的是大数据开发基础部分MySQL的事务,事务在MySQL知识点中非常重要的部分,很多伙伴只是知道MySQL的四大特性,但不知道其中的原理,老刘这次给大家详细的描述MySQL四大特性的原理,MySQL事务篇的大纲如下:

什么是事务?

在MySQL中的事务是由存储引擎实现的,而且支持事务的存储引擎不多,我们主要讲解InnoDB存储引擎中的事务。

事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。

事务用来管理 DDL、DML、DCL 操作,比如 insert,update,delete 语句,默认是自动提交的。

事务的四大特性(ACID)

  1. Atomicity(原子性):构成事务的的所有操作必须是一个逻辑单元,要么全部成功,要么全部失败。
  2. Consistency(一致性):数据库在事务执行前后状态都必须是稳定的或者是一致的,就是说事务开始和结束后,数据库的完整性不会被破坏。
  3. Isolation(隔离性):事务之间不会相互影响。由锁机制和MVCC机制来实现的,其中MVCC(多版本并发控制):优化读写性能(读不加锁、读写不冲突),四种隔离级别为RU(读未提交)、RC(读已提交)、RR(可重复读)、SERIALIZABLE (串行化)。
  4. Durability(持久性):事务执行成功后必须全部写入磁盘,事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失。

事务的使用

begin或start transaction:开启一个事务;

commit:提交一个事务,并使已对数据库进行的所有修改称为永久性的;

rollback:回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。

ACID实现原理

下面我们就来详细讲解一下上述示例涉及的事务的ACID特性的具体实现原理。总结来说,事务的隔离性由多版本控制机制和锁实现,而原子性、一致性和持久性通过InnoDB的redo log、undo log和ForceLog at Commit机制来实现。

重做日志Redo Log

如果要存储数据则先存储数据的日志,一旦内存崩了,则可以从日志找重做日志保证了数据的可靠性,InnoDB采用了Write Ahead Log(预写日志)策略,即当事务提交时,先写重做日志,然后再择时将脏页写入磁盘。如果发生宕机导致数据丢失,就通过重做日志进行数据恢复。

回滚日志Undo Log

数据库崩溃重启后需要从redo log中把未落盘的脏页数据恢复出来,重新写入磁盘,保证用户的数据不丢失。当然,在崩溃恢复中还需要回滚没有提交的事务。由于回滚操作需要undo日志的支持,undo日志的完整性和可靠性需要redo日志来保证,所以崩溃恢复先做redo恢复数据,然后做undo回滚。

所以,在事务执行的过程中,除了记录redo log,还会记录一定量的undo log。undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。

Force Log at Commit机制

它实现事务的持久性,即当事务提交时,必须先将该事务的所有日志写入到重做日志文件进行持久化,然后事务的提交操作完成才算完成。为了确保每次日志都写入到重做日志文件,在每次将重做日志缓冲写入重做日志后,必须调用一次fsync操作(操作系统),将缓冲文件从文件系统缓存中真正写入磁盘。

总结一下就是redo log用于在崩溃时恢复数据,undo log用于对事务的影响进行撤销,也可以用于多版本控制。而Force Log at Commit机制保证事务提交后redo log日志都已经持久化。

原子性

原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做。例如银行转账要么成功,要么失败,是不存在中间的状态!

Undo Log是实现原子性的关键,靠的就是undo log。当事务对数据库进行修改时,InnoDB会生成对应的undo log。undo log它属于逻辑日志,它记录的是sql执行相关的信息。当发生回滚时,InnoDB会根据undo log的内容做与之前相反的工作:对于每个insert,回滚时会执行delete;对于每个delete,回滚时会执行insert;对于每个update,回滚时会执行一个相反的update,把数据改回去。

以update操作为例:当事务执行 update 时,其生成的 undo log 中会包含被修改行的主键(以便知道修改了哪些行)、修改了哪些列、这些列在修改前后的值等信息,回滚时便可以使用这些信息将数据还原到 update 之前的状态。

持久性

持久性是指事务执行成功后必须全部写入磁盘,事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失。

InnoDB作为MySQL的存储引擎,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘IO,效率会很低。为此,InnoDB提供了缓存(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当从数据库读取数据时,会首先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中。

虽然Buffer Pool的使用大大提高了读写数据的效率,但是也有别的问题,当MySQL宕机,而此时Buffer Pool中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。

于是,优秀的程序员们引入了redo log,当我们对数据进行修改时,除了修改Buffer Pool中的数据,还会在redo log中记录这次操作。当事务提交时,会调用fsync接口对redo log进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。

还有一点必须知道就是redo log采用的是WAL策略,所有修改先写入日志,再更新到Buffer Pool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。

隔离性

在MySQL隔离性中,一般有两种情况:

  1. 要求同一时刻只能有一个事务对数据进行写操作,InnoDB通过锁机制来保证这一点。
  2. 在进行读操作的时候,可能出现脏读、不可重复读、幻读的问题。

首先讲第一种情况,MySQL要求同一时刻只能有一个事务对数据进行写操作,InnoDB通过锁机制来保证这一点。

锁机制的基本原理可以理解为:事务在修改数据之前,需要先获得相应的锁;获得锁之后,事务便可以修改数据;该事务操作期间,这部分数据是锁定的,其他事务如果需要修改数据,需要等待当前事务提交或回滚后释放锁。

至于锁机制中的锁,一般就是之前讲到的MySQL锁,大家可以去看看这篇MySQL锁的内容。

接着讲第二种情况,读操作可能出现脏读、不可重复读、幻读的问题。

隔离性追求的是并发情形下事务之间互不干扰,但是在事务的并发操作中可能会出现一些问题:

  1. 丢失更新:两个事务针对同一数据都发生修改操作时,会存在丢失更新问题。
  2. 脏读:对于两个事务 T1,T2,T1 读取了已经被 T2 更新但还没有被提交的字段。之后,若 T2 回滚,T1读取的内容就是临时且无效的。
  3. 不可重复读:对于两个事务T1,T2,T1 读取了一个字段,然后 T2 更新了该字段。之后,T1再次读取同一个字段,发现字段的内容不一样。要求,多次读取数据的时候,在一个事务中读出的都应该是一样的。一般是由于 update 操作引发,所以将来执行的时候要特别注意。
  4. 幻读:对于两个事务T1,T2,T1 从一个表中读取了一个字段,然后 T2 在该表中插入了一些新的行。之后。如果 T1 再次读取同一个表,就会多出几行。就是发现数据的数量不一样。要求,在一个事务中多次去读取数据的时候都应该是一样的。

虽然有上述这些问题,但MySQL数据库为我们提供的四种隔离级别(由低到高):

  1. Read uncommitted (读未提交):最低级别,任何情况都无法保证。
  2. Read committed (RC,读已提交):可避免脏读的发生。
  3. Repeatable read (RR,可重复读):可避免脏读、不可重复读的发生。(InnoDB默认级别为RR,它可以解决幻读,主要原因是Next-Key(Gap)锁,只有RR才能使用Next-Key锁)
  4. Serializable (串行化):可避免脏读、不可重复读、幻读的发生。

解决脏读、不可重复读、幻读的问题使用的是MVCC,即多版本的并发控制协议。它说的就是在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)。

MVCC最大的优点是读不加锁,因此读写不冲突,并发性能好。InnoDB实现MVCC,多个版本的数据可以共存,主要是依靠数据的隐藏列( 也可以称之为标记位 )和undo log。其中数据的隐藏列包括了该行数据的版本号、删除时间、指向undo log的指针等等;当读取数据时,MySQL可以通过隐藏列判断是否需要回滚并找到回滚需要的undo log,从而实现MVCC。

MVCC如何解决脏读、不可重复读、幻读的问题

1、MVCC解决脏读

当事务T1在第三个时刻读取自己的余额时,会发现数据已被T2事务修改,并且T2的状态还没有提交。此时事务A读取最新数据后,根据数据的undo log执行回滚操作,得到事务T2修改前的数据,从而避免了脏读。

2、MVCC解决不可重复读

当事务T1在第二个时刻第一次读取数据时,会记录该数据的版本号(数据的版本号是以row为单位记录的),假设版本号为1;当事务T2对自己的余额进行修改并且提交时,该行记录的版本号增加,假设版本号为2;当事务T1在第五个时刻再一次读取数据时,发现数据的版本号2大于第一次读取时记录的版本号1,因此会根据undo log执行回滚操作,得到版本号为1时的数据,从而实现了可重复读。

3、MVCC解决幻读

InnoDB实现的RR通过next-key lock机制避免了幻读现象。

next-key lock是行锁的一种,实现相当于record lock(记录锁) + gap lock(间隙锁),它的特点是不仅会锁住记录本身(record lock的功能),还会锁定一个范围(gap lock的功能)。

当事务T1在第二个时刻第一次读取0<id<5数据时,会进行标记,标记内容包括数据的版本号等,并且标记的不只是id=1的数据,还将范围(0,5)进行了标记。我们接着在第三个时刻插入新的用户并且提交事务,最后第五个时刻再次读取0<id<5数据时,便可以发现id=2的数据比之前标记的版本号更高,此时再结合undo log执行回滚操作,避免了幻读。

稍微总结下,InnoDB通过锁机制、数据的隐藏列、undo log和类next-key lock,实现了一定程度的隔离性,可以满足大多数场景的需要。不过需要说明的是,RR虽然避免了幻读问题,但是毕竟不是Serializable,不能保证完全的隔离。

一致性

一致性是事物追求的最终目标,前面提到的原子性,隔离性,持久性都是为了保证数据库的一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。

总结

本文作为大数据开发指南MySQL的第四篇详细介绍了MySQL事务的内容,尤其是MySQL四大特性的原理。希望大家能够跟着老刘的文章,好好捋捋思路,争取能够用自己的话把这些知识点讲述出来!

相关推荐

redis的八种使用场景

前言:redis是我们工作开发中,经常要打交道的,下面对redis的使用场景做总结介绍也是对redis举报的功能做梳理。缓存Redis最常见的用途是作为缓存,用于加速应用程序的响应速度。...

基于Redis的3种分布式ID生成策略

在分布式系统设计中,全局唯一ID是一个基础而关键的组件。随着业务规模扩大和系统架构向微服务演进,传统的单机自增ID已无法满足需求。高并发、高可用的分布式ID生成方案成为构建可靠分布式系统的必要条件。R...

基于OpenWrt系统路由器的模式切换与网页设计

摘要:目前商用WiFi路由器已应用到多个领域,商家通过给用户提供一个稳定免费WiFi热点达到吸引客户、提升服务的目标。传统路由器自带的Luci界面提供了工厂模式的Web界面,用户可通过该界面配置路...

这篇文章教你看明白 nginx-ingress 控制器

主机nginx一般nginx做主机反向代理(网关)有以下配置...

如何用redis实现注册中心

一句话总结使用Redis实现注册中心:服务注册...

爱可可老师24小时热门分享(2020.5.10)

No1.看自己以前写的代码是种什么体验?No2.DooM-chip!国外网友SylvainLefebvre自制的无CPU、无操作码、无指令计数器...No3.我认为CS学位可以更好,如...

Apportable:拯救程序员,IOS一秒变安卓

摘要:还在为了跨平台使用cocos2d-x吗,拯救objc程序员的奇葩来了,ApportableSDK:FreeAndroidsupportforcocos2d-iPhone。App...

JAVA实现超买超卖方案汇总,那个最适合你,一篇文章彻底讲透

以下是几种Java实现超买超卖问题的核心解决方案及代码示例,针对高并发场景下的库存扣减问题:方案一:Redis原子操作+Lua脚本(推荐)//使用Redis+Lua保证原子性publicbo...

3月26日更新 快速施法自动施法可独立设置

2016年3月26日DOTA2有一个79.6MB的更新主要是针对自动施法和快速施法的调整本来内容不多不少朋友都有自动施法和快速施法的困扰英文更新日志一些视觉BUG修复就不翻译了主要翻译自动施...

Redis 是如何提供服务的

在刚刚接触Redis的时候,最想要知道的是一个’setnameJhon’命令到达Redis服务器的时候,它是如何返回’OK’的?里面命令处理的流程如何,具体细节怎么样?你一定有问过自己...

lua _G、_VERSION使用

到这里我们已经把lua基础库中的函数介绍完了,除了函数外基础库中还有两个常量,一个是_G,另一个是_VERSION。_G是基础库本身,指向自己,这个变量很有意思,可以无限引用自己,最后得到的还是自己,...

China&#39;s top diplomat to chair third China-Pacific Island countries foreign ministers&#39; meeting

BEIJING,May21(Xinhua)--ChineseForeignMinisterWangYi,alsoamemberofthePoliticalBureau...

移动工作交流工具Lua推出Insights数据分析产品

Lua是一个适用于各种职业人士的移动交流平台,它在今天推出了一项叫做Insights的全新功能。Insights是一个数据平台,客户可以在上面实时看到员工之间的交流情况,并分析这些情况对公司发展的影响...

Redis 7新武器:用Redis Stack实现向量搜索的极限压测

当传统关系型数据库还在为向量相似度搜索的性能挣扎时,Redis7的RedisStack...

Nginx/OpenResty详解,Nginx Lua编程,重定向与内部子请求

重定向与内部子请求Nginx的rewrite指令不仅可以在Nginx内部的server、location之间进行跳转,还可以进行外部链接的重定向。通过ngx_lua模块的Lua函数除了能实现Nginx...