事务
事务( Transaction
,简写为 tx
),由一步或几步数据库操作(DML
语句)序列组成的逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行(一组原子性的 SQL
査询)
事务的 4 个特性(ACID 性)
原子性(Atomicity):
- 事务是数据库的逻辑工作单位,事务中包含的所有操作要么全部提交成功,要么全部失败回滚
一致性(Consistency):
- 事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态
隔离性(Isolation):
- 一个事务所做的修改在最终提交以前,对其它事务是不可见的
持久性(Durability):
- 也称永久性,一个事务一旦提交,则其所做的修改就会永久保存到数据库中,接下来的其它操作或故障不会对其执行结果有任何影响
Mysql
事务的 ACID
是通过 InnoDB
日志和锁来保证:事务的隔离性是通过数据库锁的机制实现的,原子性和持久性通过 redo log(重做日志)
来实现,一致性通过 undo log
来实现。在修改表的数据时,先需要修改其内存拷贝,再把该修改行为记录到重做日志 Buffer(redo log buffer)
中,在事务结束后将重做日志写入磁盘,并通知文件系统刷新缓存中的数据到磁盘文件。
事务控制的命令
begin
或 start transaction
、commit
、rollback
事务自动提交
- MySQL 默认采用自动提交
(autocommit = 1)
模式,即如果不是显式地开始一个事务,则毎个查询都被当作一个事务执行提交操作 - 在执行
DDL
、DCL
操作之前会强制执行commit
提交当前的活动事务
事务并发问题
脏读
- 一个事务读到另一个事务未提交的更新数据
不可重复读
- 一个事务两次读同一行数据,期间有另一个事务提交了更新,导致这两次读到的数据不一样
幻读
- 一个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当再次读取该范围的记录时,会产生幻行
第一类丢失更新(回滚丢失)
- 撤消一个事务时,把其它事务已提交的更新的数据回滚掉了
第二类丢失更新(覆盖丢失)
- 提交一个事务时,把其它事务已提交的更新的数据覆盖了
事务隔离级别
-
应用程序设计者可以允许并发的事务运行在不同的隔离级别上,从而牺牲一些正确性来换取更大的吞吐量。
-
低的隔离级别提高了事务的并发程度,但是增加了数据库中含有不正确数据的风险。
-
有一些事务运行在最高的隔离级别,但是有另外一些事务却运行在较低的,可以访问未提交状态的隔离级别。
-
运行在较低隔离级别的事务可能产生无效的数据。应用程序设计者必须阻止之后运行在较高隔离级别的事务去访问这些无效的数据,继而传播错误。
读未提交
- 事务中的修改,即使没有提交,对其它事务也都是可见的
读已提交
- 一个事务开始时,只能“看见”其它已经提交的事务所做的修改
可重复读
- 同一个事务中多次读取同样记录的结果是一致的,当 A 事务修改了一条记录但未提交时,B 事务将不允许修改这条记录(会被阻塞,
innodb_lock_wait_timeout
,默认是 50s)
可串行化
- 事务顺序执行,事务在读取的毎一行数据上都加锁
InnoDB
默认的事务隔离级别是Repeatable Read(可重复读)
,并且通过间隙锁(next-key locking)
策略防止幻读的出现
事务隔离级别实现
在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在可重复读
隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。在读提交
隔离级别下,这个视图是在每个 SQL
语句开始执行的时候创建的。这里需要注意的是,读未提交
隔离级别下直接返回记录上的最新值,没有视图概念;而串行化
隔离级别下直接用加锁的方式来避免并行访问。
我们可以看到在不同的隔离级别下,数据库行为是有所不同的。Oracle
数据库的默认隔离级别其实就是“读提交”,因此对于一些从 Oracle
迁移到 MySQL
的应用,为保证数据库隔离级别的一致,你一定要记得将 MySQL
的隔离级别设置为“读提交”。
配置的方式是,将启动参数 transaction-isolation
的值设置成 READ-COMMITTED
。你可以用 show variables
来查看当前的值。
show variables like 'transaction_isolation';
在 MySQL
中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。
记忆图