文章目录
前言
事务是由MySQL的引擎来实现的,通过show engines命令查看MySQL存储引擎类别,观察只有InnoDB存储引擎支持事务。
一、什么是事务?
1.1 事务概念
事务(Transaction)是一系列的数据库操作,这些操作要么全部成功执行,要么全部回滚(即全部失败,回到操作前的状态),这样确保数据库的数据在并发访问的情况下保持一致性和完整性。
1.2 事务的四大特性
一个完整的事务必须具备四个条件,这四个条件我们称为ACID,ACID特性确保了一个完整的事务在数据库中的正确执行和数据的一致性。ACID这四个字母,每个字母代表了一个特性。
原子性(Atomicity): 事务(transaction)被视为一个不可分割的单元,要么全部执行成功,要么全部不执行。如果在事务的执行过程中发生错误,那么事务会被回滚到开始前的状态,保持数据的一致性。
一致性(Consistency): 事务在执行前和执行后,数据库的数据应该处于一致的状态。这意味着事务在执行时,必须遵守一些预定的规则或约束,以保持数据的正确性。
隔离性(Isolation): 事务之间是相互隔离的,一个事务的执行不会被其他事务干扰。这意味着并发执行的事务不会相互影响,可以保证数据的完整性和正确性。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。
持久性(Durability): 一旦事务成功提交,对数据库的修改将永久保存,即使在系统故障或崩溃后,数据也不会丢失。持久性确保数据的持久性存储,保障数据的可靠性。
1.3 事务的隔离级别
事务隔离级别的主要目的是解决数据库并发访问时可能出现的以下问题:
-
脏读(Dirty Read): 一个事务读取了另一个事务尚未提交的数据,如果另一个事务回滚,则读取到的数据实际上是无效的,这种现象就被称为脏读。
-
不可重复读(Non-repeatable Read): 在同一个事务内,由于其他事务对同一行数据进行了修改(更新或删除),导致在多次读取该行数据时,得到的结果不一致。这意味着同一个事务内的两次读取操作得到了不同的数据值。重点在于其他事务的更新(update)和删除(delete)。
-
幻读(Phantom Read): 在同一个事务内,由于其他事务对数据进行了插入(insert)或删除(delete)操作,导致多次查询时得到的结果集不一致。幻读主要发生在范围查询中,即同一个事务内的两次查询得到了不同数量的结果行。重点在于其他事务的插入(insert)和删除(delete)。
为解决以上问题可以通过设置不同的事务隔离级别,可以控制这些问题的发生概率。MySQL支持以下四种事务隔离级别:
-
读未提交(Read uncommitted): 最低级别的隔离,允许一个事务读取另一个事务尚未提交的数据。这可能导致脏读、不可重复读和幻读问题。
-
读已提交(Read Committed): 允许一个事务读取另一个事务已提交的数据,解决了脏读问题。但是仍可能出现不可重复读和幻读问题。
-
可重复读(Repeatable Read): 这是MySQL InnoDB引擎的默认隔离级别,保证在一个事务内多次读取同一数据时,结果始终一致。解决了脏读和不可重复读问题。但是仍可能出现幻读问题。
-
串行化(Serializable): 最高级别的隔离,确保同时只有一个事务能够访问数据,解决了脏读、不可重复读和幻读问题。但是串行化会导致并发性能大幅下降,因为多个事务无法同时访问数据。
隔离级别以及可能产生的读现象:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
串行化 | 不会发生 | 不会发生 | 不会发生 |
可重复读 | 不会发生 | 不会发生 | 可能发生 |
读已提交 | 不会发生 | 可能发生 | 可能发生 |
读未提交 | 可能发生 | 可能发生 | 可能发生 |
应结合实际的业务需求,可以选择适合的隔离级别来处理数据并发操作。隔离级别越高,性能可能越差,因为它需要锁定更多的资源以保证数据的一致性。因此,在设计事务时,需要综合考虑业务的要求和性能的影响。
二、实现原理
2.1 事务ACID特性是如何保证的?
- A原子性:靠Undo log来保证(异常或执行失败后进行回滚)。
- C一致性:事务的最终目的,即需要数据库层面保证,又需要应用层面进行保证,并且MySQL底层通过两阶段提交事务保证了事务持久化时的一致性。
- I隔离性:事务间的读写靠MySQL的锁机制 + MVCC机制(快照读、当前读)来保证隔离性。
- D持久性:靠Redo log来保证(保证当MySQL宕机或停电后,可以通过Redo log最终将数据保存至磁盘中)。
2.2 Undo Log日志
2.2.1 什么是Undo Log?
Undo Log的字面意思就是撤销操作的日志,指的是使MySQL中的数据回到某个状态。
在MySQL数据库中,事务开始之前MySQL会将待修改的记录保存到Undo Log中,如果数据库崩溃或者事务需要回滚时,MySQL可以通过利用Undo Log日志,将数据库中的数据回滚到之前的状态。
MySQL新增、修改和删除数据时,在事务开始前就会将信息写入Undo Log中。事务提交时并不会立刻删除Undo Log, InnoDB存储引擎会将事务对应的Undo Log放入待删除列表中,之后会通过后台的purge thread对待删除的列表进行删除处理。值得注意的是:Undo Log是一种逻辑日志, 记录的是一个变化过程。比如,MySQL执行一个delete操作,Undo Log就会记录一个insert操作;MySQL执行一个insert操作,Undo Log就会记录一个delete操作;MySQL执行一个update操作,Undo Log就会记录一个相反的update操作。
Undo Log以段的方式来管理和记录日志信息,在InnoDB存储引擎的数据文件中,包含了一种叫做rollback segment的回滚段,其内部包含了1024个undo log senment。
2.2.2 Undo Log作用
Undo Log对于MySQL实现事务来说起着至关重要的作用,它实现了事务的原子性和多版本并发控制,也就是我们经常说的MVCC。
实现事务的原子性
- Undo Log能够实现MySQL事务的原子性,在事务的处理过程中,如果MySQL出现了错误或者用户手动执行了事务的回滚操作(执行了rollback操作),MySQL可以利用Undo Log日志将数据库中的数据恢复到之前的状态。
实现MVCC机制
- Undo Log在MySQL的InnoDB存储引擎中实现了多版本并发控制(MVCC)机制。事务未提交前,Undo Log保存了未提交之前的版本数据,Undo Log中的数据可以作为旧版本数据的副本或者快照以便其他并发事务进行读取操作。
事务A手动开启事务后,对goods数据表中id为1的数据进行更新操作,首先会把更新命中的数据写入到Undo Buffer中。在事务A未提交之前,此时,事务B手动开启事务,对goods数据表中的id为1的数据进行查询操作,此时的事务B会读取Undo Log中的数据并返回给客户端,这就是MySQL中的MVCC机制。
可以在MySQL中通过下面的命令来查看控制Undo Log日志的参数。
show variables like '%innodb_undo%';
2.3 Redo Log日志
2.3.1 什么是Redo Log?
顾名思义Redo Log的字面意思就是重做日志,指的是在数据库出现意外情况时能够对重新执行某种操作。在MySQL中事务中修改的任何数据,都会将最新的数据写入Redo Log中进行备份。
在MySQL中,随着事务操作的执行,就会产生Redo Log日志,在事务提交时会产生Redo Log并将其写入Redo Buffer,Redo Buffer也并不是随着事务的提交就会被立刻写入到磁盘中,而是等事务操作的脏页写入到磁盘之后,Redo Log的使命也就完成了,此时,Redo Log日志占用的空间可以重新利用,会被后续产生的Redo Log日志覆盖。
2.3.2 Redo Log的原理
Redo Log能够实现事务的持久性,防止在发生故障的时间点,有脏页未写入表的 ibd 文件中,在重启MySQL服务的时候,根据 Redo Log进行重做,从而将未提交的事务进行持久化。这个过程可以简化为下图所示。
2.3.3 Redo Log的写机制
Redo Log文件的内容是以顺序循环的方式写入文件的,写满时就会回到第一个文件,进行覆盖写。
- Write Pos 是当前记录的位置,一边写一边后移,写到最后一个文件末尾后就回到 0 号文件开头;
- CheckPoint是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数 据文件;
Write Pos 和 CheckPoint之间还空着的部分,可以用来记录新的操作。如果 Write Pos 追上 CheckPoint,表示已经写满,此时就需要向后移动CheckPoint来擦除数据。
每个InnoDB存储引擎至少有1个重做日志文件组(group),每个文件组至少有2个重做日志文件,默认为ib_logfile0和ib_logfile1 。
可以在MySQL中通过如下命令来查看控制Redo Log的参数。
show variables like '%innodb_log%';
Redo Log写入机制
在Redo Log日志信息从Redo Buffer持久化到Redo Log时,具体的持久化策略可以通过innodb_flush_log_at_trx_commit 参数进行设置,具体策略如下所示。
- 0:每秒提交 Redo buffer ->OS cache -> flush cache to disk,可能丢失一秒内的事务数据。由后台Master线程每隔 1秒执行一次操作。
- 1(默认值):每次事务提交执行 Redo Buffer -> OS cache -> flush cache to disk,这种方式最安全,性能最差。
- 2:每次事务提交执行 Redo Buffer -> OS cache,然后由后台Master线程再每隔1秒执行OS cache -> flush cache to disk 的操作。
一般建议选择取值2,因为 MySQL 挂了数据没有损失,整个服务器挂了才会损失1秒的事务提交数据。
2.4 Binlog日志
2.4.1 什么是Binlog?
Binlog记录所有MySQL数据库表结构变更以及表数据修改的二进制日志,不会记录select和show这类查询操作的日志。Binlog日志是以事件形式记录,还包含语句所执行的消耗时间。开启Binlog日志有以下两个最重要的使用场景:
- 主从复制: 在主库中开启Binlog功能,这样主库就可以把Binlog传递给从库,从库拿到Binlog后实现数据恢复达到主从数据一致性。
- 数据恢复: 通过mysqlbinlog等工具来恢复数据。
2.4.2 Binlog文件记录模式
Binlog文件记录模式有STATEMENT、ROW和MIXED三种,具体含义如下。
ROW模式
ROW(row-based replication, RBR):日志中会记录每一行数据被修改的情况,然后在slave端对相同的数据进行修改。
-
优点:能清楚记录每一个行数据的修改细节,能完全实现主从数据同步和数据的恢复。
-
缺点:批量操作,会产生大量的日志,尤其是alter table会让日志暴涨。
STATMENT模式
STATMENT(statement-based replication, SBR):每一条被修改数据的SQL都会记录到master的Binlog中,slave在复制的时候SQL进程会解析成和原来master端执行过的相同的SQL再次执行。简称SQL语句复制。
-
优点:日志量小,减少磁盘IO,提升存储和恢复速度
-
缺点:在某些情况下会导致主从数据不一致,比如last_insert_id()、now()等函数。
MIXED模式
MIXED(mixed-based replication, MBR):以上两种模式的混合使用,一般会使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog,MySQL会根据执行的SQL语句选择写入模式 。
鸣谢:相关日志部门转载了up主冰河世纪,以此鸣谢!
2.4 MVCC 原理
todo