大家好,这里是编程Cookbook。本文详细介绍乐观锁的两种实现方式:版本号机制和CAS(Compare And Swap)。

目录
MySQL 内置的并发控制机制 MVCC(多版本并发控制) 锁机制 总结 乐观锁实现方式(需要手动实现) 1. 版本号机制 2. CAS(Compare And Swap) 3. 版本号机制 vs CAS
MySQL 内置的并发控制机制
MySQL 内置了强大的并发控制机制,例如 MVCC(多版本并发控制) 和锁机制。这些机制在更高层次上实现了并发控制。自动处理事务隔离和并发冲突,适用于复杂数据库事务管理。
MVCC(多版本并发控制)
核心:基于事务 ID 和 Undo Log,实现高效的读写并发。 特点:快照读无需加锁,写操作使用回滚日志实现隔离性。
MVCC的详细信息参考:《MySQL数据库——多版本并发控制MVCC》
锁机制
按锁粒度:表锁、行锁(InnoDB 默认)。
按锁类型:共享锁(S 锁)、排他锁(X 锁)。
意向锁:用于标记表中是否有行级锁,避免加锁冲突。
按加锁机制分:悲观锁和乐观锁。
悲观锁:通过显式加锁防止并发冲突。 乐观锁:通过版本号机制和CAS实现, 本文重点
。
锁的详细信息参考:《MySQL数据库——锁的分类》
总结
版本号机制、CAS和MVCC 的实现方式在不同层面有区别。以下是对它们在 程序员实现 和 MySQL 内部支持 上的区别详细分析:
机制 | 程序员实现 | MySQL 支持 |
---|---|---|
版本号机制 | version 字段和条件更新 | |
CAS | ||
MVCC |
程序员实现的版本号机制和 CAS: 需要手动设计表结构、编写 SQL。 适用于轻量级业务逻辑的并发控制。
乐观锁实现方式(需要手动实现)
版本号机制 和 CAS(Compare And Swap) 是实现 乐观锁 的两种常见方式,它们的核心思想是通过条件检查来保证并发安全。以下是两种方法的实现详细介绍:
1. 版本号机制
MySQL 本身并未内置对 版本号机制 的直接支持。版本号机制通常由应用程序开发人员在数据库设计和操作层手动实现。
基本原理
为每一条记录添加一个额外的字段(版本号)。 在更新数据时,先检查版本号是否与读取时一致,再执行更新。 版本号的变化表明数据已经被其他事务修改,当前事务需要重新尝试或放弃。
实现步骤
读取数据和版本号:
SELECT version, value FROM table_name WHERE id = 1;
复制查询需要更新的记录,并获取当前版本号。 检查版本号并更新:
UPDATE table_name
SET value = 'new_value', version = version + 1
WHERE id = 1 AND version = 10;复制在更新时,检查版本号是否一致。 如果一致,执行更新并将版本号加 1。 处理并发冲突(如果存在):
如果 WHERE
子句中的version
不匹配,说明该记录已被其他事务修改,当前事务更新失败。
示例
假设表结构如下:
CREATE TABLE table_name (
id INT PRIMARY KEY,
value VARCHAR(255),
version INT
);复制
事务 A:
SELECT version, value FROM table_name WHERE id = 1; -- 返回 version = 10
UPDATE table_name SET value = 'new_value', version = version + 1 WHERE id = 1 AND version = 10;复制
事务 B(同时运行):
SELECT version, value FROM table_name WHERE id = 1; -- 返回 version = 10
UPDATE table_name SET value = 'another_value', version = version + 1 WHERE id = 1 AND version = 10; -- 更新失败复制
优缺点
优点: 不需要加锁,性能高。 简单易实现。 缺点: 需要额外的字段存储版本号。 并发冲突时,需要重试或回滚,可能增加系统开销。
2. CAS(Compare And Swap)
MySQL 不直接支持类似硬件级别的 CAS 操作。对于类似 CAS 的功能,依赖程序员通过代码实现【也有说通过 SQL 实现】。
基本原理
CAS 是一种原子操作,用于更新某个值时,先比较当前值是否符合预期。 如果当前值符合预期,则执行更新,否则不更新。
实现步骤
读取数据的当前值:
获取目标变量的当前值。 比较值是否符合预期:
如果当前值与预期值一致,说明没有其他线程修改过该值。 更新数据:
在当前值符合预期时,执行更新。 如果值不一致,操作失败,可以选择重试。
示例:Go 中 CAS 实现
Go 中的 CAS 使用 sync/atomic
包提供的 CompareAndSwap
系列方法。
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var counter int32 = 10
// 期望值是 10,新值是 11
success := atomic.CompareAndSwapInt32(&counter, 10, 11)
if success {
fmt.Printf("Update successful, new value: %d\n", counter)
} else {
fmt.Printf("Update failed, current value: %d\n", counter)
}
}复制
补充:也有说可以通过SQL实现,SQL实现,本质上和版本号机制一样,不一样的点在于不需要额外字段,直接操作数据值(
count
)。如下:-- 假设表结构如下:
CREATE TABLE counter (
id INT PRIMARY KEY,
count INT );
-- 当前 count = 10
UPDATE counter SET count = count + 1 WHERE id = 1 AND count = 10;复制
优缺点
优点: 无需加锁,性能高。 操作是原子的,由硬件保证一致性。 缺点: 存在 ABA 问题(值从 A 改为 B,又改回 A,CAS 不会察觉)。 如果冲突频繁,可能导致多次重试。
3. 版本号机制 vs CAS
特性 | 版本号机制 | CAS |
---|---|---|
实现方式 | 基于字段的版本号 | |
字段要求 | version ) | 不需要额外字段 |
适用场景 | ||
是否依赖数据库 | 不依赖数据库 | |
优点 | ||
缺点 |
两者适用于不同的场景,但核心思想相同:通过比较条件,确保操作的正确性。