事务的特性ACID
- 原子性(atomicity):事务的每一个写操作都是原子性的,要么一起成功,要么一起失败。要么全部提交成功,要么全部回滚
- 一致性(consistency):先举例子,银行转账
数据库总是从一个一致性的状态转换到另一个一致性的状态。当第3行执行结束后,开始执行第4行的时候,系统崩溃了,上一个账户还是没有减少200块,因为事务没有提交。回滚事务 - 隔离性(isolation): 一个事务在提交前,对另外一个事务是不可见的,隔离的。详见事务的隔离级别。
- 永久性(durability):一个事务提交后,就会永久保存到数据库中。
事务冲突后,会回滚修改行数较小的事务。
事务的隔离级别
-
读未提交(read-uncommitted)
一个事务未提交,另一个事务可以读到另外一个事务的脏数据。称为脏读 -
不可重复度(read-committed) 或者是叫 读已提交。就是读取到的两次数据不一致。
1)打开一个客户端A,并设置当前事务模式为read committed(读已提交),查询表account的所有记录:
2)在客户端A的事务提交之前,打开另一个客户端B,更新表account,金额减去50块。
3)B客户端的事务未提交,这时A看到账户里的钱还是 450,客户端A不能查询到B已经更新的数据,解决了脏读
4)客户端B提交事务后,客户端A读到的数据成了400,两次读的数据不一样,所以不能重复读。
- 可重复读(repeatable-read)
当隔离级别设置为可重复读的时候,在第四部查询的结果相同了
1)打开一个客户端A,并设置当前事务模式为repeatable read,查询表account的所有记录
2)在客户端A的事务提交之前,打开另一个客户端B,更新表account并提交
3)在客户端A查询表account的所有记录,与步骤(1)查询结果一致,没有出现不可重复读的问题
4)在客户端A,接着执行update balance = balance - 50 where id = 1,balance没有变成400-50=350,lilei的balance值用的是步骤(2)中的350来算的,所以是300,数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了MVCC机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)。
-
串行化(serializable)
-
总结:事务的隔离级别越高,性能越慢。
mvcc多版本并发控制(存储引擎层面实现Innodb)
MySQL的事务型存储引擎,并不是通过简单的行级锁来处理并发的,而是通过MVCC,MVCC是行级锁的一个变种,只是在必要的时候,加上锁。
mysql中的mvcc在 可重复读(repeatable-read)的隔离级别下是如何工作的。
mvcc是在数据列后面加了2个隐藏的列:一个是创建时间,一个是过期时间(或删除时间)。创建时间是当前的版本号,每开启一个事务,版本号+1,
-
select:
A:查询创建时间比当前版本号小的。这样就可以确保事务读取的行,要么是在事务开始之前就已经存在的,要么是当前事务本身增加的。
B:行的删除版本号,要么是空的,要么是大于当前版本号的。这样就可以确保事务读取到的行,在事务开始之前未被删除。只有符合上面两个条件的记录,才能被返回。
-
insert: Innodb为新插入的每一行,保存当前系统版本号+1 ,为该行的创建时间版本号
-
delete: innodb为删除的每一行,保存当前的系统版本号+1,为该行的删除时间版本号
-
update:innodb 会插入一条修改后的记录,当前系统版本号+1为改行的创建时间版本号,同时保存当前的版本号+1 为原来的行作为删除时间版本号