乐观锁与悲观锁

一、悲观锁

悲观锁(Pessimistic Lock),每次拿到数据时都会担心被别人修改,所以每次在拿数据的时候都会上锁,确保自己使用的过程中不会被被人修改,使用完后再释放锁。悲观锁常见的实现场景:

  • a. 数据库:通过select … for update;上锁,直到事务commit或rollback
  • b. synchronized:操作前先上同步锁
  • c. 分布式锁:操作前先上分布式锁,操作完成后释放锁

以第一种数据库层的悲观锁为例,在关系型数据库中,悲观并发控制(Pessimistic Concurrency Control, 简写为PCC)被称为悲观锁,它是一种数据库层的并发控制方法,如果一个事务执行的操作对一行数据上了锁,那只有当这个事务释放锁,其他事务才执行与该锁冲突的操作。

本文使用MySQL InnoDB做测试,MySQL默认使用autocommit模式,也就是说执行一个更新操作后,MySQL会立即将结果提交,因此,为便于测试需要关闭自动提交模式.

开启MySQL-console1:

mysql> set autocommit=0;

创建的测试数据如下:

mysql> select * from user;
---
+----+---------+-----+
| id | name    | age |
+----+---------+-----+
|  1 | hehe    |  29 |
|  2 | Tonglin |  25 |
+----+---------+-----+

开启并执行事务:

mysql> begin; # 同begin work/start transaction
mysql> select * from user where id=1 for update;
mysql> update user set age=100 where id=1;

此时,开启MySQL-console2,对同一行记录进行查询或修改:

mysql> select * from user where id=1;
---
+----+------+-----+
| id | name | age |
+----+------+-----+
|  1 | hehe |  29 |
+----+------+-----+
1 row in set (0.00 sec)

mysql> update user set age=99 where id=1;
---

在MySQL-console2上可以对id=1的记录进行查询,但修改操作会被阻塞,直到MySQL-console1上的事务提交或回滚。

MySQL-console1:

mysql> commit; # commit work,或回滚rollback

MySQL-console2:

mysql> update user set age=99 where id=1;
---
Query OK, 1 row affected (0.00 sec)
二、乐观锁

乐观锁(Optimistic Lock),每次拿数据的时候完全不担心被别人修改,所以每次拿数据的时候都不会上锁,但是会在更新数据的时候判断是否被别人修改过,期间数据可以被其他人读取。悲观锁常见的实现场景:

  • a. 数据库:版本号机制
  • b. CAS操作

仍然以数据库为例,在关系型数据库中,乐观并发控制(Optimistic Concurrency Control, 简写为OCC)被称为乐观锁。在提交数据库更新前会检查其他事务是否已经修改了数据,如果有更新,则当前提交的事务进行回滚。

首先为数据库表增加version字段:

mysql> select * from user;
---
+----+---------+-----+---------+
| id | name    | age | version |
+----+---------+-----+---------+
|  1 | hehe    |  99 |       0 |
|  2 | Tonglin |  25 |       0 |
+----+---------+-----+---------+

在执行更新前先查询数据库,将版本号version字段一同读出,数据每更新一次将version值加1。模拟并发的场景,假设两个线程读取了相同的记录数据,并尝试对相应主键(id=1)和版本号(version=1)的就进行修改,并将其版本号加1,这样只有第一个执行的线程可以执行成功,第二个线程执行时版本号已经变为2,因此更新失败。

select * from user where id=1;
---
+----+------+-----+---------+
| id | name | age | version |
+----+------+-----+---------+
|  1 | hehe |   1 |       1 |
+----+------+-----+---------+

mysql> update user set age=2, version=version+1 where id=1 and version=1;
---
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> update user set age=3, version=version+1 where id=1 and version=1;
---
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0  Changed: 0  Warnings: 0
三、优缺点对比

两种锁各有优缺点,不能单纯说哪个更好,需要根据具体业务场景决定使用哪一种锁。

  • 3.1 乐观锁比较适合数据修改比较少,读取比较频繁的场景,即使出现了少量写冲突,也省去了大量锁的开销,因此提高了系统的吞吐量。
  • 3.2 悲观锁比较适合数据修改较多的场景,悲观锁通过锁记录减少冲突,从而减少上层retry,可以提升系统性能。
参考
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值