后浪笔记一零二四

乐观锁和悲观锁

悲观锁

在修改数据之前先锁定(悲观地认为一定有并发),再修改的方式 被称之为悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control)。

悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。

但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加死锁的机会; 另外,还会降低并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务 处理完才可以处理那行数据。

1
2
3
4
5
6
7
8
//0. 开始事务
begin;
// 1. 查询出商品库存信息
select quantity from items where id=1 for update;
// 2. 修改商品库存为2
update items set quantity=2 where id = 1;
// 3. 提交事务
commit;

乐观锁

实现方式:Compare And Swap(CAS)

1
2
3
4
5
6
7
8
//0. 开始事务
begin;
//1. 查询出商品库存信息,quantity=3
select quantity from items where id=1;
//2. 修改商品库存为2
update items set quantity=2 where id=1 and quantity=3;
//3. 提交事务
commit;

ABA问题:

+-------------------------------------------+-------------------------------------------+
|事务A                                      |事务B                                      |
+-------------------------------------------+-------------------------------------------+
|start transaction with consistent snapshot;|start transaction with consistent snapshot;|
+-------------------------------------------+-------------------------------------------+
|select quantity from items where id=1;     |select quantity from items where id=1;     |
|quantity: 3                                |quantity: 3                                |
+-------------------------------------------+-------------------------------------------+
|                                           |update items set quantity=2 where id=1 and |
|                                           | quantity=3;                               |
+-------------------------------------------+-------------------------------------------+
|                                           |update items set quantity=3 where id=1 and |
|                                           | quantity=2;                               |
+-------------------------------------------+-------------------------------------------+
|update items set quantity=4 where id=1 and |                                           |
| quantity=3;   (blocked)                   |                                           |
+-------------------------------------------+-------------------------------------------+

使用一个递增的version字段来解决ABA问题:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//0. 开始事务
begin;
//1. 查询出商品库存信息,version=1
select version,* from items where id=1;
//2. 修改商品库存为2
update items set quantity=2,version=2 where id=1 and version=1;
//3. 提交事务
commit;

+-------------------------------------------+-------------------------------------------+
|事务A                                      |事务B                                      |
+-------------------------------------------+-------------------------------------------+
|start transaction with consistent snapshot;|start transaction with consistent snapshot;|
+-------------------------------------------+-------------------------------------------+
|select version,* from items where id=1;    |select version,* from items where id=1;    |
|version: 1                                 |version: 1                                 |
+-------------------------------------------+-------------------------------------------+
|                                           |update items set quantity=2,version=2 where|
|                                           | id=1 and  version=1;                      |
+-------------------------------------------+-------------------------------------------+
|                                           |update items set quantity=3,version=3 where|
|                                           | id=1 and  version=2;                      |
+-------------------------------------------+-------------------------------------------+
|update items set quantity=4,version=2 where|                                           |
| id=1 and  version=1;                      |                                           |
+-------------------------------------------+-------------------------------------------+

上面加锁的粒度还是太大了,如果并发度上来后,就会出现大量无效(update的行数为0)的事务。

可以像这样,直接在一个sql语句中实现:

1
2
3
4
5
6
//0. 开始事务
begin;
//1. 修改商品库存(假设用户单次下单数位1)
update items set quantity=quantity-1 where id =1 and quantity-1 > 0
//2. 提交事务
commit;

总结

能一个sql搞定的,尽量用一个sql。 能使用乐观锁的,尽量使用乐观锁。


专题:

本文发表于 2022-07-01,最后修改于 2022-07-01。

本站永久域名「 jiavvc.top 」,也可搜索「 后浪笔记一零二四 」找到我。


上一篇 « 全文索引 下一篇 » mysql基础

赞赏支持

请我吃鸡腿 =^_^=

i ysf

云闪付

i wechat

微信

推荐阅读

Big Image