事务并发处理可能存在的异常都有哪些?

在了解数据库隔离级别之前,需要了解设定事务的隔离级别都要解决哪些可能存在的问题,也就是事务并发处理时会存在哪些异常情况。实际上,SQL-92标准中已经对3种异常情况进行了定义,这些异常情况级别分别为脏读(Dirty Read)不可重复度(Nnrepeatable)幻读(Phantom Read)

脏读、不可重复读和幻读都代表了什么,比如有个英雄表heros_temp,如下所示:

这张英雄表,我们会记录很多英雄的姓名,假设我们不对事务进行隔离操作,那么数据库在进行事务的并发处理时会出现怎样的情况?

第一天,小张访问数据库,正在进行事务操作,往里面写入一个新的英雄 “吕布”:

1
2
SQL> BEGIN
SQL> INSERT INTO heros_temp values(4,'吕布')

当小张还有提交该事务的时候,小李又对数据表进行了访问,他想看下这张英雄表里都有哪些英雄:

1
SQL> SELECT * FROM heros_temp;

这时,小李看到的结果如下:

你有没有发现什么异常?这个时候小张还没有提交事务,但是小李却读到了小张还没有提交的数据,这种想象我们称之为 “脏读”。

那么什么时不可重复读呢?

第二天,小张想查看id =1 的英雄是谁,于是他进行了SQL查询:

1
SQL> SELECT name FROM heros_temp WHERE id = 1;

运行结果:

然而此时,小李开始了一个事务操作,他对id=1的英雄进行了修改,把原来的“张飞”改成了“张翼德”:

1
2
SQL> BEGIN;
SQL> UPDATE heros_temp SET name = '张翼德' WHERE id = 1;

然后小张再进行一次查询,同样也是查看id=1 的英雄是谁:

1
SQL> SELECT name FROM heros_temp WHERE id = 1;

运行结果:

这个时候你会发现,两次查询的结果并不一样。小张会想这是怎么回事呢?他明明刚执行了一次查询,马上又进行了一次查询,结果两次的查询结果不同。实际上小张遇到的情况我们称之为“不可重复读”。 也就是同一条记录,两次读取的结果不同。

什么时幻读?

第三天,小张想要看下数据表里都有哪些数据,他开始执行下面这条语句:

1
SQL> SELECT * FROM heros_temp;

这时当小张执行完之后,小李又开始一个事务,往数据库里插入一个新的英雄“吕布”:

1
2
SQL> BEGIN;
SQL> INSERT INTO heros_temp values(4,'吕布');

不巧的是,小张这时忘记了英雄都有哪些,又重新执行了一遍查询:

1
SQL> SELECT * FROM heros_temp;

他发现这一次查询多了一个英雄,原来只有3个,现在变成了4个。这种异常情况我们称之为 “幻读”。

我来总结下这三种异常情况的特点:

  1. 脏读:读到了其他事务还没有提交的数据。
  2. 不可重复读:对某数据进行读取,发现两次读取的结果不同,也就是说没有读到相同的内容。这是因为有其他事务对这个数据同时进行了修改或删除。
  3. 幻读:事务 A 根据条件查询得到了 N 条数据,但此时事务 B 更改或者增加了 M 条符合事务 A 查询条件的数据,这样当事务 A 再次进行查询的时候发现会有 N+M 条数据,产生了幻读。

事务隔离的级别有哪些?

脏读、不可重复读、和幻读这三种异常情况,是在SQL-92标准中定义的,同时SQL-92标准还定义了4中隔离级别来解决这些异常情况。

解决异常数量从少到多的顺序决定了隔离级别的高低,这四种隔离级别从低到高分别是:读未提交(READ UNCOMMITTED)读已提交(READ COMMITTED)可重复读(REPEATABLE REDAD)可串行化(SERIALIZABLE)。这些隔离级别能解决的异常情况如下表所示:

  • 读未提交,也就是允许读到未提交的数据,这种情况下查询是不会使用锁的,可能会产生脏读、不可重复读、幻读等情况。
  • 读已提交,也就是只能读到已经提交的内容,可以避免脏读的产生,属于RDBMS中常见的默认隔离级别,但如果想要避免不可重复度或者幻读,就需要我们在SQL查询的时候编写带加锁的SQL语句。
  • 可重复读,保证了一个事务在相同条件下两次查询得到的数据结果数据结果是一致的,可以避免不可重复读和脏读,但无法避免幻读。MySQL默认隔离级别就是可重复度。
  • 可串行化,将事务进行串行话,也就是在一个队列中按照顺序执行,可串行化是最高的隔离等级,可以解决事务读取中所有可能出现的异常情况,但是他牺牲了系统的并发性。

隔离级别越低,意味着系统吞吐量(并发程度)越大,但同时也意味着出现异常问题的可能性会更大。在实际使用过程中我们往往需要在性能和正确性上进行权衡和取舍,没有完美的解决方案,只有适合与否。

不可重复读是对于同一条记录内容的“不可重复读”
幻读是对于某一范围的数据集,发现查询数据集的行数多了或者少了,从而出现的不一致。
所以不可重复读的原因是 对于要查询的那条数据进行了UPDATE或DELETE
而幻读是对于要查询的 那个范围的数据集,进行了INSERT。

总结