事务
1、什么是数据库事务?
事务(Transaction)是访问和更新数据库的程序执行单元,是逻辑上的一组操作,要么都执行,要么都不执行。如果任意一个操作失败,那么整组操作即为失败,会回到操作前状态或者是上一个节点。
因此,事务是保持 逻辑数据一致性 和 可恢复性 的重要利器。而锁是实现事务的关键,可以保证事务的完整性和并发性。
事务控制语句:
- BEGIN 或 START TRANSACTION 显式地开启一个事务;
- COMMIT 也可以使用 COMMIT WORK,不过二者是等价的。COMMIT 会提交事务,并使已对数据库进行的所有修改成为永久性的;
- ROLLBACK 也可以使用 ROLLBACK WORK,不过二者是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
- SAVEPOINT identifier,SAVEPOINT 允许在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT;
- RELEASE SAVEPOINT identifier 删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
- ROLLBACK TO identifier 把事务回滚到标记点;
- SET TRANSACTION 用来设置事务的隔离级别。InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。
2、介绍一下事务具有的四个特征?
事务的四大特征 ->ACID:
- 原子性:事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做;
- 一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态;
- 隔离性:一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性:也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
3、说一下MySQL 的四种隔离级别? ⚡
事务的隔离级别:多个客户端操作时,各个客户端的事务之间应该是隔离的,不同的事务之间不该互相影响,而如果多个事务操作同一批数据时,则需要设置不同的隔离级别,否则就会产生问题。
- 读未提交:最低的隔离级别,一个事务可以读到另一个事务未提交的结果,所有的并发事务问题都会发生。
- 读已提交:只有在事务提交后,其更新结果才会被其他事务看见,可以解决 脏读问题,但是不可重复读或幻读仍有可能发生。Oracle 默认采用的是该隔离级别。
- 可重复读:在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交,除非数据是被本身事务自己所修改。可以解决 脏读、不可重复读,但幻读仍有可能发生。MySQL 默认采用可重复读隔离级别。
- 可串行化:事务 串行化执行,隔离级别最高,完全服从 ACID,牺牲了系统的并发性,也就是说,所有事务依次逐个执行,所以可以解决并发事务的所有问题。
隔离级别 |
名称 |
会引发的问题 |
数据库默认隔离级别 |
Read Uncommitted |
读未提交 |
脏读、不可重复读、幻读 |
|
Read Committed |
读已提交 |
不可重复读、幻读 |
Oracle / SQL Server |
Repeatable Read |
可重复读 |
幻读 |
MySQL |
Serializable |
可串行化 |
无 |
4、什么是脏读?幻读?不可重复读?丢失更新?(事务的并发问题)
- 脏读:一个事务读取了另一个事务未提交的数据。
- 不可重复读: 就是在一个事务范围内,两次相同的查询会返回两个不同的数据,这是因为在此间隔内有其他事务对数据进行了修改。
- 幻读: 幻读是指当事务 不是独立执行时 发生的一种现象,例如事务A对表中的数据进行了修改,这种修改涉及到表中的全部数据行,同时,另一个事务B也修改这个表中的数据,这种修改是向表中 插入一行新数据。那么,事务A的用户发现表中还有没有修改的数据行,就好像发生了幻觉一样。
- 丢失更新(脏写):
两个事务同时读取同一条记录,事务 A 先修改记录,事务 B 也修改记录(B 是不知道 A 修改过),当 B 提交数据后, 其修改结果覆盖了 A 的修改结果,导致事务 A 更新丢失。
5、事务的实现原理?
- 事务是基于重做日志文件(redo log)和回滚日志(undo log)实现的。
- 每提交一个事务必须先将该事务的所有日志写入到重做日志文件(redo log)进行持久化,数据库就可以通过重做日志来保证事务的持久性。
- 每当有修改事务时,还会产生 undo log,如果需要回滚,则根据 undo log 的反向语句进行逻辑操作,比如 insert 一条记录就 delete 一条记录,回滚日志主要实现数据库的原子性。
6、MySql的事务日志介绍一下?
InnoDB 存储引擎提供了两种事务日志:redo log(重做日志)和 undo log(回滚日志)
- redo log 用于保证事务持久性;
- undo log 用于保证事务原子性和一致性;
- redo log
redo log 是 innodb 引擎特有的,常译作重做日志。它是物理日志,记录了在某个数据页上进行了什么修改。这个数据页对应着数据硬盘上的实际存储地址。InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB。redo log是一块循环写的空间,从头写到尾以后,如果需要继续写,会覆盖之前写的日志。
- undo log
undo log 属于逻辑日志,根据每行操作进行记录,记录了 SQL 执行相关的信息,用来回滚行记录到某个版本。
当事务对数据库进行修改时,InnoDB 会先记录对应的 undo log,如果事务执行失败或调用了 rollback 导致事务回滚,InnoDB 会根据 undo log 的内容做与之前相反的操作:
- 对于每个 insert,回滚时会执行 delete
- 对于每个 delete,回滚时会执行 insert
- 对于每个 update,回滚时会执行一个相反的 update,把数据修改回去
7、什么是MySQL的 binlog?
- MySQL的 binlog 是记录所有数据库表结构变更(例如 CREATE、ALTER TABLE)以及表数据修改(INSERT、UPDATE、DELETE)的二进制日志。binlog 不会记录 SELECT 和 SHOW 这类操作,因为这类操作对数据本身并没有修改,但你可以通过查询通用日志来查看 MySQL 执行过的所有语句;
- binlog 的主要目的是复制和恢复;
- binlog 是MySQL server 层的实现,与引擎无关,这就意味着不管是使用 Innodb 引擎,还是使用 MyISAM 引擎都可以使用。
- binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如 给 ID=2 这一行的 c 字段加 1。binlog 是可以追加写入的。“追加写” 是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
- binlog 通常有三种格式,一种是 Row 格式(将每一行的修改记录到记录到binlog中),一种是 Statement 格式(相当于是记录了操作的SQL语句),另一种则是 Mixed 模式,即混合了Row 格式和 Statement 格式。
8、讲一讲binlog和redo log的区别? ⚡
- 作用不同:redo log 是用于故障恢复,保证 MySQL 宕机也不会影响持久性;binlog 是用于基于时间点恢复数据和主从复制。
- 层次不同:redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
- 内容不同:redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志, 记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”
- 写入不同:redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
9、一条更新语句怎么执行(两阶段提交)? ⚡
以这条更新语句为例:
update T set c=c+1 where ID=2;
流程如下:
- 执行引擎将这行新数据更新到内存中(若对应的行在内存直接返回,否则先去磁盘读取再返回)
- 执行器执行更新操作并将其记录到 redo log buffer 里,此时 redo log(重做日志) 处于 prepare 状态,代表执行完成随时可以提交事务[时刻A]。
- 执行器生成这个操作的 binlog 并把 binlog 写入磁盘[时刻B]。
- 执行器调用引擎事务提交接口,引擎把刚写入的redolog改为commit状态,更新完成。
总结:redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致,也有利于主从复制,更好的保持主从数据的一致性 。
故障恢复数据:
- 如果在时刻 A 发生了崩溃(crash),由于此时 binlog 还没写,redo log 也没提交,所以数据恢复的时候这个事务会回滚
- 如果在时刻 B 发生了崩溃,redo log 和 binlog 有一个共同的数据字段叫 XID,崩溃恢复的时候,会按顺序扫描 redo log:
- 如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,说明 binlog 也已经记录完整,直接从 redo log 恢复数据
- 如果 redo log 里面的事务只有 prepare,就根据 XID 去 binlog 中判断对应的事务是否存在并完整,如果完整可以从 binlog 恢复 redo log 的信息,进而恢复数据,提交事务。
10、如何实现事务的 ACID 特性?
DBMS 采用 日志 来保证事务的 原子性、一致性 和 持久性。日志记录了事务对数据库所做的更新,如果某个事务在执行过程中发生错误,就可以根据日志,撤销事务对数据库已做的更新,使数据库退回到执行事务前的初始状态。
- redo log 用于保证事务持久性(记录事务开启后对数据的修改)
- undo log 用于保证事务原子性(记录事务开始前的老版本数据,可以保证原子操作)
- DBMS 采用 锁机制 来实现事务的隔离性。当多个事务同时更新数据库中相同的数据时,只允许 持有锁的事务 能更新该数据,其他事务必须等待,直到前一个事务释放了锁,其他事务才有机会更新该数据。