手记

Mysql系列-事务

前言

面试过程中,事务也是一种常见面试官常见爱问的点,目前大概内容如下

  • 聊一下Mysql事务的ACID四大特性嘛,Mysql如何实现这ACID特性的
  • 开发过程中你是如何让解决长事务问题,长事务回带来什么问题

概念

​ 官方点的说法就是一组SQL语句组成的逻辑处理单元 通俗点理解就是Mysql经过一系列逻辑操作,这一系列操作必须全都成功,要不全部失败。事务有4个属性:原子性(A),一致性©,隔离行(I),持久性(D),这四个属性我们称为ACID特性,ACID理论是对Mysql特性的抽象,简单理解就是如果你的操作满足ACID特性,你就实现了事务

  • 原子性:一个事务中的所有操作要不就全都完成,要不全部失败,不会停止中间某一个环节,中间出现异常就会进行事务的回滚回到事务的最初状态,就像这个事务没有被执行过一样
  • 一致性:事务开始和结束时候,数据库中的数据状态都保存一致状态,比如两个人转账,转之前两个人的金额总和为1000,转账完成之后金额总和也应该为1000,不能多不能少,数据保存一致
  • 隔离性:一个事务的执行不能被其他事务干扰,并发执行的各个事务不能相互干扰。比如A给B转账和B给C转账这两个事务没有什么影响(如果B给C转账和A给B转账,B的余额就要看数据库的隔离级别了),事务的隔离性通常使用锁来实现的
  • 持久性:事务一旦提交,对数据库中数据改变就是永久的,并且不会被回滚

并发事务

多个事务同时操作数据库,如果没有任何机制,会出现,数据丢失,脏读,不可重复读等问题

  • 数据丢失:两个事务T1和T2同时读入数据进行数据的修改,T1先提交,T2后提交,T2提交的会把T1修改覆盖掉导致T1修改丢失
  • 脏读:两个事务,T1修改完一个数据并且写回磁盘,T2读取同一个数据之后,T1由于某一些原因事务进行回滚。这个时候T2读取的数据就和数据库中的数据不一致,T2读取的数据就是脏数据
  • 不可重复读:一个事务内多次读取同一个数据,读的数据内容不一致。两个事务 T1读取一个数据,读到数据假如是100,还没结束T2也来读取这个数据并且修改成80之后提交。T1事务再来读这个数据,发现这个数据变为80了,一个事务内两次读的数据内容不一致,我们就称为不可重复读
  • 幻读:一个事务内前后多次读取,读的数据总量不一致。两个事务 T1查询当前表的总量比如是5条,这个时候T2新增一条数据,T1再查的时候,发现当前表的总量为6条了,平白无故多了一条数据,这称为幻读

隔离级别

数据库必须具有隔离并发运行各种事务的能力,使其之间不相互影响,避免各种并发问题,比如脏读,不可重复读和幻读等问题,Mysql定义了以下四种隔离级别(由低到高),这4个隔离级别可以逐个解决脏读,不可重复读,幻读这几个问题。

  1. 读未提交(Read uncommitted 简成rn):一个事务可以读取到另外一个事务未提交的修改。这个隔离级别最差,会产生脏读,幻读,不可重复读问题
  2. 读已提交(Read Commit):一个事务只能读取另外一个事务已经提交的修改,避免脏读,仍然会存在不可重复都和幻读的问题。Oracle默认隔离级别就是这个
  3. 可重复读(Repeated Read):相同事务多次读取相同的数据返回的结果都是相同的,避免的脏读和不可重复读的问题,但是没避免幻读。Mysql默认的隔离级别就是这个。Mysql通过多版本并发控制(MVCC),间隙锁的问题解决了幻读
  4. 串行话(Serializable):数据库最高的隔离级别,事务都会排着队一个个执行,能够解决上面几个问题,但是执行效率很差。

开启事务

  • begin/start transaction:显示开启一个事务
  • commit:提交一个事务
  • rollback:回滚一个事务

实现原理

​ 先说结论,Mysql通过redo log和undo log保证事务的原子性,一致性和持久性,隔离性是同时锁方式来实现, 接下来一一介绍这几个名词。

redo log

·Redo log 称为重做日志,主要用来进行数据恢复使用,包含设计两部分内容,一部分是内存中日志缓冲(redo log buffer),该部分日志因为在内存中,重启之后会丢失,另外一部分是在磁盘中的日志文件(redo log file),这部分日志是持久的,接下来我们通过一个更新例子详细了解一下redo 日志的相关作用

数据更新流程

​ 我们一般在开发过程中,一般下面类似这样的sql去更新数据库中的数据,今天我们来研究一下这个sql语句Mysql是如何执行的,博主在学习相关知识点的时候,发现CSDN上一篇博客相关图简单明了(点击进入相关链接)以及Mysql45讲相关知识点,今天我们站在前辈的肩膀上学习一下相关知识点,如有侵权,请联系博主删除。

update user set name="程序员fly" where  id=8

如图所示,我们看到,客户端发送一起更新请求,需要Server层和存储引擎层

Server层

​ 我们常常使用的存储引擎,触发器,视图都在这一层上面实现,Server层只要有以下几个构成

  • 连接器:主要用来管理连接和权限认证的,客户端请求首先经过三次握手和四次挥手建立TCP连接之后,连接器会首先查询这个用户拥有的权限
  • 查询缓存:连接器建立完成之后如果是查询语句,会看一下缓存中有没有数据,因为我们执行的更新语句,所以在相关表上更新的时候,这个表上面的所有更新都失效。(PS:缓存比较鸡肋,多数情况下其实不用的)
  • 分析器:缓存完事之后,会到达分析器,分析器会进行词法分析和语法分析,看sql是否有语法错误和词法错误,完事交给优化器
  • 优化器:优化器经过分析上面的sql,会决定使用id这个索引,接着调用执行器
  • 执行器:执行器会调用存储引擎进行相关操作,接下俩就是存储引擎层上面都操作了
存储引擎层

存储引擎主要设计数据的存储和提取的,当执行器传来具体的执行计划的时候,存储引擎是这样干的

  • 存储引擎会先找到id=8这一行,查找过程是用过树搜索的方式进行,如果查找的数据页在内存中,就会返回给执行器,如果不在,就会将数据从磁盘中读入内存,然后再返回
  • 执行器拿到存储引擎返回的数据,会将name修改为’程序员fly‘,然后调用存储引擎写入这条数据
  • 存储引擎会将这行数据更新内存,同时将记录更新操作记录到redo log(这时候的redo log 是放在redo log buffer中,状态为prepare状态),告知执行器完事了,可以随时提交(ps 两阶段提交)
  • 执行器生成这个操作的binlog到binlog日志缓冲池中,(缓冲池何时刷到物理磁盘看参数与sync_binlog控制)Binlog记录未完成执行,执行器会同时存储引擎
  • 存储引擎收到执行器通知之后,更新redo log buffer中的redo log状态为commit状态,更新完成(redo log buffer里面的redo何时刷盘有参数innodb_flush_log_at_trx_commit控制)
    • 参数设置为0的时候,表示每次事务提交时都只是把redo log留在redo log buffer中
    • 参数为1的时候,表示每次事务提交时都将redo log直接持久化到磁盘(一般数据库是设置为1,设置1的时候,redo log在prepare阶段就需持久化磁盘)
    • 参数2的时候,表示每次事务提交时都只是把redo log写到page cach(文件系统)

通过上面一些列流程,只要Mysql中redo log持久化到磁盘中以后,当系统崩溃之后,就可以通过redo log来进行数据的恢复

undo log

​ undo log主要有两个功能:实现事务回滚和多版本并发控制(MVCC),数据在进行修改的时候,不仅会记录redo log。还会记录相对于的undo log,如果过程中数据库异常等原因导致事务失败会通过undo log进行事务回滚,实现原子性关键,undo log记录的逻辑日志。

  • 当delete一条数据的时候。undo log记录的是一条对应的insert记录,删除记录的是插入
  • 当update的时候,记录的是一条相反的update信息

MVCC

​ undo log主要有两个功能:实现事务回滚和多版本并发控制(MVCC),在Mysql中数据是有版本这个概念的,同一条数据在系统中会存在多个版本,事务的隔离性实现就是通过对比数据版本来实现的,如果不可见,就通过undo log回滚到上一个版本,一直到比较对当前视图可见的值,我们来看一个例子(例子来源Mysql45讲),假设现在数据库中一个值为1,被顺序改成了2,3,4,在回滚日志中就会这样记录,如图

比如这个数据的当前值4,但是查询这个数据的时候,不同事务有不同的视图(read-view),视图A,B,C,D中对应的值分为为1,2,3,4。事务A在查询的数据的时候,会通过undo日志回滚到视图A所看到的的1,这样隔离我们想一下有什么好处 这样可以增加并发,我们查询的时候不用等到另外一个事务释放锁,使得不同事务的读-写,写-读操作能够并发执行,提升系统性能

总结

  • 原子性:使用undo log来实现,如果事务执行过程中出现错误,系统会通过undo log日志回滚返回事务开始的状态
  • 持久性:使用redo log来实现,只要redo log日志持久化之后,当系统崩溃之后,可以通过redo log把数据恢复
  • 隔离性:使用MVCC以及锁来实现
  • 一致性:通过该回滚,恢复以及并发情况下实现一致性

几个问题

Q: redo log buffer的日志是不是每次都生成之后都需要持久化到磁盘中?

A:不需要,如果执行期间Mysql事务发生异常重启,因为事务还没提交,日志丢失时候也没影响

Q:一个事务完成提交需要两次写入磁盘,一个redo log(prepare阶段),一个binlog,Mysql做了什么优化

A: Mysql使用组提交的机制来进行优化,比如三个并发事务t1,t2,t3,对应的日志逻辑序列号LSN为10,20,30,我们简单理解为一段记录即可,t1提交的时候,直接提交LSN为30,t1返回的时候,将<30的都提交上去了,t2,t3完成之后直接返回,不用等待写磁盘。

Q:长事务会有什么问题

A: 长事务一直不释放,MVCC机制数据库上会有很长的回滚段。

1人推荐
随时随地看视频
慕课网APP