什么是事务
数据库事务(transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。
为什么要用事务
因为很多时候我们做一件事,需要同时修改数据库的多个数据项,为了保持事件的完整性,我们使用事务来保证多个数据的一致性
事务的原则
原子性(atomicity)
- 指一个事务中,要么全部成功,要么全部失败。
- MySQL 中由 Undo Log 日志来保证原子性
一致性(consistency)
- 指数据的可见性,中间状态的数据对外部不可见,只有最初状态和最终状态的数据对外可见。事务开始和结束后,数据库的完整性不会被破坏。
- 通过原子性,隔离性,持久性来保证一致性。
隔离性(isolation)
- 不同事务之间互不影响,四种隔离级别为READ-UNCOMMITTED(读未提交)、READ COMMITTED(读已提交)、REPEATABLE READ(可重复读)、SERIALIZABLE(串行化)。
- 利用锁和MVCC来保证隔离性
-
(一个事务)写操作对(另一个事务)写操作的影响:
锁机制保证隔离性
-
(一个事务)写操作对(另一个事务)读操作的影响
MVCC保证隔离性
-
持久性(durability)
- 指一旦事务提交成功,则其所作的修改就会永久保存到数据库中,即使系统崩溃,修改的数据也不会丢失。
- 由 Redo Log和Bin Log来保证持久性
事务的三级封锁协议
-
一级封锁协议
- 事务T中如果对数据R有写操作,必须在这个事务中对R的第一次读操作前对它加X(写锁,直到事务结束才释放。事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK)
- 使用一级封锁协议可以解决丢失更新问题,但是不能解决可重复读和脏读
-
二级封锁协议
- 一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后方可释放S锁
- 二级封锁协议可以在一级封锁协议的基础上进一步解决脏读的问题
-
三级封锁协议
- 一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放
- 三级封锁协议可以在二级封锁协议的基础上进一步解决可重复读读问题
-
总结
三级锁操作一个比一个厉害(满足高级锁则一定满足低级锁)。但有个非常致命的地方,一级锁协议就要在第一次读加X锁,直到事务结束。几乎就要在整个事务加写锁了,效率非常低。三级封锁协议只是一个理论上的东西,实际数据库常用另一套方法来解决事务并发问题。
数据的读取特性
类型 | 特点 |
---|---|
脏读 | A事务读取B事务尚未提交的更改数据,并在这个数据基础上操作 如果B事务回滚,那么A事务读到的数据根本不是合法的,称为脏读 |
不可重复读 | A事务读取了B事务已经提交的更改(或删除)数据 比如A事务第一次读取数据,然后B事务更改该数据并提交,A事务再次读取数据,两次读取的数据不一致 |
幻读 | A事务读取了B事务已经提交的新增数据。注意和不可重复读的区别,这里是新增,不可重复读是更改(或删除) 这两种情况对策是不一样的,对于不可重复读,只需要采取行级锁防止该记录数据被更改或删除,然而对于幻读必须加表级锁,防止在这个表中新增一条数据 SQL标准这么定义幻读,在两个连续的查找之间一个并发的修改事务修改了查询的数据集,导致这两个查询返回了不同的结果 |
丢失更新 | A事务撤销时,把已提交的B事务的数据覆盖掉 |
覆盖更新 | A事务提交时,把已提交的B事务的数据覆盖掉 |
MySQL支持的事务隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ-UNCOMMITTED | ✅ | ✅ | ✅ |
READ-COMMITTED | ❎ | ✅ | ✅ |
REPEATABLE-READ | ❎ | ❎ | ✅ |
SERIALIZABLE | ❎ | ❎ | ❎ |
从上往下,隔离级别越高,效率则越低
-
READ-UNCOMMITTED(未提交读)
- 一个事务还没提交时,它做的变更就能被别的事务看到。
- 一句总结:读取数据一致性在最低级别,只能保证不读物理上损坏的数据,会脏读,会不可重复读,会幻读
- (事务不完全隔离,可以读取未提交的数据,称为脏读,会导致很多问题,从性能上来说也不会比其他级别好太多,很少使用)
-
READ-COMMITTED(已提交读)
- 一个事务从开始到提交前,所作的任何修改对其他事务不可见
- 一句总结:读取数据一致性在语句级别,不会脏读,会不可重复读,会幻读
- 仅能读取到已提交的记录,这种隔离级别下,会存在幻读现象,所谓幻读是指在同一个事务中,多次执行同一个查询,返回的记录不完全相同的现象。幻读产生的根本原因是,在RC隔离级别下,每条语句都会读取已提交事务的更新,若两次查询之间有其他事务提交,则会导致两次查询结果不一致。虽然如此,读提交隔离级别在生产环境中使用很广泛。
- 特点
- 互联网应用考虑使用该级别,并发性更好
- 半一致性读
- 此时binlog必须使用row格式避免commit先后的问题导致从库数据和主库不一致
-
REPEATABLE-READ(可重复读)
- 一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
- 一句总结:读取数据一致性在事务级别,不会脏读,不会不可重复读,会幻读
- 但是MySQL InnoDB引擎通过MVCC或者next-key(间隙锁)的方式解决了幻读
-
SERIALIZABLE(串行化)
- 最高的隔离级别,强制事务串行执行,不允许并发,避免幻读,对于同一行记录,“写”会加“写锁”,“读”会加“读锁”,可能导致大量的超时和锁竞争,只有再非常需要一致性可以接受没有并发的情况下才使用
- 一句总结:读取数据一致性在最高级别,事务级别,不会脏读,不会不可重复读,不会幻读
- 在串行化隔离模式下,消除了脏读,幻象,但事务并发度急剧下降,事务的隔离级别与事务的并发度成反比,隔离级别越高,事务的并发度越低。实际生产环境下,dba会在并发和满足业务需求之间作权衡,选择合适的隔离级别。
查看当前事务隔离级别
mysql> show variables like 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name | Value |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.00 sec)
设置事务级别
当前会话修改
# 设置读未提交
set session transaction isolation level read uncommitted;
# 设置读提交
set session transaction isolation level read committed;
# 设置可重复读
set session transaction isolation level repeatable read;
# 设置串行化
set session transaction isolation level serializable;
全局修改
[mysqld]
transaction-isolation = READ-COMMITTED
事务语法
# 开启事务 (注意:这不是一个事务的起点)
# 一致性视图是在第执行第一个快照读语句时创建的
begin/start transaction
# 开启事务 (如果想马上启动一个事务)
# 一致性视图是在执行 start transaction with consistent snapshot 时创建的
start transaction with consistent snapshot
# 提交并开启下个事务
commit work and chain
系列文章