事务与锁的探讨-自动锁

  • 事务与锁的探讨-自动锁
  • 事务与锁的探讨-人工锁
  • 事务与锁的探讨-应用锁

学了数据库理论知识,我们都知道数据库中有个锁概念,但是我们在软件开发过程中似乎从来没特别指定过锁,那么这个锁到底存不存在呢?

在类似 SQL Server 这类大型数据库管理系统中是存在的,程序中没指定锁的话,就是自动的。

示例一、

代码执行顺序为:

  1. 事务 A 读取记录
  2. 事务 B 读取同一记录
  3. 事务 A 更新同一记录

在数据库管理系统中执行顺序为:

  1. 事务 A 读取记录
  2. 事务 B 读取同一记录
  3. 事务 A 更新同一记录

没啥区别呀,这有啥锁呀?不急,继续看:

示例二、

代码执行顺序为:

  1. 事务 A 更新记录
  2. 事务 B 读取同一记录
  3. 事务 A 更新同一记录

在数据库管理系统中执行顺序为:

  1. 事务 A 更新记录
  2. 事务 A 更新同一记录
  3. 事务 B 读取同一记录

这次仅仅因为第 1 步由读取记录变成了更新记录,所以事务 B 就得等事务 A 执行完了再执行,所以说呀,这个锁是自动的,而且还挺聪明,能够识别加什么样的锁。

事务 B 为什么要去等事务 A 呢?试想一下,如果事务 A 最后 1 步更新时失败,就会导致第 1 步被会撤销(事务的特性就是这样,要么都成功,要么都失败),为了避免事务 B 读取的数据是被撤销的数据,所以就让事务 B 等一等了,等事务 A 完成,确认数据不会被撤销了,再来读取。

小结一下:在事务 A,当执行到 update 时,就会在这里开始加锁,事务 B 若在加锁后执行到 select、update 相同的表时,就会等待事务 A 结束。(注意是执行到 select、update 相同的表时开始等待,并不是事务 B 一开始就等待。)

示例三、

代码执行顺序为:

  1. 事务 A 更新记录
  2. 事务 B 读取另一表的记录
  3. 事务 A 更新另一表的记录(与事务 B 同表)

在数据库管理系统中执行顺序为:

  1. 事务 A 更新记录
  2. 事务 B 读取另一表的记录
  3. 事务 A 更新另一表的记录(与事务 B 同表)

嘿,这回又变了,足见锁的聪明:

它知道事务 A 的第 1 步更新并不与事务 B 冲突,所以事务 B 并没有等待;

事务 A 的最后 1 步本身就位于事务 B 的后面,它更新成功与否不影响事务 B 的脏不脏读问题,所以按顺序执行。

我们在实际使用过程中,如果要求不是很严格,并发量不是很大,大多数也不需要关注上述理论,只需要知道有这么回事即可。但如果并发量很大,要求又高(比如金融),这就必须考虑,此时可以认真阅读一下本文。如果你想亲自测试一下,我们建议用两个线程,再加 Thread.Sleep 让代码执行顺序固定下来,看看最终在数据库管理系统中的执行顺序与代码执行顺序有何差别。

扩展一下

在同一事务中:

1、事务开始

2、更新表记录

3、选取表记录

4、更新表记录(这次失败了,事务撤销)

最终数据库中的表记录没有更新,但第 3 步选出来的记录仍然是第 2 步更新之后的记录。

相关阅读

  • .NET 事务技术-System.Transactions.TransactionScope 隐式事务
  • 多线程与异步-C# 如何使用线程
  • 事务与锁的探讨-自动锁
  • 事务与锁的探讨-人工锁
  • 事务与锁的探讨-应用锁

你可能感兴趣的