数据库事务

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

原子性

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

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

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

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为每条变更的数据独立记录一条日志

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李昂的数字之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值