2021-05-02

数据库事务ACID和锁

Views: 5609 | Add Comments

数据库事务功能非常重要, 任何应用只要操作的多个对象之间有依赖(约束)关系, 都会不约而同地想到使用事务, 例如银行转账功能, 社交 App 中的粉丝关注功能, 购物网站下订单功能. 任何一个数据库系统, 如果不提供事务功能, 就不能减少用户(应用开发者)的某些麻烦, 因为用户不得不自己在应用层去实现类似事务的代码逻辑.

从用户的角度看, 如果数据库不提供事务, 他就要多写代码, 这让他很不爽. 所以, 即使是 KV 数据库, 也应该提供事务功能. 但是, 不仅事务功能的实现是有成本的, 使用也有成本. 比如, 很多用户不能准确地理解事务的特点和作用.

抽丝剥茧, 我们来分析一下数据库事务的本质是什么? 用户对事务功能的需求的本质是什么?

先从 ACID(Atomicity, Consistency, Isolation, Durability)说起.

  • Durability(持久化), 这不是事务的特性, 而是数据库系统的本能特性, 没有持久化的数据库是"伪数据库", 是缓存系统. 所以, 没有啥可讨论的.
  • Consistency(一致性), 一致性用于解决多副本不相同, 和解决数据依赖(约束)级联更新问题. 多副本和数据依赖, 本质上都是多处数据更新时间差的问题, 因为多处数据绝对不可能立即同时更新. 一致性本质上是原子性(Atomicity)问题.
  • Isolation(隔离性), 隔离性本质上也是受多数数据不能"立即同时"更新所限而产生的. 因为多数数据不能立即同时更新, 所以看到更新的先后顺序会对业务的因果关系(数据依赖)产生影响. 不同的隔离级别产生不同的影响.
  • Atomicity(原子性), 原子性是数据库事务的最重要特性, 也是用户的目的(本质). 用户之所以使用事务, 是因为用户追求的正是原子性

数据库事务常常和锁一起使用, 无论是用户的意图, 或者是数据库系统的实现原理, 事务都和锁紧密联系在一起. 大部分的事务都涉及到单一资源的争抢.

在网站购物为例子, 我们在一个事务中扣减商品的库存, 同时添加一条订单记录. 这个事务里, 就涉及到了对商品库存的争抢, 其本质就是锁. 虽然你可以在 SQL 中使用 "update where amount>=x"这种句式来使用乐观锁, 但业务的本质确实是在争抢资源, 就是在用锁.

只要事务中的多个操作之间有因果依赖关系, 本质就是锁.

有一个加粉丝关注功能的例子, 原来我认为用户需要的只是原子性, 后来才发现, 用户本质上需要的是原子性+锁. 这个例子是这样的:

开始时, B 关注了 A.

接着, A 加 B 为好友, 需要在事务(T1)中做这些操作:

  1. 往 A 的 following 列表里添加 B
  2. 往 B 的 followers 列表里添加 A
  3. 因为 A 和 B 互相关注, 往 A 的 friends 列表里添加 B, 同时往 B 的 friends 列表里添加 A

在 T1 开始之前, 用户 B 决定取关 A, 注意, 只是下了决定, 但还没有执行, 基于决定, B 要在事务(T2)中做这些操作:

  1. 把 A 从 B 的 following 列表中删除
  2. 把 B 从 A 的 followers 列表中删除

T2 不操作 friends 列表, 因为 B 下决定的时候, 认为两者不是好友. 如果 T1 完全执行完毕后, T2 再执行, 就会出现 B 在 A 的好友列表中, 但 B 实际上并没有关注 A, 违反了业务约束.

用数据表格来表示数据的随时间变动:

A's following A's followers A's friends B's following B's followers B's friends
开始 B A
T1 之后 B B B A A A
T2 之后 B B A A

为了解决这个问题, T2 可以利用悲观锁, 在下决定之前加锁(或者把加锁和下决定作为事务的一部分), 排斥 T1.

或者, 保存"下决定"所依据的数据的版本号, 然后在事务 T2 中执行 CAS 操作(乐观锁).

总结

原子性, 悲观锁, 乐观锁, 这3者是用户对数据库事务需求的本质, 大部分实际场景的事务都会使用锁.

PS: 数据库系统的读操作的因果关系可能与原子性冲突, 一个事务中修改了2个对象, 你可能先见到一个对象已更新, 但之后又发现另一个对象还没有更新. 见这个有趣的讨论: https://www.ideawu.net/blog/archives/1150.html

Related posts:

  1. 数据库事务的原子性与隔离级别
  2. Raft 选主优化之 PreVote
  3. 可靠通信的三条基本定理(可靠通信三原则)
  4. 数据库的持久化等级
  5. 6岁的 Heather Martin 唱她妈妈写的歌
Posted by ideawu at 2021-05-02 13:30:21

Leave a Comment