数据库系统原理: 并发控制之悲观锁与乐观锁

1. 并发控制

在多线程环境下,为了保证线程安全,需要使用并发控制。

                                数据库系统原理: 并发控制之悲观锁与乐观锁

数据库管理系统中有事务的概念,它是一组操作,并且满足 ACID 特性。一个事务可以看成一组任务,而任务是由线程驱动的,因此事务也可以并发地执行。并发执行多个事务时,为了保证每个事务都具有 ACID 特性,也同样需要使用并发控制。

                 数据库系统原理: 并发控制之悲观锁与乐观锁

主要有两种并发控制方法:悲观并发控制乐观并发控制,应该注意到它们都是思想,而不是具体的实现。

 

2. 悲观并发控制

悲观并发控制保持着悲观的态度,认为并发执行过程一定会出现问题,也就一定要做点什么去防止出现问题。

传统的锁机制都是悲观并发控制的实现,通过加锁从而保证多线程互斥地修改和访问共享数据,那么多线程就不会竞争使用共享数据,也就不会产生线程安全问题。

                                           数据库系统原理: 并发控制之悲观锁与乐观锁

在数据库管理系统中提供了两种锁:共享锁(S)排它锁(X),也称为读锁和写锁。

  • 一个事务对数据 A 加了 X 锁,就可以对 A 进行读取和更新,加锁期间其它事务不能对 A 加任何锁。
  • 一个事务对数据 A 加了 S 锁,可以对 A 进行读取操作,但是不能进行更新操作,加锁期间其它事务能对 A 加 S 锁,但是不能加 X 锁。

MySQL 的 InnoDB 存储引擎可以使用以下方法分别加 S 锁和 X 锁:

SELECT ... LOCK In SHARE MODE;
SELECT ... FOR UPDATE;

悲观并发控制能够保证线程安全性,但是无论共享数据是否真的会出现竞争,它都要进行加锁。而加锁操作需要涉及用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作,代价非常高。所以悲观并发控制只适合共享数据经常出现竞争的场景,但是对于共享数据基本不会出现竞争的场景,它花费了很多不必要的锁开销。

 

 

3. 乐观并发控制

针对悲观并发控制对于共享数据基本不会出现竞争的情况下也需要加锁的问题,出现了乐观并发控制。它保持乐观的态度,认为并发执行过程不会对共享数据出现竞争问题。它只在修改数据之后检测一下修改期间该共享数据有没有出现竞争问题,也就是说有没有其它线程也同样修改了该共享数据,如果没有则修改成功,如果有则需要重做。

                                数据库系统原理: 并发控制之悲观锁与乐观锁

CAS 实现

CAS 指令是硬件支持的操作:比较并交换(Compare-and-Swap),它需要有 3 个操作数,分别是内存地址 V、旧值 A 和新值 B。CAS 用于冲突检测,内存地址 V 中的值如果不等于旧值 A,则表示冲突,否则才将内存地址 V 的值更新为新值 B。

                    数据库系统原理: 并发控制之悲观锁与乐观锁

 

使用版本号实现

在数据库管理系统中,可以为表增加一个 version 列,那么每个记录就可以维护一个版本号,每次修改时版本号加 1。在对记录进行修改之前先读出 version,并在修改后判断 version 是否发生改变。如果没有发生改变就表示没有发生冲突,执行提交,否则回滚。

                                              数据库系统原理: 并发控制之悲观锁与乐观锁

 

转自:CS-NOTES