MySQL的事务(Transaction
)
知识来源:
- 《MySQL是怎样运行的》--- 小孩子4919
程序员所做的工作,就是将现实世界中的事物映射到计算机,并且将大量工作交给计算机完成。而事务是现实世界中的状态转移能映射到数据库中的一个或多个操作。而在计算机执行过程中,并不能保证这些操作能成功反应现实世界的变化,比如:转钱分为发起人扣钱,接收者加钱这两个大操作,如果钱扣了,然后数据库崩了,接收者的钱没多,这时候就出问题了。所以需要事务遵守四个规则,ACID。TIPS:只有InnoDB引擎和NDB引擎(这个基本没啥人用)支持事务!!!!!!!
事务的四大特性--ACID
ACID
是Atomicity
、Isolation
、Consistency
和Durability
首字母的缩写。以下就拿张三给李四转账,来说明这四种规则。
-
原子性(
Atomicity
):事务中的一个或多个操作应该全部成功或者全部失败,不能有部分成功这种中间状态。- 比如:张三钱扣了,那么李四的钱就需要增加相应数额。如果钱扣了,李四的钱没加,那么就需要给张三的钱加回去(这叫做回滚 ,
rollback
)。
- 比如:张三钱扣了,那么李四的钱就需要增加相应数额。如果钱扣了,李四的钱没加,那么就需要给张三的钱加回去(这叫做回滚 ,
-
隔离性(
Isolation
):事务内的操作不能对其他事务的操作产生影响。- 图中有两个事务,为T1、T2。T1中的张三扣钱操作执行钱,T2就先把张三钱的数额读出来了。最终导致,张三只扣了5块钱,而李四却增加了10块钱。最正确的应该是T1读了A,那么此时T2就不能读这个值。
-
持久性(
Durability
):如果状态转移成功,那么就需要将成功的状态保持下来。- 比如:张三的钱转给李四后。张三和李四都看到了转账的结果,开心地去吃饭了。结果此时服务器一挂,再恢复之后,这笔转账操作没保存下来。张三的钱又回去了。李四拍着胸脯说要结账,结果发现自己手机里分币没有。。。着实尴尬
-
一致性(
Consistency
):数据库中的数据在状态转移前后应该都符合现实世界的约束。- 比如:张三把钱转给李四的前后,张三的钱和李四的钱加起来应该保持一致。TIPS :前面的原子性、隔离性和持久性都强调了这点,所以说,原子性、隔离性、持久性是实现一致性的手段。而一致性是事务的目标,保证了一致性,就保证了现实世界和数据之间映射的成功。
事务的五种状态
事务是实现了ACID规则的一系列数据操作。根据这些操作执行的程度不同,划分出了事务的不同阶段。事务的两个最终状态,对应着原子性的全部成功或全部失败。

-
三个中间状态
-
活动的(
Active
):事务的一系列操作开始执行或执行了部分 -
部分提交的(
Partially Committed
):事务的最后一个操作执行完成,但此时还没将结果持久化到磁盘中,还未保证持久性 -
失败的(
Failed
):处于Active
或Partially Committed
阶段时,突然出现错误(可能是服务器断电、操作系统崩溃、MySQL崩溃、程序崩溃。。。)或者人为终止了事务
-
-
两个最终状态
-
提交的(
Committed
):将Partially Committed
阶段的结果持久化到磁盘中。对应原子性的全部成功。 -
中止的(
Aborted
):将处于Failed
阶段的事务的操作全部回滚(rollback
),彻底回到所有操作都没执行的状态。对应原子性的全部失败。
-
事务的并发问题
脏写(Dirty Write)
-
在两个事务中修改同一条数据,但是一个事务的回滚导致另一个事务的修改数据失败,尽管另一个事务已经提交了。
-
例如:原数据name为"wangwu",事务A将数据name修改为"zhangsan",事务B在事务A提交前开启了,并且修改成"lisi",事务A提交了,但是事务B回滚了,这时候读到的数据却是"wangwu",事务B的修改对事务A的修改造成了影响。则事务A的第一次修改产生了脏写。
脏读(Dirty Read)
-
一个事务读取一条数据,但是由于其他事务的修改或者删除(未提交) ,导致数据读取出来和数据库持久化的数据不一致。
-
例如:事务A第一次读取数据name为"wangwu",但是由于事务B将name修改为"lisi",导致事务A第二次读取为"lisi",随后事务B回滚,事务A第三次读取name为"wangwu"。则事务A的第二次读取发生了脏读。
不可重复读(Non-Repeatable Read)
-
一个事务中多次读取同一条数据,但是由于其他事务的修改或删除(提交后),导致该事务中读取到的数据不一致。
-
例如:事务A读取name字段为"wangwu",但是事务B修改为"lisi"并且提交,事务A第二次读取name字段为"lisi"。则事务A的第二次读取发生了不可重复读。
幻读(Phantom Read)
-
一个事务多次读取符合条件的数据,但是由于其他事务的插入操作(提交后),导致该事务中读取到的数据多出来几条。
-
例如:事务A第一次读取user表的所有数据有一条数据,事务B插入了一条数据,导致事务A第二次读取出来的数据变成了两条。事务A的第二次读取就形成了幻读。
数据库的四种隔离级别
行表示隔离级别,列表示并发问题。
√表示在该种隔离级别下会发生这种并发问题
x表示在该种隔离级别下不会发生这种并发问题
|------------------------|----|----|-------|----|
| | 脏写 | 脏读 | 不可重复读 | 幻读 |
| 读未提交(Read Uncommitted) | × | √ | √ | √ |
| 读已提交(Read Committed) | × | × | √ | √ |
| 可重复读(Repeatable Read) | × | × | × | √ |
| 串行化(Serializable) | × | × | × | × |
MySQL的四种隔离级别
MySQL的默认隔离级别是可重复读。
实现隔离级别的方式是MVCC和加锁。
-
读未提交
-
读已提交,事务中的每个查询语句都会生成一个Read View
-
可重复读,每个事务开启时生成一个Read View
-
串行化
sql
-- 查看事务隔离级别
select @@transaction_isolation;
-- 修改事务隔离级别(设置内存中,没写到配置文件,重启失效)
-- global,即全局设置,所有会话生效,但是设置隔离级别的当前会话不受影响
-- session,即会话设置,当前会话立即生效,但是对其他会话不生效
set [global/session] transaction_isolation = ['read-uncommitted'/'read-committed'
/'repeatable-read'/'serializable'];