浅谈Yii乐观锁的使用及原理


Posted in PHP onJuly 25, 2017

本文介绍了Yii乐观锁的使用及原理,自己做个学习笔记,也分享给大家,希望对大家有用处

原理:

数据表中使用一个int类型的字段来存储版本号,即该行记录的版本号。更新数据时,对比版本号是否一致

sql查询代码如下(伪代码)

update `test_ver` set `name`="lili" and `ver`=2 where `id`=1 and `ver`=1

即在更新时的where查询条件中,带上之前查询记录时得到的版本号,如果其他线程已经修改了该记录,则版本号势必不会一致,则更新失败

示例

数据表

假设有如下数据表

浅谈Yii乐观锁的使用及原理

模型类

appmodelsTestVer

该模型类,重写BaseActiveRecord类中的optimisticLock方法

声明用于记录版本号的字段

/**
 * 乐观锁
 * @return string
 */
public function optimisticLock()
{
 return 'ver';
}

public function updateRecord(){
 $ver = self::findOne(['id'=>1]);
 $ver->name = "lili";
 $res = $ver->update();
 return $res;
}

updateRecord修改id为1的记录

控制器

控制器中调用updateRecord方法

public function actionVersion(){
 $testVer = new TestVer();
 $res = $testVer->updateRecord();
 return $this->render('version');
}

Yii Debugger结果

查看database选项,可以查看到实际执行的sql语句。

有一条语句如下

UPDATE `test_ver` SET `name`='lili', `ver`='2' WHERE (`id`='1') AND (`ver`='1')

Yii乐观锁实现原理

实现原理在yiidbBaseActiveRecord::updateInteranl()方法

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);

 // 获取版本号字段的字段名,比如 ver
 $lock = $this->optimisticLock();

 // 如果 optimisticLock() 返回的是 null,那么,不启用乐观锁。
 if ($lock !== null) {
  // 这里的 $this->$lock ,就是 $this->ver 的意思;
  // 这里把 ver+1 作为要更新的字段之一。
  $values[$lock] = $this->$lock + 1;

  // 这里把旧的版本号作为更新的另一个条件
  $condition[$lock] = $this->$lock;
 }
 $rows = $this->updateAll($values, $condition);

 // 如果已经启用了乐观锁,但是却没有完成更新,或者更新的记录数为0;
 // 那就说明是由于 ver 不匹配,记录被修改过了,于是抛出异常。
 if ($lock !== null && !$rows) {
  throw new StaleObjectException('The object being updated is outdated.');
 }
 $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;
}

从上面的代码中,我们不难得出:

  1. 当 optimisticLock() 返回 null 时,乐观锁不会被启用。
  2. 版本号只增不减。
  3. 通过乐观锁的条件有2个,一是主键要存在,二是要能够完成更新。
  4. 当启用乐观锁后,只有下列两种情况会抛出 StaleObjectException 异常:
    1. 当记录在被别人删除后,由于主键已经不存在,更新失败。
    2. 版本号已经变更,不满足更新的第二个条件。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
提升PHP速度全攻略
Oct 09 PHP
php zend解密软件绿色版测试可用
Apr 14 PHP
PHP中获取时间的下一周下个月的方法
Mar 18 PHP
thinkphp特殊标签用法概述
Nov 24 PHP
php绘图之生成饼状图的方法
Jan 24 PHP
PHP如何通过传引用的思想实现无限分类(代码简单)
Oct 13 PHP
PHP Header用于页面跳转时的几个注意事项
Oct 21 PHP
PHP实现QQ登录的开原理和实现过程
Feb 04 PHP
PHP学习笔记之session
May 06 PHP
php微信开发之音乐回复功能
Jun 14 PHP
php设计模式之原型模式分析【星际争霸游戏案例】
Mar 23 PHP
PHP如何开启Opcache功能提升程序处理效率
Apr 27 PHP
PHP异常处理定义与使用方法分析
Jul 25 #PHP
PHP实现防盗链的方法分析
Jul 25 #PHP
浅谈PHP发送HTTP请求的几种方式
Jul 25 #PHP
php 删除指定文件夹的实例讲解
Jul 25 #PHP
Laravel5.* 打印出执行的sql语句的方法
Jul 24 #PHP
PHP实现时间比较和时间差计算的方法示例
Jul 24 #PHP
PHP实现的登录页面信息提示功能示例
Jul 24 #PHP
You might like
php 全局变量范围分析
2009/08/07 PHP
Windows下安装Memcached的步骤说明
2010/04/25 PHP
PHP重定向与伪静态区别
2017/02/19 PHP
ThinkPHP中Widget扩展的两种写法及调用方法详解
2017/05/04 PHP
不能再简单的无闪刷新验证码原理很简单
2007/11/05 Javascript
ext for eclipse插件安装方法
2008/04/27 Javascript
IE与firefox之jquery用法区别
2008/10/03 Javascript
JavaScript delete 属性的使用
2009/10/08 Javascript
IE event.srcElement和FF event.target 功能比较
2010/03/01 Javascript
js word表格动态添加代码
2010/06/07 Javascript
javascript控制swfObject应用介绍
2012/11/29 Javascript
jquery定时滑出可最小化的底部提示层特效代码
2013/10/02 Javascript
显示今天的日期js代码(阳历和农历)
2014/09/30 Javascript
浅谈javascript 归并方法
2015/01/21 Javascript
jQuery获取浏览器类型和版本号的方法
2016/07/05 Javascript
使用vue.js实现联动效果的示例代码
2017/01/10 Javascript
js CSS3实现卡牌旋转切换效果
2017/07/04 Javascript
vuejs使用$emit和$on进行组件之间的传值的示例
2017/10/04 Javascript
vue通过接口直接下载java生成好的Excel表格案例
2020/10/26 Javascript
[01:56]2014DOTA2西雅图邀请赛 MVP外卡赛老队长精辟点评
2014/07/09 DOTA
python实现代码行数统计示例分享
2014/02/10 Python
Python的迭代器和生成器
2015/07/29 Python
Python使用time模块实现指定时间触发器示例
2017/05/18 Python
Python线性回归实战分析
2018/02/01 Python
python脚本作为Windows服务启动代码详解
2018/02/11 Python
Python使用pymongo模块操作MongoDB的方法示例
2018/07/20 Python
使用Python进行目录的对比方法
2018/11/01 Python
Python2 Selenium元素定位的实现(8种)
2019/02/25 Python
python 解决tqdm模块不能单行显示的问题
2020/02/19 Python
python实现的分层随机抽样案例
2020/02/25 Python
Django 允许局域网中的机器访问你的主机操作
2020/05/13 Python
你需要学会的8个Python列表技巧
2020/06/24 Python
为什么如下的代码int a=100,b=100;long int c=a * b;不能工作
2013/11/29 面试题
2019年销售人员的职业生涯规划书
2019/03/25 职场文书
golang日志包logger的用法详解
2021/05/05 Golang
详解pytorch创建tensor函数
2022/03/22 Python