Mysql事务

Innodb引擎支持以事务的方式执行SQL,事务包含ACID四个特性,分别是原子性、一致性、隔离性和持久化。

原子性

原子性是指开启事务后,使用commit提交事务或rollback回滚事务,使事务内的多条修改语句同时成功或失败。

原子性是通过redo log和undo log的方式实现。一起成功是通过在执行变更语句时,先把执行的sql先记录到redo log。记录成功后,代表变更结果已经持久化。一起都失败是事务开始后,对每条记录的变更都会记录undo log,在回滚事务的时候,通过undo log里记录的之前版本的数据进行回滚数据。

Redo Log为同个数据页下的多条数据变更记录一条日志

sql 复制代码
TransactionID: 127
PageID: 462
Operation: MULTIPLE_UPDATES
Updates:
  - UpdateType: UPDATE
    Before Image: (1, '小明')
    After Image: (1, '小李')
  - UpdateType: INSERT
    After Image: (2, '小红')
  - UpdateType: DELETE
    Before Image: (3, '小张')

Undo Log为每条变更的数据独立记录一条日志

sql 复制代码
TransactionID: 126
PageID: 459
Operation: UPDATE
Before Image: (1, '小明')

一致性

一致性是指事务结束后,各个表变更的记录状态是一样的。要么是成功状态,要么是失败状态,也就是开启事务之前的状态。在数据库发生意外奔溃时,会通过redo log和undo log恢复到崩溃之前的状态,确保数据库前后的一致性。

一致性是通过原子性、隔离性和持久性保证的。

隔离性

事务有不同的隔离级别,数据变更后,在不同事务之间,可见性不一样。

读未提交

是最弱的隔离级别,当前事务可以看见其他未提交事务的数据变更结果,这种现象称为脏读。

读已提交

当前事务可以看见其他已提交事务的数据变更结果。例如,第一次查询到的列a值等于1,第二次查到列a值等于2。这种现象称为不可重复读,也就是事务内没做修改,但是两次select查询到的结果不一样。

可重复读

当前事务无法看到其他事务的数据变更结果。也就是事务内没做修改,两次select查询到的结果基本是一样的。除了一种情况,第一次查询了范围数据,然后其他事务在同一个到范围内插入了数据,这时原来的事务再次查询同个范围的数据,就会读到另一个事务插入的新数据。这种现象称为幻读。但是Innodb的这个级别不会有幻读,因为它使用了临键锁(记录锁+间隙锁),会把查询范围内,存在的数据加上记录锁,不存在的数据范围加了间隙锁。这样其他事务想插入数据,就需要先获取锁。

但是另一种情况也会出现不可重复度。例如,有一条记录a=1。事务A先开启查询到a=1,事务B先执行a=a+1,并提交事务。这时事务A也执行a=a+1,再查询a,得到的结果是3而不是2。原因是update操作是先读取再更新记录,读取方式是当前读,即会读到最新的数据状态。所以事务A在更新a的时候,a已经等于2,然后被当前事务改成3。这时,事务再取读a值就是3。

序列化

序列化就是通过加锁的方式一个个执行事务,不存在可见性问题。

MVCC

对于读已提交和可重复读这两个隔离级别,Innodb使用了无锁读的技术MVCC和读视图,来提升查询性能。MVCC会在记录被修改时记录多个版本的数据,其实是一条最新版本的数据,然后跟着一串undo log。其他事务在开启一致性读后,根据事务ID在读视图里查询可见的数据版本。

这两个隔离级别的区别在于,读已提交是每次执行查询语句之前,都会重新创建读视图,这就会每次读到最近的已提交记录。可重复读是在第一次执行查询语句的时候创建读视图,这样能保证后面读到的数据是一样的。

读视图

MVCC里不同版本数据对事务的可见性由读视图来控制,读视图主要是一组事务ID,分为已提交事务、活跃事务、不可见事务三部分。

  • 已提交事务是指在当前事务开启的时刻,这些事务已经提交。这部分事务的变更对当前事务都是可见的。比当前活跃事务里最小的事务ID还小的事务,必然是已提交事务。在最小的事务ID和已分配的最大事务之间的非活跃事务,也算已提交事务。
  • 活跃事务是指当前事务开启的时刻,还未提交的事务。这部分事务的变更对当前事务都是不可见的。当前还活跃的事务ID集合。
  • 不可见事务是指当前事务开启的时刻,还不存在的事务。这部分事务的变更对当前事务都是不可见的。比当前系统已分配的最大事务ID还大的事务,必然是不可见事务。

持久性

持久性是指把数据变更写入磁盘,实现持久化的目的。Innodb使用AWL(ahead write log)方式,在事务提交的时候先把变更写入redo log,然后再同步到磁盘。

redo log写入磁盘的过程中分为三个状态,写入内存的redo buffer、写入文件的page cache、最后同步到硬盘。只有同步到硬盘了,数据才算最终持久化成功。

根据redo log的不同状态,提交事务时又分为三种同步策略,由参数innodb_flush_log_at_trx_commit控制:

0:redo log先写入redo buffer,由后台线程每秒写入page cache并同步到磁盘。这个过程数据库crash会导致数据丢失。

1:redo log会马上同步到磁盘(这个是默认值)

2:redo log写入到page cache,由后台线程每秒将日志写入磁盘。这个过程数据库crash不会影响page cache的内容,但是服务器宕机会导致数据丢失。

相关推荐
小林coding2 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
18号房客3 小时前
高级sql技巧进阶教程
大数据·数据库·数据仓库·sql·mysql·时序数据库·数据库架构
翔云1234564 小时前
MySQL purged gtid是如何生成和维护的
数据库·mysql
平行线也会相交6 小时前
云图库平台(三)——后端用户模块开发
数据库·spring boot·mysql·云图库平台
恒辉信达7 小时前
hhdb客户端介绍(53)
数据库·mysql·hhdb·数据库可视化界面客户端
Hello.Reader8 小时前
Redis热点数据管理全解析:从MySQL同步到高效缓存的完整解决方案
redis·mysql·缓存
是程序喵呀8 小时前
MySQL备份
android·mysql·adb
指尖上跳动的旋律8 小时前
shell脚本定义特殊字符导致执行mysql文件错误的问题
数据库·mysql
苹果醋312 小时前
2020重新出发,MySql基础,MySql表数据操作
java·运维·spring boot·mysql·nginx
先睡13 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式