今天接到了一个电话面试,面试官问到了事务的隔离级别,之前在使用数据库时,我从未考虑过事务的并发控制相关的知识。始终觉得我使用了事务,那么事务就应该如我所期望的一样,保证数据之间的一致性。面试结束后,我也去查询了相关资料,了解了数据库事务隔离相关的知识,现在就和大家分享一下:
事务的特性:
我们都知道,事务应当具有以下特性:
1. 原子性:事务不可分割
2. 一致性:事务执行的前后,数据完整性保持一致.
3. 隔离性:一个事务执行的时候,不应该受到其他事务的打扰
4. 持久性:一旦结束,数据就永久的保存到数据库.
不考虑隔离性产生的事务并发问题:
原子性和一致性大多数人都应经很熟悉了,本文将重点围绕隔离性来总结。如果我们不考虑事务隔离性,则会产生以下现象:
- 丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加任何锁造成的;
- 脏读:一个事务读到另一个事务未提交数据,后一个事务如果操作失败进行了回滚,前一个事务读取的就是错误数据,这样就造成了脏读。
- 不可重复读: 事务 A 多次读取同一数据,事务 B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。关注点在于修改。
- 虚读(幻读):一个事务多次读取一组数据,另一个事务在第一个事务的两次读取操作之间提交了数据(insert),导致一个事务两次查询结果条数不一致。关注点在于新增和删除,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
事务隔离级别:
数据库提供了四种事务隔离级别:
事务隔离级别 | 丢失更新 | 不可重复读 | 脏读 | 幻读 |
---|---|---|---|---|
读未提交(read-uncommitted) | 否 | 是 | 是 | 是 |
不可重复读(read-committed) | 否 | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 | 否 |
1. 读未提交:
一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据,该隔离级别可以通过“排他写锁”实现。解决了丢失更新的问题。这可能会造成脏、不可重复、幻读现象
2. 不可重复读(sqlServer、Oracle默认级别):
事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变(不可重复读)。可防止脏读,但不可重复读和幻读仍可发生
3. 可重复读(MySql默认级别):
在开始读取数据(事务开启)时,不再允许修改操作,这可以通过“共享读锁”和“排他写锁”实现。可防止脏读、不可重复读,但幻读仍可发生
4. 序列化:
事务隔离的最高级别,提供严格的事务隔离,它要求事务串行执行,事务只能一个接着一个地执行,但不能并发执行。会引发超时和锁竞争,耗资源多、损失并行度、性能低,一般不用
总结:如果仅仅把事务级别设为最高,就会带来并发的性能问题,因此,如果使用好事务隔离级别是一门很深的学问