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
猜你喜欢
- 在Python中将字符串转换为集合使用 set() 类将字符串转换为集合,例如 my_set = set(my_str)。 set() 类将
- 关于权限管理的思考最近用laravel设计后台,后台需要有个权限管理。权限管理实质上分为两个部分,首先是认证,然后是权限。认证部分非常好做,
- 随着因特网的发展,在网络环境中,数据库应用渐渐向操作简单、功能实用的方向发展。本文介绍如何利用ASP技术实现对数据库进行在线维护的方法,并给
- 事件流浏览器中的事件流意味着页面上可有不仅一个,甚至多个元素响应同一个事件。而这一个或多个元素响应事件发生的先后顺序在各个浏览器(主要针对I
- # 建表CREATE TABLE `T_TEST` ( `Id` int(11) NOT NULL AUTO_INCREMENT
- 很久没有发表文章了,最近一直在研究产品设计标准的问题,之前有发过一篇关于 Axure的教程 ,相信很多人已经学会如何使用,这次我给大家介绍一
- 内容摘要:本文介绍了使用asp来JMail v4.3发信的大部分常用方法,包括邮件基本信息、身份验证、附件等。无需很多的修改就可以
- 1 查找记录条数 select count(*) from table_name(换成表名)&nbs
- 后台数据库用是Access,客户用了一年后说打开界面非常慢,查看了数据库后发现数据表中的记录已有五万多条,自己试过将记录复制到10 万条,打
- 当系统出现故障时,只要存在数据日志那么就可以利用它来恢复数据解决数据库故障。作为SQL Server数据库管理员,了解数据日志文件的作用,以
- 方法一【推荐】、用js插入flash,可防止虚线框激活建立一个ShowFlash.js文件,拷贝以下代码:function sho
- php中可以把函数名通过字符串的方式传递给一个变量,然后通过此变量动态调用函数下面是一个简单的动态函数调用范例<html><
- setTimeoutsetTimeout 语法例子用 setTimeout 来执行 function不断重复执行的 setTimeout设定
- 现象:在IE下,用JS修改p标签的innerHTML时,出"未知的运行时错误(unknown runtime error)&quo
- 一、功能简述正是微博如火如荼的时节,其中各个微博的分享功能是网站推广产品的好东东啊,此时如何方便快捷的使用微博的分享功能就显得比较重要了。我
- <!-- -----------[test]表生成脚本---------------
- JavaScript中indexOf函数方法是返回 String 对象内第一次出现子字符串的字符位置。使用方法:strObj.indexOf
- 安装很简单pip install psutil官网地址为:https://pythonhosted.org/psutil/ (文档上有详细的
- 记得上次电梯按钮讨论中有朋友提到日本的无序电梯,我没有太明白意思。除了各位大师提出的无厘头方案,也有不少超前的创意,好多都值得继续思考和探索
- 要随机生成字符串代码如下:在MySQL中定义一个随机串的方法,然后再SQL语句中调用此方法。随机串函数定义方法:CREATE DEFINER