MySQL事务
事务(transaction)
一、事务特性(ACID)
-
原子性(Atomicity):指事务是一个不可分割的最小工作单位,事务中的操作只有都发生和都不发生两种情况。
-
一致性(Atomicity):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
-
隔离性(Atomicity):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
-
持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
二、事务分类
-
隐式事务:该事务没有明显的开启和结束标记,它们都具有自动提交事务的功能;insert、update、delete都是隐式事务;
-
显示事务:该事务具有明显的开启和结束标记;使用显示事务的前提是要先把自动提交事务功能给禁用;禁用自动提交功能设置方法;
-- 查看是否自动提交方法 (ON 自动提交 OFF不是自动提交 需要手动 commit;) show variables like 'autocommit'; SELECT @@autocommit; -- 关闭自动提交事务,开启自动提交事务 set autocommit = off; set autocommit = on; set autocommit = 0; set autocommit = 1; -- 开启事务 start transaction; -- 提交事务 commit; -- 回滚事务 rollback;
三、隔离级别
数据库执行多个事务同时执行时,可能会脏读、幻读、不可重复读 等问题,隔离级别可以解决这些问题。隔离越高,效率越低,并发越低,安全性越高。
隔离级别 | 解释 | 优缺点 |
---|---|---|
读未提交(read uncommitted,RU) | 事务还未提交,做的变更就能被其它事务看见 | 会出现脏读、不可重复读和幻读问题; |
读已提交(read committed,RC) | 事务在提交之前,做的变更都无法被其它事务看见,但是事务本身可以看到数据库的变更 | 可以避免脏读,会出现不可重复度和幻读问题; |
可重复度(repeatable read,RR) | 确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新(update)。 | 可以避免脏读和不可重复读,会出现幻读 |
串行化(serializable) | 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。 | 所有并发问题都可避免,但性能十分低下 |
oracle默认的事务隔离级别是:读已提交(read committed)。
mysql的默认事务隔离级别是:可重复读(repeatable read)。
隔离级别:
-
读未提交:
事务还未提交,做的变更就能被其它事务看见;
事务B修改了值,事务A可以直接看见;
-
读已提交:
事务在提交之前,做的变更都无法被其它事务看见,但是事务本身可以看到数据库的变更;
事务B做了修改以后,事务A无法立刻看见,事务B提交以后,事务A就可以看到数据库变更了;
-
可重复读:
事务总是只能看到在启动的那个时刻,数据库的状态,即在事务未提交之前,自己做的变更别的事务看不见,数据库中的变更自己也看不见。
事务 B 提交之前,A 看不见 B 做的变更,事务 B 提交以后,数据库的值虽然发生了变更,但是事务 A 还未提交,A 还是只能看到自己在启动时刻,数据库的值(可以理解为数据库有版本,只能看见历史版本)。
-
串行化:
串行化是指,事务在操作某一行记录时会加锁,“读”会加“读锁”,“写”会加“写锁”。
事务 A 在启动后,先执行了 1 条查询语句,对这行数据加了“读锁”,这个“读锁”要等事务 A 提交以后,才会释放。因此事务 B 执行查询语句会处于等待锁释放状态。这时候事务 B 会一直等待。事务 A 提交之后,“读锁”释放,事务 B 获取该行记录的“读锁”,并更新了数据(又加了一个“写锁”),事务 B 未提交之前,A 再去查数据也需要等待锁释放,事务 B 提交以后,锁释放,A 拿到锁,开始查询数据。
并发问题:
-
脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据;
-
不可重复读:事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据做了更新并提交,导致事务A多次读取同一数据时,结果不一致;
-
幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为了ABCD等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像法神了幻觉一样,这就叫幻读。
四、事务隔离的实现原理
读未提交:
在该隔离级别下,直接返回记录上的最新值;
读已提交:
在该隔离级别下,数据库会在执行SQL语句时创建一个视图;
可重复读 :
在该隔离级别下,事务启动时数据库会创建一个视图,整个事务执行期间都会用这个视图;
串行化:
在该隔离级别下,直接用加锁的方式来避免并行访问;
五、事务实现
redo log
重做日志,是用来实现事务的持久性的。重做日志又分为2部分,重做日志缓冲(redo log buffer) 和 重做日志文件(redo log),重做日志缓冲是用在内存中,重做日志文件是在磁盘中的。
mysql每次对数据的更新都不会实时的写入到磁盘中的,而是先将数据放到缓冲池(buffer pool)中,然后再通过另一个线程将缓冲池的数据同步到磁盘中。 如果在数据同步的过程中,忽然服务器宕机了,那么缓冲池的数据则会丢失,从而导致数据丢失。因此redo log就是用来记录已经提交了的事务的修改数据,并且会将redo log日志持久化到磁盘,就算是宕机了,重启后还是会通过读取redo log来恢复数据。
为什么都是写入磁盘,反而要引入redo log呢?因为数据库存储的数据在磁盘上不是顺序的,每次找到数据需要指针寻址,比较耗时,而redo log存储的是顺序的,读取很快,只需要每次往下读取就可以了。
因此可以了解到redo log是用来确保数据的持久性的。
undo log
回滚日志。用于记录数据被修改之前的数据信息。主要记录的是数据的一系列变化的过程,当有错误/异常的情况或rollback发生时,可以根据这些日志进行回滚处理。将数据回滚至修改前的状态。
undo log 是用来回滚数据的,从而保障未提交事务的原子性的
MVCC
多版本并发控制机制,是通过在每行数据增加2个隐藏字段trx_id 和 roll_pointer 记录版本号,然后与undo log日志进行串联起来形成一个历史记录版本链,在可重复读的隔离级别下,当前事务下查询sql会生成当前事务的一致性视图read-view,sql里的任何查询结果需要从undo log日志的最新数据开始与read-view做对比,从而得到最终输出的结果。
隔离性就是通过MVCC来保障的。
MVCC机制就是通过undo log日志记录版本链与read-view对比机制,使不同的事务会根据数据版本链对比规则读取同一条数据在版本链上的不同版本的数据。
到了这里,知道了事务的原子性由undo log日志保障,持久性由redo log日志保障,隔离性由MVCC机制保障。