InnoDB事务不能只懂ACID

本文主要是对MySQL事务的知识点进行归纳总结,除了ACID之外的一些比较重要的知识点,多了解对我们对数据库的使用有一定的帮助

数据库事务是数据库系统与文件系统的重要区别之一,就是因为有了事务,数据库系统才成为大众存储数据的重要组件.接下来先介绍一下事务是什么,再介绍事务的分类,再谈谈事务是怎么实现的,一条龙梳理清楚事务的知识点.

一、事务是什么

事务是访问并更新数据库数据的一个程序执行单元.事务具有以下四个特性

  • 原子性:要么成功,要么失败
  • 一致性:让数据库系统从一种状态变更为另一种状态,但是事务开始前后数据库的完整性约束等特性不会被破坏.即执行前后状态保持一致.
  • 隔离性:两个事务读写对象会被相互隔离,这一特性是通过数据库中的锁进行实现.可以参考之前的一篇介绍锁的文章链接
  • 持久性:即存盘操作,提交成功后数据能正确落盘,数据能得到持久化保障

二、事务的分类

1、扁平事务

也就是我们经常看见的普通事务,由begin开始,commit或rollback结束

2、带保存点的扁平事务

当我们运行一个逻辑比较复杂的事务,后面一个动作执行失败我们不希望前面几个动作都会被回滚,那么我们可以在A动作执行完成后,设置一个保存点S,当B动作执行失败的时候我们可以不回滚整个事务,而是回滚事务到保存点S,然后让事务继续执行.
注意,回滚到保存点时,这个事务是从未被中断,持有的锁也会继续持有.保存点只是提高了运行性能,让事务运行的时候不至于因为一个小动作而导致整个事务被回滚.我们对这种事务叫带保存点的扁平事务.
实际上我们比较少运用到这样的特性,但是InnoDB是支持的,可以通过以下命令对保存点进行设置,并回滚到某一个保存点

SAVEPOINT identifier
ROLLBACK [WORK] TO [SAVEPOINT] identifier
RELEASE SAVEPOINT identifier

可以参考MySQL官方文档

3、链式事务

与带保存点的事务类似,也是提高系统性能,让我们的操作失败不致于整个流程回滚.但是链式事务不是设置保存点,而是类似于把一个长事务拆解为一个个小事务串起来的事务链,比如在A事务节点执行后我们可以立刻开始事务B,此时A已经完成提交并已经释放锁,当B执行失败的时候我们可以回滚到A的执行结束时的状态并重新执行.同样的InnoDB中也支持此类的事务,可以通过completion_type这个全局参数进行控制.这个参数代表了事务的完成类型,默认是非链式的,调成1是链式事务,在一个事务提交或回滚下一个动作会默认启动一个事务,具体可以参考MySQL官方文档

4、嵌套事务

类似一颗树一样,父事务里面包裹着许多的子事务,当每个子事务完成提交父事务才能被提交,一个子事务被回滚了会让这棵“树”所有的事务都被回滚.这样的事务叫嵌套事务.
因为InnoDB没有对这种事务做实现,所以也不再细说,不过要注意的是,之前在写Java的时候可以在一个事务里面开启另一个事务,其实并不是开启一个嵌套事务,而且将当前事务挂起并重新开始一个新的事务.

5、分布式事务

指的是在一个分布式环境下运行的扁平事务,这也是微服务设计里面的一种分布式事务解决方案,方案很多复杂.
虽然MySQL也能对其支持,但实际应用场景并不多,所以也不再继续深入.但这里我们可以留意一个知识点就是内部的分布式事务.
内部分布式事务体现在bin log写入跟redo log写入时.因为两者是分别写入的,万一一者写入成功,另一者写入失败,就会导致数据不一致的问题.
所以在binlog跟redolog之间的写入用了分布式事务.当事务提交时,InnoDB存储引擎会先做一个prepare操作,将事务xid写入,接着进行二进制日志的写入,如果在InnoDB存储引擎提交前,MySQL宕机,再重启后会检查xid事务是否已经提交,如果没有,会重新做一次提交操作.

三、事务的实现

事务的隔离性是通过锁实现的,之前的一篇文章已经说过就不再重复.现在我们主要焦聚其他特性的实现.
原子性和持久性的实现是通过redo log.而一致性是通过undo log实现.下面我们分别说说这两种机制.

1、redo log

重做日志,保障事务的原子性和持久性的机制实现方案.重做日志体现的形式如下.

  • 缓冲
  • 文件

当一个事务开始时,就会根据执行的内容开始以日志的形式往redo log里面记录执行的过程.先写缓冲,再通过fsync刷盘,此时我们可以通过innodb_flush_log_at_trx_commit了解到它的刷屏策略.

  • 1 提交一次刷一次盘
  • 0 每一秒刷一次盘
  • 2 提交一次不刷盘,刷到系统缓存

看到这里是否感觉和redis的AOF刷盘机制非常相似,但是要注意此时的数据是刷到redo log里面,redo log记录的是物理日志,即表空间中哪一条数据发生了什么变化.redo log记录完成后还要去表空间中对真正的数据进行修改,后面恢复过程我们再细说如何保障数据的一致性.而这里要留意的是缓冲和刷盘策略,数据越可靠性能越低,要速度快那么持久性就无法得到保证.

1.1、内容格式

redo log以block的形式存在,称之为重做日志块,一个块大小为512字节,前12字节表示日志头、后8字节表示日志尾,就像http协议一样,有头有尾去包裹真正的数据.从这里我们看出来redo log的作用,它记录的是数据的操作而不是真正的数据,这样有两个好处
一来是不断的appand让真正的数据操作延后,加快操作.
二来是保存了操作记录,为宕机的数据回滚做好的备份方案.

1.2、LSN

一个int类型的数值,用来表示relog当前记录到哪一个位置.他分别可以记录以下几个地方的重做日志总量

  • 重做日志的写入总量,也就是实际的总量
  • checkpoint的位置,也就是磁盘此时记录的重做日志总量
  • 页的版本,也就是当前刷到内存的总量

举个例子,比如当前的LSN为1000,T1写入100字节的重做日志数据,那么LSN就会变成1100,然后我们就可以观察这几个地方LSN来判断重做日志是否已经落盘.
MySQL的数据回滚也是根据这个LSN来做判断,当LSN在redo log中和数据表空间的LSN不一致的时候,那么就可以通过redo log的操作对磁盘数据做回滚.在系统上我们可以通过show engine innodb status来看到这几个数字

---
LOG
---
Log sequence number 481356407564 //数据库当前的LSN
Log flushed up to 481356406060//刷新到重做日志的LSN,LSN并不总是由事务产生
Pages flushed up to 481356406060
Last checkpoint at 481356406051//刷新到数据磁盘的LSN
0 pending log flushes, 0 pending chkp writes
8655 log i/o's done, 1.25 log i/o's/second

1.3、数据恢复

MySQL启动的时候,检查数据磁盘里面的LSN与重做日志里面的LSN做比较,如果数据磁盘里面的记录的LSN比重做里面小,那么就开始对事务进行重做.

1.4、与bin log的区别

这是老生常谈的问题,但是这两套机制总爱让人犯糊涂,所以重新说明一下:

  • 作用不同:bin log是用来做数据恢复,和主从环境搭建,注意是主动的数据的恢复,要人为的去使用binlog来进行操作重放.它并不是为宕机做准备的
  • 架构层级不同:bin log是MySQL层级,与引擎无关
  • 内容格式不同:bin log是逻辑日志,说的是哪一条数据被做了什么操作.redolog是物理日志,说的是磁盘中那一个页哪里的数据发生了改变
  • 记录的时间不同:bin log是结果数据,只有当事务被提交的时候才会创建一条记录被记录,redo log是在事务的执行过程不断的记录,因为事务执行过程需要记录内存或磁盘哪里的数据被修改了.

redo log主要就是有这几个方面的特性,也因为有了这些特性才让事务的持久性,原子性成为可能.

2、undo log

回滚日志,逻辑概念,用于数据的回滚,除此之外也提供了MVVC功能,提高了数据库并发查询的性能.
undo log存放是在表空间中的,与数据存储的位置相同.记录的内容是变更与删除的数据,与变更前的数据,记录变更前的数据也就是MVVC的实现.
结合undo log,MySQL的回滚方式是这样的.
逻辑回滚,并非物理回滚,虽然从数据库视图层看出来数据是回滚了,但是其实物理层已经是变动了.在InnoDB中,实际上做的是与之前相反的一个动作,通过一个新的动作去抵消掉了上一次的动作.
虽然undo log记录了一条数据的多个版本,那么什么时候进行清除呢?这个要靠purge操作,当数据被删除与变更的旧数据并不会被立刻被清理.只要当这些数据不会被事务引用时,才会被purge所清理.
因为undo log是逻辑概念,往下深入就是数据结构、参数调控的知识,所以到此为止,主要是了解了undo log的作用和回滚的机制,重点留意的是逻辑回滚,抵消机制.

四、事务中我们要留意什么

1、留意隐式提交的语句

在InnoDB中存在一些语句每当执行完便会隐式提交,是没法回滚的,分为以下几类

  • DDL语句:alter、create、drop、truncate
  • 修改MySQL架构的操作:比如创建用户,分配权限,设置密码等
  • 管理语句:optimize table等

执行语句时间,即使在一个事务中执行,也要考虑是否会被隐式提交,无法回滚

2、留意不好的事务习惯

  • 不要在循环中提交
  • 数据库运维时,不要使用自动提交
  • 不要使用自动回滚程序
  • 不要使用长事务,要把长事务分解为小事务

3、留意事务隔离级别

  • 在InnoDB中使用可重复读不会影响多少性能,所以没必要用读已提交来代替可重复读
  • 序列化的实现方式是在InnoDB的实现中就是对每一条select的语句都加上一个排他锁,废弃了MVVC的功能,在InnoDB中可重复读这一级别已经能达到接近幻读这一要求了

回归主题,本文主要是对常见的事务知识点做小部分补充,以后遇到问题可以考虑事务这些细节机制,方便对相关问题做判断,希望对读者有所帮助

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值