悲观锁(Pessimistic Lock),每次拿到数据时都会担心被别人修改,所以每次在拿数据的时候都会上锁,确保自己使用的过程中不会被被人修改,使用完后再释放锁。悲观锁常见的实现场景:
以第一种数据库层的悲观锁为例,在关系型数据库中,悲观并发控制(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),每次拿数据的时候完全不担心被别人修改,所以每次拿数据的时候都不会上锁,但是会在更新数据的时候判断是否被别人修改过,期间数据可以被其他人读取。悲观锁常见的实现场景:
仍然以数据库为例,在关系型数据库中,乐观并发控制(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
两种锁各有优缺点,不能单纯说哪个更好,需要根据具体业务场景决定使用哪一种锁。