• 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方法负责

    1. public function optimisticLock()
    2. {
    3.     return null;
    4. }


    这个方法返回 null ,表示不使用乐观锁,如果我们需要使用乐观锁的话,我们需要在我们的需要加锁的表的Model中重载optimisticLock方法

    1. public function optimisticLock()
    2. {
    3.     return 'version';
    4. }


    如上说明当前的ActiveRecord中,有一个 version 字段,可以为乐观锁所用

    2:实现乐观锁

    我们在Model中设置了版本号后,这时候我们的更新和删除都是乐观锁操作了,与正常操作数据库的方式一致

    1. try {
    2.     $crowd = Crowd::findOne(['crowd_id' => 12]);
    3.     $crowd->status = 1;
    4.     $crowd->save();
    5. } catch (\Exception $e) {
    6.     return false;
    7. }


    在更新过程中,我们会调用到 yii\db\BaseActiveRecord::updateInternal()方法,此方法里面就具有处理乐观锁的代码

    1. protected function updateInternal($attributes = null)
    2. {
    3. if (!$this->beforeSave(false)) {
    4. return false;
    5. }
    6. // 获取等下要更新的字段及新的字段值
    7. $values = $this->getDirtyAttributes($attributes);
    8. if (empty($values)) {
    9. $this->afterSave(false, $values);
    10. return 0;
    11. }
    12. // 把原来ActiveRecord的主键作为等下更新记录的条件,也就是说,等下更新的,最多只有1个记录。
    13. $condition = $this->getOldPrimaryKey(true);
    14. // 获取版本号字段的字段名,比如 version
    15. $lock = $this->optimisticLock();
    16. // 如果 optimisticLock() 返回的是 null,那么,不启用乐观锁。
    17. if ($lock !== null) {
    18. // 这里的 $this->$lock ,就是 $this->version 的意思; 这里把 version+1 作为要更新的字段之一。
    19. $values[$lock] = $this->$lock + 1;
    20. // 这里把旧的版本号作为更新的另一个条件
    21. $condition[$lock] = $this->$lock;
    22. }
    23. $rows = static::updateAll($values, $condition);
    24. // 如果已经启用了乐观锁,但是却没有完成更新,或者更新的记录数为0
    25. // 那就说明是由于 version 不匹配,记录被修改过了,于是抛出异常。
    26. if ($lock !== null && !$rows) {
    27. throw new StaleObjectException('The object being updated is outdated.');
    28. }
    29. if (isset($values[$lock])) {
    30. $this->$lock = $values[$lock];
    31. }
    32. $changedAttributes = [];
    33. foreach ($values as $name => $value) {
    34. $changedAttributes[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
    35. $this->_oldAttributes[$name] = $value;
    36. }
    37. $this->afterSave(false, $changedAttributes);
    38. return $rows;
    39. }


    在删除过程中,我们会调用到 yii\db\BaseActiveRecord::delete()方法,此方法里面就具有处理乐观锁的代码

    1. public function delete()
    2.     {
    3.         $result = false;
    4.         if ($this->beforeDelete()) {
    5.             // 删除的SQL语句中,WHERE部分是主键
    6.             $condition = $this->getOldPrimaryKey(true);
    7.             // 获取版本号字段的字段名,比如 version
    8.             $lock = $this->optimisticLock();
    9.             // 如果启用乐观锁,那么WHERE部分再加一个条件,版本号
    10.             if ($lock !== null) {
    11.                 $condition[$lock] = $this->$lock;
    12.             }
    13.             $result = static::deleteAll($condition);
    14.             if ($lock !== null && !$result) {
    15.                 throw new StaleObjectException('The object being deleted is outdated.');
    16.             }
    17.             $this->_oldAttributes = null;
    18.             $this->afterDelete();
    19.         }
    20.         return $result;
    21.     }


    如上我们就知道了,在yii中已经有了乐观锁相关的代码了,我们只需要在Model中设置一个版本号字段即可

    二:在Yii中实现悲观锁

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

    1:在yii中实现悲观锁的步骤

    1):在对任意记录进行修改前,先尝试为该记录加上锁

    2):如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常

    3):如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了

    2:yii中悲观锁实现

    使用select.....for update实现悲观锁,简单示例如下:

    1. $transaction = Yii::$app->db->beginTransaction();
    2. try{
    3.     //查询id为12的这条数据并且锁定
    4.     $sql = "select * from ubo_crowd where crowd_id = 12 for update";
    5.     $crowd = Yii::$app->db->createCommand($sql)->queryOne();
    6.     //更新数据
    7.     $crowd1 = Crowd::findOne(['crowd_id' => $crowd['crowd_id']]);
    8.     $crowd1->sort += 1;
    9.     if($crowd1->save()){
    10.         $transaction->commit();
    11.     }
    12. }catch(Exception $e){
    13.     $transaction->rollBack();
    14. }

  • 相关阅读:
    网页图标工具
    【MySQL】(1))连接方式+存储引擎+字符集
    java毕业设计便捷式管理系统mybatis+源码+调试部署+系统+数据库+lw
    java 强引用和弱引用
    webuploader文件上传 拖拽上传 进度监听 类型控制 上传结果监听控件
    【C++ Primer Plus学习记录】递增运算符(++)和递减运算符(--)
    俄罗斯方块
    go的堆内存结构分析
    ConcurrentHashMap源码解析 1.内部结构
    redis分布式锁使用方式
  • 原文地址:https://blog.csdn.net/huaweichenai/article/details/127635042