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

作者:huaweichenai 时间:2023-11-16 13:38:38 

一、在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();
}

来源:https://blog.csdn.net/huaweichenai/article/details/127635042

标签:Yii,乐观锁,悲观锁
0
投稿

猜你喜欢

  • Python标准库学习之psutil内存详解

    2023-09-14 21:33:35
  • pandas分区间,算频率的实例

    2021-12-12 01:27:24
  • msxml3.dll (0x80070005)拒绝访问 解决方法

    2010-03-11 21:26:00
  • face++与python实现人脸识别签到(考勤)功能

    2021-10-18 09:06:06
  • 抽取oracle数据到mysql数据库的实现过程

    2024-01-14 03:07:12
  • SQL Server数据库连接查询和子查询实战案例

    2024-01-15 02:44:21
  • Python实现KNN(K-近邻)算法的示例代码

    2023-09-25 15:56:18
  • 基于Python的科学占卜工具开发过程

    2023-01-01 03:15:05
  • 從無到有實現一個xml數據庫登錄驗証

    2008-09-05 17:12:00
  • Golang中使用JSON的一些小技巧分享

    2024-04-27 15:33:34
  • Python2.7 实现引入自己写的类方法

    2022-02-25 00:07:44
  • 解析smarty模板中类似for的功能实现

    2023-11-15 12:53:40
  • Django实现微信小程序的登录验证功能并维护登录态

    2022-03-14 22:56:48
  • python使用mailbox打印电子邮件的方法

    2023-09-05 10:15:30
  • 如何更改Linux(CentOS)系统下的MySQL数据库目录位置

    2024-01-24 15:07:00
  • Mootools 1.2教程(8)——输入过滤第一部分(数字)

    2008-11-27 13:01:00
  • pytorch 移动端部署之helloworld的使用

    2022-03-30 18:56:18
  • MySQL和Oracle的元数据抽取实例分析

    2024-01-20 18:52:00
  • MySQL触发器基本用法详解【创建、查看、删除等】

    2024-01-14 09:48:22
  • python面向对象编程设计原则之单一职责原则详解

    2022-02-13 20:51:25
  • asp之家 网络编程 m.aspxhome.com