特性ACID
关系性数据库需要遵循ACID规则,具体内容如下:
-
原子性 atomicity:
要么全部提交成功,要么全部失败回滚
-
一致性 consistency:
事务执行前后,数据库都必须处于一致性状态
-
隔离性 isolation:
一个事务所做的修改在最终提交以前,对其他事务是不可见的
-
持久性 durability:
事务提交后, 对数据的改变是持久的,即使数据库发生故障也不对其有任何影响。
4个隔离级别
-
读未提交 Read Uncommitted
隔离级别最低
事务中的修改,即使没有提交,对其他事务也都是可见的
比如事务A和事务B同时进行,事务A在整个执行阶段,会将某数据的值从1开始一直加到10,然后进行事务提交,此时,事务B能够看到这个数据项在事务A操作过程中的所有中间值(如1变成2,2变成3等)
-
读已提交 Read Committed
大多数数据库的默认隔离级别
事务一旦提交,该事务所作的修改对其他正在进行中的事务就是可见的。
比如事务A和事务B同时进行,事务A进行10次+1操作,此时,事务B无法看到这个数据项在事务A操作过程中的所有中间值,只能看到最终的10。另外,如果说有一个事务C,和事务A进行非常类似的操作,只是事务C是将数据项从10加到20,此时事务B也同样可以读取到20
-
可重复读 Repeatable Read
MySQL的默认隔离级别
同一个事务中多次读取同样记录结果是一致的
-
可序列化 Serializable
隔离级别最高
事务只能串行执行,不能并发执行
如果不考虑隔离性,会发生什么事呢?
-
脏读
事务可以读取未提交的数据,而该数据可能在未来因回滚而消失
-- 初始数据 -- id name -- 1 nameBeforeUpdate -- 事务1 START TRANSACTION; UPDATE table_a SET name = 'nameAfterUpdate' WHERE id = 1; -- 此时事务2开始查询, 查询id = 1 ROLLBACK; -- 事务2 SELECT name FROM table_a WHERE id = 1; -- 查询到name 为 'nameAfterUpdate'
-
不可重复读
事务内的多次查询却返回了不同的结果,这是由于在查询过程中,数据被另外一个事务修改并提交了。-- 初始数据 -- id name -- 1 nameBeforeUpdate -- 事务1 SELECT name FROM table_a WHERE id = 1; -- 查询结果为nameBeforeUpdate -- 事务2在此时提交 SELECT name FROM table_a WHERE id = 1; -- 查询结果为nameAfterUpdate -- 事务2 START TRANSACTION; UPDATE table_a SET name = 'nameAfterUpdate' WHERE id = 1; COMMIT;
-
幻读
事务在读取目标范围内的记录时,另一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生第一次读取范围时不存在的幻行-- 初始数据 -- id name -- 1 nameBeforeUpdate -- 事务1 SELECT COUNT(*) FROM table_a; -- 查询结果为1 -- 事务2在此时提交 SELECT COUNT(*) FROM table_a; -- 查询结果为2 -- 事务2 START TRANSACTION; INSERT INTO table_a SET VALUES (2, 'nameAfterUpdate')\; COMMIT;
总结
隔离级别/能解决的问题 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | - | - | - |
读已提交 | Y | - | - |
可重复读 | Y | Y | - |
可序列化 | Y | Y | Y |
不可重复读和幻读比较相似, 区别在于:
- 解决不可重复读的方法是 锁行,解决幻读的方式是 锁表。
- 不可重复读 读到其他事务update/delete后已提交的数据
幻读 读到其他事务insert已经提交的数据