理解乐观锁、悲观锁、共享锁、排他锁、表锁、行锁

本文深入探讨了乐观锁和悲观锁的概念,包括它们的实现方式、适用场景以及如何在数据库中具体应用。同时,文章对比了两种锁机制的优缺点,并通过实例演示了在高并发环境下如何选择合适的锁策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.行锁:即共享锁和排他锁

 

转载于:https://www.cnblogs.com/xincanzhe/p/10411107.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值