理解乐观锁、悲观锁、共享锁、排他锁、表锁、行锁
数据库的乐观锁、悲观锁、共享锁、排他锁、表锁、行锁说明:
1. 乐观锁:乐观锁的实现完全是逻辑的,不需要数据库提供特殊的支持,一般的做法是在需要锁的数据上增加一个版本号
public function lock() { //获取id=1该数据行的版本 $version = Db::table('test')->where('id',1)->value('version'); //TO DO...(执行其他业务逻辑) //如果最后取锁失败,可能需要回撤这些业务逻辑,所以乐观锁适合数据不那么频繁被更新的场景 //更新数据 $data['user_id'] = 100; $data['user_name'] = 'guangdong'; $data['version'] = $version + 1; //更新条件:id=1 version=$version //如果没有被其他业务更新,版本号是不会变化的,更新操作可以完成;否则更新失败 $affectRows = Db::table('test')->where(['id'=>1,'version'=>$version])->update($data); if($affectRows !== false){ $result['code'] = 1; $result['msg'] = '乐观锁取锁成功'; }else{ $result['code'] = 2; $result['msg'] = '乐观锁取锁失败'; //TO DO...(回撤其他业务逻辑) } return json($result); }
2. 悲观锁:悲观锁的实现需要数据库提供支持,开启事务并调用数据库的相关语句 lock in share mode 或者 for update,即共享锁和排他锁两种表示形式
2.1 共享锁:如select ... lock in share mode,此时给数据加了一把锁,对其只能进行读操作,其他事务可以对该数据再加共享锁,但不能再加排他锁,需要等待当前事务共享锁的释放
2.2 排他锁:如select ... for update,给数据加了一把锁,只有当前事务可以对其进行读和写操作,其他事务不能读写也不能加锁,需要等待当前事务排他锁的释放
2.3 注意 :
a.加锁时所有扫描过的行都会被锁上,因此使用悲观锁务必确定走了索引,而不是全表扫描,全表扫描相当于锁表操作
b.update、delete、insert操作会自动给语句加排他锁,所以在这三种语句后面不能加lock in share mode或是for update,否则语法会报错
c.事务提交、进程结束、进程中断都会自动释放锁
d.无论加共享锁还是排他锁都不影响普通select查询,因为普通查询时不会给语句加锁
2.4 测试:打开两个浏览器窗口,窗口1执行add_lock方法,窗口2执行test_lock方法
//当前事务:对id=1的数据加锁
public function add_lock()
{
Db::startTrans();
$result = Db::query('SELECT * FROM `test` WHERE id=1 LOCK IN SHARE MODE'); //对主键为1的数据加共享锁
//$result = Db::query('SELECT * FROM `test` WHERE id=1 FOR UPDATE'); //对主键为1的数据加排他锁
sleep(150);//阻断锁的释放
}
//其他进程:对id=1的数据增删改查
public function test_lock()
{
//查询
$result = Db::query('SELECT * FROM `test` WHERE id=1'); //普通查询
//$result = Db::query('SELECT * FROM `test` WHERE id=1 LOCK IN SHARE MODE'); //共享锁查询
//$result = Db::query('SELECT * FROM `test` WHERE id=1 FOR UPDATE'); //排他锁查询
//更新
$result = Db::query('UPDATE `test` SET version=2 WHERE id=1'); //普通更新
//$result = Db::query('UPDATE `test` SET version=2 WHERE id=1 LOCK IN SHARE MODE'); //共享锁更新
//$result = Db::query('UPDATE `test` SET version=2 WHERE id=1 FOR UPDATE'); //排他锁更新
//删除
$result = Db::query('DELETE FROM `test` WHERE id=1'); //普通删除
//$result = Db::query('DELETE FROM `test` WHERE id=1 LOCK IN SHARE MODE'); //共享锁删除
//$result = Db::query('DELETE FROM `test` WHERE id=1 FOR UPDATE'); //排他锁删除
//插入(第一个1是主键)
$result = Db::query('INSERT INTO `test` VALUES (1,6,6,6,6,6,6)'); //普通插入
//$result = Db::query('INSERT INTO `test` VALUES (1,6,6,6,6,6,6) LOCK IN SHARE MODE');//共享锁插入
//$result = Db::query('INSERT INTO `test` VALUES (1,6,6,6,6,6,6) FOR UPDATE'); //排他锁插入
dump($result);
}
测试结果:ok表示可以进行操作;no表示需要等待锁的释放,否则无法进行操作;error表示语法报错
加共享锁后: 普通查询ok 共享锁查询ok 排他锁查询no
普通更新no 共享锁更新error 排他锁更新error
普通删除no 共享锁删除error 排他锁删除error
普通插入error 共享锁插入error 排他锁插入error
加排他锁后: 普通查询ok 共享锁查询no 排他锁查询no
普通更新no 共享锁更新error 排他锁更新error
普通删除no 共享锁删除error 排他锁删除error
普通插入error 共享锁插入error 排他锁插入error
3. 表锁:LOCK TABLE为当前线程锁定表。UNLOCK TABLES释放被当前线程持有的任何锁。如果一个线程获得在一个表上的一个READ锁,该线程和所有其他线程只能从表中读。如果一个线程获得一个表上的一个WRITE锁,那么只有持锁的线程可以从表中读写,其他线程被阻止。
LOCK TABLE [table] [READ|WRITE];
...
...
...
UNLOCK TABLES;
4. 行锁:即共享锁和排他锁
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦