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的内容,但是服务器宕机会导致数据丢失。

相关推荐
爱吃烤鸡翅的酸菜鱼1 小时前
【SpringMVC】概念引入与连接
java·开发语言·mysql
小白考证进阶中2 小时前
0基础可以考MySQL OCP么?备考时间需要多久?
数据库·mysql·开闭原则
努力奋斗的小杨3 小时前
学习MySQL的第十二天
数据库·笔记·学习·mysql·navicat
苹果酱05673 小时前
【Azure Redis 缓存】在Azure Redis中,如何限制只允许Azure App Service访问?
java·vue.js·spring boot·mysql·课程设计
tcoding4 小时前
《MySQL 技术内幕-innoDB 存储引擎》笔记
数据库·笔记·mysql
uncofish5 小时前
springboot不连接数据库启动(原先连接了mysql数据库)
数据库·spring boot·mysql
宛如昨晚没早睡6 小时前
MVCC(多版本并发控制)
mysql
鱼儿也有烦恼6 小时前
MySQL最新安装、连接、卸载教程(Windows下)
mysql·navicat
异常君9 小时前
MySQL 查询优化:JOIN 操作背后的性能代价与更优选择
后端·mysql·性能优化
LG.YDX9 小时前
MySQL:13.用户管理
数据库·mysql