数据库事务与并发

多事务运行时的并发问题

  1. 第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖;
  2. 脏读:一个事务读到另一个事务未提交的更新数据;
  3. 虚读:一个事务读到另一个事务已提交的新插入的数据;
  4. 不可重复读:一个事务读到另一个事务已提交的更新数据;
  5. 第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另一个事务已提交的更新数据。

数据库锁

资源上已放置的锁 第二个事务读操作 第二个事务更新操作
立即获取共享锁 立即获取独占锁
共享锁 立即获取共享锁 等待第一个事务解除共享锁
独占锁 等待第一个事务解除独占锁 等待第一个事务解除独占锁

共享锁

用于读数据操作,他是非独占的,允许其他事务同时读取其锁定的资源,但不允许其他事务更新他。

  1. 加锁条件:当一个事务执行select语句时。
  2. 解锁条件:默认情况下,数据读取后,数据库系统立即释放共享锁。
  3. 兼容性:放置共享锁后还可放置共享锁和更新锁。
  4. 并发性:具有良好的并发性能。当多个事务读取相同的数据时,每个事务都会获得一把共享锁,因此可以同时读锁定的数据。

独占锁

也叫排他锁,使用与修改数据的场合。他锁定的资源,其他事务不能进行读写。

  1. 加锁条件:当一个事务执行insert,update,delete时,数据库系统会自动对被操作的数据使用独占锁。如果该数据已有其他锁存在,则不能放置独占锁。
  2. 解锁条件:事务结束。
  3. 兼容性:不和其他锁兼容。
  4. 并发性:并发性差,只允许有一个事务访问锁定数据。其他事务需等待,直到当前事务结束。

更新锁

更新操作的初始阶段用来锁定可以能要被修改资源的锁。更新锁可避免使用共享锁造成的死锁现象。

  1. 加锁条件:当执行update时,数据库系统会先为事务分配一个更新锁。
  2. 解锁条件:当读取数据完毕,执行更新操作时,更新锁升级为独占锁。
  3. 兼容性:与共享锁兼容。一个资源可同时放置更新锁和共享锁,但只能放置一把更新锁。
  4. 并发性:允许多个事务同时读锁定的资源,但不允许其他事务修改。

如何防止死锁

  1. 合理安排表访问顺序;
  2. 使用短事务(包含尽可能少的能在短时间内完成操作的事务);

    • 将事务分解为多个小事务,让后分别执行。这样可保证每个小事务的快速完成,减少对数据资源锁定的时间;
    • 减少执行事务过程中等待需要更新的数据。
  3. 如果对数据要求不高,可以允许脏读。脏读不需要对数据资源加锁,可以避免锁冲突;

  4. 如果可能,错开多个事务访问相同数据资源的时间,防止锁冲突;

  5. 使用尽可能低的事务隔离级别。

数据库事务隔离级别

  1. Serializalble: 串行化。一个事务在执行过程完全看不到其他事务对数据库所做的更新。当两个事务同时访问相同数据时,第一个事务必须等第二个事务完成后才能访问。

  2. Repeatable Read: 可重复读。事务在执行过程中可以看到其他事务已提交的新插入记录,但不能看到其他事务已提交的对已有记录的更新。

  3. Read Committed: 读已提交数据。事务在执行过程中可以看到其他事务已提交的新插入记录,也可看到其他事务已提交的对已有记录的更新。

  4. Read Uncommited: 读未提交数据。事务在执行过程既可以看到其他事务没有提交的新插入数据,也可看到其他事务已经提交的对已有记录的更新。

其中Serializalble的隔离级别最高,但并发性能最差。而Read Uncommited的隔离级别最低,但并发性能最好。隔离级别越高,越能保证数据的完整性和一致性,但并发性能则会降低。对于多数程序,可以优先考虑把数据库系统的隔离级别设置为Read Committed,它可以避免脏读,而且具有较好的并发性能。