搜索

查看: 3119|回复: 11

[PHP] Yii框架实现乐观锁与悲观锁流程详解

[复制链接]
发表于 2023-5-4 17:07:07 | 显示全部楼层 |阅读模式
Editor 2023-5-4 17:07:07 3119 11 看全部
目录
  • 一、在Yii中实现乐观锁
  • 1、在yii中实现乐观锁步骤
  • 2、Yii中实现乐观锁
  • 3、实现乐观锁
  • 二、在Yii中实现悲观锁
  • 1、在yii中实现悲观锁的步骤
  • 2、yii中悲观锁实现
    一、在Yii中实现乐观锁
    乐观锁(optimistic locking)表现出大胆、务实的态度。使用乐观锁的前提是, 实际应用当中,发生冲突的概率比较低。他的设计和实现直接而简洁。 目前Web应用中,乐观锁的使用占有绝对优势。因此在Yii为ActiveReocrd乐观锁支持

    1、在yii中实现乐观锁步骤
    1、给需要加锁的表添加一个字段,用于表示版本号,这里我一般选手version字段作为版本号字段,注意,如果你需要加锁的表已经生成Model了,那么对应表的Model要将你添加的版本号字段(version)信息加入Model
    2、在更新表中字段时,使用 try ... catch 看看是否能捕获一个 yii\db\StaleObjectException 异常,如果捕捉到yii\db\StaleObjectException 异常,说明在本次修改这个记录的过程中, 该记录已经被修改过了,作出相应提示

    2、Yii中实现乐观锁
    1、在yii中声明指定字段为版本号
    版本号是实现乐观锁的根本所在。所以第一步,我们要告诉Yii,哪个字段是版本号字段,声明版本号的方法由yii\db\BaseActiveRecord(vendor/yiisoft/yii2/db/BaseActiveRecord)中的optimisticLock方法负责
    public function optimisticLock()
    {
        return null;
    }
    这个方法返回 null ,表示不使用乐观锁,如果我们需要使用乐观锁的话,我们需要在我们的需要加锁的表的Model中重载optimisticLock方法
    public function optimisticLock()
    {
        return 'version';
    }
    如上说明当前的ActiveRecord中,有一个 version 字段,可以为乐观锁所用

    3、实现乐观锁
    我们在Model中设置了版本号后,这时候我们的更新和删除都是乐观锁操作了,与正常操作数据库的方式一致
    try {
        $crowd = Crowd::findOne(['crowd_id' => 12]);
        $crowd->status = 1;
        $crowd->save();
    } catch (\Exception $e) {
        return false;
    }
    在更新过程中,我们会调用到 yii\db\BaseActiveRecord::updateInternal()方法,此方法里面就具有处理乐观锁的代码
    protected function updateInternal($attributes = null)
    {
        if (!$this->beforeSave(false)) {
          return false;
        }
        // 获取等下要更新的字段及新的字段值
        $values = $this->getDirtyAttributes($attributes);
        if (empty($values)) {
          $this->afterSave(false, $values);
          return 0;
        }
        // 把原来ActiveRecord的主键作为等下更新记录的条件,也就是说,等下更新的,最多只有1个记录。
        $condition = $this->getOldPrimaryKey(true);
        // 获取版本号字段的字段名,比如 version
        $lock = $this->optimisticLock();
        // 如果 optimisticLock() 返回的是 null,那么,不启用乐观锁。
        if ($lock !== null) {
        // 这里的 $this->$lock ,就是 $this->version 的意思; 这里把 version+1 作为要更新的字段之一。
          $values[$lock] = $this->$lock + 1;
          // 这里把旧的版本号作为更新的另一个条件
          $condition[$lock] = $this->$lock;
        }
        $rows = static::updateAll($values, $condition);
    // 如果已经启用了乐观锁,但是却没有完成更新,或者更新的记录数为0;
      // 那就说明是由于 version 不匹配,记录被修改过了,于是抛出异常。
        if ($lock !== null && !$rows) {
          throw new StaleObjectException('The object being updated is outdated.');
        }
        if (isset($values[$lock])) {
          $this->$lock = $values[$lock];
        }
        $changedAttributes = [];
        foreach ($values as $name => $value) {
          $changedAttributes[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
          $this->_oldAttributes[$name] = $value;
        }
        $this->afterSave(false, $changedAttributes);
        return $rows;
    }
    在删除过程中,我们会调用到 yii\db\BaseActiveRecord::delete()方法,此方法里面就具有处理乐观锁的代码
    public function delete()
        {
            $result = false;
            if ($this->beforeDelete()) {
                // 删除的SQL语句中,WHERE部分是主键
                $condition = $this->getOldPrimaryKey(true);
                // 获取版本号字段的字段名,比如 version
                $lock = $this->optimisticLock();
                // 如果启用乐观锁,那么WHERE部分再加一个条件,版本号
                if ($lock !== null) {
                    $condition[$lock] = $this->$lock;
                }
                $result = static::deleteAll($condition);
                if ($lock !== null && !$result) {
                    throw new StaleObjectException('The object being deleted is outdated.');
                }
                $this->_oldAttributes = null;
                $this->afterDelete();
            }
            return $result;
        }
    如上我们就知道了,在yii中已经有了乐观锁相关的代码了,我们只需要在Model中设置一个版本号字段即可

    二、在Yii中实现悲观锁
    正如其名字,悲观锁(pessimistic locking)体现了一种谨慎的处事态度

    1、在yii中实现悲观锁的步骤
    1、在对任意记录进行修改前,先尝试为该记录加上锁
    2、如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常
    3、如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了

    2、yii中悲观锁实现
    使用select.....for update实现悲观锁,简单示例如下:
    $transaction = Yii::$app->db->beginTransaction();
    try{
        //查询id为12的这条数据并且锁定
        $sql = "select * from ubo_crowd where crowd_id = 12 for update";
        $crowd = Yii::$app->db->createCommand($sql)->queryOne();
        //更新数据
        $crowd1 = Crowd::findOne(['crowd_id' => $crowd['crowd_id']]);
        $crowd1->sort += 1;
        if($crowd1->save()){
            $transaction->commit();
        }
    }catch(Exception $e){
        $transaction->rollBack();
    }
    到此这篇关于Yii框架实现乐观锁与悲观锁流程详解的文章就介绍到这了,更多相关Yii乐观锁与悲观锁内容请搜索知鸟论坛以前的文章或继续浏览下面的相关文章希望大家以后多多支持知鸟论坛
  • 知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-28 18:52:34 | 显示全部楼层
    ffycxyw2274436 2023-6-28 18:52:34 看全部
    楼主太厉害了!楼主,I*老*虎*U!我觉得知鸟论坛真是个好地方!
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-28 20:28:05 | 显示全部楼层
    塞翁364 2023-6-28 20:28:05 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-29 11:03:51 | 显示全部楼层
    123456868 2023-6-29 11:03:51 看全部
    我看不错噢 谢谢楼主!知鸟论坛越来越好!
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-29 20:29:35 | 显示全部楼层
    米老鼠和蓝精鼠v 2023-6-29 20:29:35 看全部
    论坛不能没有像楼主这样的人才啊!我会一直支持知鸟论坛
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-29 21:28:01 | 显示全部楼层
    123456865 2023-6-29 21:28:01 看全部
    既然你诚信诚意的推荐了,那我就勉为其难的看看吧!知鸟论坛不走平凡路。
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-30 08:20:59 | 显示全部楼层
    永远爱你冰塘 2023-6-30 08:20:59 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-30 11:35:03 | 显示全部楼层
    尘埃416 2023-6-30 11:35:03 看全部
    其实我一直觉得楼主的品味不错!呵呵!知鸟论坛太棒了!
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-30 11:35:59 | 显示全部楼层
    老橡树1 2023-6-30 11:35:59 看全部
    论坛不能没有像楼主这样的人才啊!我会一直支持知鸟论坛
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-30 12:51:00 | 显示全部楼层
    123456825 2023-6-30 12:51:00 看全部
    既然你诚信诚意的推荐了,那我就勉为其难的看看吧!知鸟论坛不走平凡路。
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    • 您可能感兴趣
    点击右侧快捷回复 【请勿灌水】
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则 返回列表

    RSS订阅| SiteMap| 小黑屋| 赞兔论坛
    联系邮箱E-mail:zniao@foxmail.com
    快速回复 返回顶部 返回列表