位置: 编程技术 - 正文

深入理解Yii2.0乐观锁与悲观锁的原理与使用(深入理解llvm)

编辑:rootadmin

推荐整理分享深入理解Yii2.0乐观锁与悲观锁的原理与使用(深入理解llvm),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:深入理解es6中文版pdf,深入理解rcu,深入理解runloop,深入理解es,深入理解osgi,什么是深入理解,深入理解es,深入理解rcu,内容如对您有帮助,希望把文章链接给更多的朋友!

本文介绍了深入理解Yii2.0乐观锁与悲观锁的原理与使用,分享给大家,具体如下:

Web应用往往面临多用户环境,这种情况下的并发写入控制, 几乎成为每个开发人员都必须掌握的一项技能。

在并发环境下,有可能会出现脏读(Dirty Read)、不可重复读(Unrepeatable Read)、 幻读(Phantom Read)、更新丢失(Lost update)等情况。具体的表现可以自行搜索。

为了应对这些问题,主流数据库都提供了锁机制,并引入了事务隔离级别的概念。 这里我们都不作解释了,拿这些关键词一搜,网上大把大把的。

但是,就于具体开发过程而言,一般分为悲观锁和乐观锁两种方式来解决并发冲突问题。

乐观锁

乐观锁(optimistic locking)表现出大胆、务实的态度。使用乐观锁的前提是, 实际应用当中,发生冲突的概率比较低。他的设计和实现直接而简洁。 目前Web应用中,乐观锁的使用占有绝对优势。

因此,Yii也为ActiveReocrd提供了乐观锁支持。

根据Yii的官方文档,使用乐观锁,总共分4步:

为需要加锁的表增加一个字段,用于表示版本号。 当然相应的Model也要为该字段的加入,作出适当调整。比如, rules() 中要加入该字段。 重载 yiidbActiveRecord::optimisticLock() 方法,返回上一步中的字段名。 在记录的修改页面表单中,加入一个 <input type="hidden"> 用于暂存读取时的记录的版本号。 在保存代码的地方,使用 try ... catch 看看是否能捕获一个 yiidbStaleObjectException 异常。如果是,说明在本次修改这个记录的过程中, 该记录已经被修改过了。简单应对的话,可以作出相应提示。智能点的话, 可以合并不冲突的修改,或者显示一个diff页面。

从本质上来讲,乐观锁并没有像悲观锁那样使用数据库的锁机制。 乐观锁通过在表中增加一个计数字段,来表示当前记录被修改的次数(版本号)。

然后在更新、删除前通过比对版本号来实现乐观锁。

声明版本号字段

版本号是实现乐观锁的根本所在。所以第一步,我们要告诉Yii,哪个字段是版本号字段。 这个由 yiidbBaseActiveRecord 负责:

这个方法返回 null ,表示不使用乐观锁。那么我们的Model中,要对此进行重载。 返回一个字符串,表示我们用于标识版本号的字段。比如可以这样:

说明当前的ActiveRecord中,有一个 ver 字段,可以为乐观锁所用。 那么Yii具体是如何借助这个 ver 字段实现乐观锁的呢?

更新过程

具体来讲,使用乐观锁之后的更新过程,就是这么一个流程:

读取要更新的记录。 对记录按照用户的意愿进行修改。当然,这个时候不会修改 ver 字段。 这个字段对用户是没意义的。 在保存记录前,再次读取这个记录的 ver 字段,与之前读取的值进行比对。 如果 ver 不同,说明在用户修改过程中,这个记录被别人改动过了。那么, 我们要给出提示。 如果 ver 相同,说明这个记录未被修改过。那么,对 ver +1, 并保存这个记录。这样子就完成了记录的更新。同时,该记录的版本号也加了1。

由于ActiveRecord的更新过程最终都需要调用 yiidbBaseActiveRecord::updateInteranl() ,理所当然地,处理乐观锁的代码, 也就隐藏在这个方法中:

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

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

删除过程

与更新过程相比,删除过程的乐观锁,更简单,更好理解。代码仍在 yiidbBaseActiveRecord 中:

深入理解Yii2.0乐观锁与悲观锁的原理与使用(深入理解llvm)

比起更新过程,删除过程确实要简单得多。唯一的区别就是省去了版本号+1的步骤。 都要删除了,版本号+1有什么意义?

乐观锁失效

乐观锁存在失效的情况,属小概率事件,需要多个条件共同配合才会出现。如:

应用采用自己的策略管理主键ID。如,常见的取当前ID字段的最大值+1作为新ID。 版本号字段 ver 默认值为 0 。 用户A读取了某个记录准备修改它。该记录正好是ID最大的记录,且之前没被修改过, ver 为默认值 0。 在用户A读取完成后,用户B恰好删除了该记录。之后,用户C又插入了一个新记录。 此时,阴差阳错的,新插入的记录的ID与用户A读取的记录的ID是一致的, 而版本号两者又都是默认值 0。 用户A在用户C操作完成后,修改完成记录并保存。由于ID、ver均可以匹配上, 因此用户A成功保存。但是,却把用户C插入的记录覆盖掉了。

乐观锁此时的失效,根本原因在于应用所使用的主键ID管理策略, 正好与乐观锁存在极小程度上的不兼容。

两者分开来看,都是没问题的。组合到一起之后,大致看去好像也没问题。 但是bug之所以成为bug,坑之所以能够坑死人,正是由于其隐蔽性。

对此,也有一些意见提出来,使用时间戳作为版本号字段,就可以避免这个问题。 但是,时间戳的话,如果精度不够,如毫秒级别,那么在高并发,或者非常凑巧情况下, 仍有失效的可能。而如果使用高精度时间戳的话,成本又太高。

使用时间戳,可靠性并不比使用整型好。问题还是要回到使用严谨的主键成生策略上来。

悲观锁

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

在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。 如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。 如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。 其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。

悲观锁确实很严谨,有效保证了数据的一致性,在C/S应用上有诸多成熟方案。 但是他的缺点与优点一样的明显:

悲观锁适用于可靠的持续性连接,诸如C/S应用。 对于Web应用的HTTP连接,先天不适用。 锁的使用意味着性能的损耗,在高并发、锁定持续时间长的情况下,尤其严重。 Web应用的性能瓶颈多在数据库处,使用悲观锁,进一步收紧了瓶颈。 非正常中止情况下的解锁机制,设计和实现起来很麻烦,成本还很高。 不够严谨的设计下,可能产生莫名其妙的,不易被发现的, 让人头疼到想把键盘一巴掌碎的死锁问题。

总体来看,悲观锁不大适应于Web应用,Yii团队也认为悲观锁的实现过于麻烦, 因此,ActiveRecord也没有提供悲观锁。

作为Yii的构成基因之一的Ruby on rails,他的ActiveReocrd模型,倒是提供了悲观锁, 但是使用起来也很麻烦。

悲观锁的实现

虽然悲观锁在Web应用上存在诸多不足,实现悲观锁也需要解决各种麻烦。但是, 当用户提出他就是要用悲观锁时,牙口再不好的码农,就是咬碎牙也是要啃下这块骨头来。

对于一个典型的Web应用而言,这里提供个人常用的方法来实现悲观锁。

首先,在要锁定的表里,加一个字段如 locked_at ,表示当前记录被锁定时的时间, 当为 0 时,表示该记录未被锁定,或者认为这是年时加的锁。

当要修改某个记录时,先看看当前时间与 locked_at 字段相差是否超过预定的一个时长T,比如 min ,1 h 之类的。

如果没超过,说明该记录有人正在修改,我们暂时不能打开(读取)他来修改。 否则,说明可以修改,我们先将当前时间戳保存到该记录的 locked_at 字段。 那么之后的时长T内如果有人要来改这个记录,他会由于加锁失败而无法读取, 从而无法修改。

我们在完成修改后,即将保存时,要比对现在的 locked_at 。只有在 locked_at 一致时,才认为刚刚是我们加的锁,我们才可以保存。 否则,说明在我们加锁后,又有人加了锁正在修改, 或者已经完成了修改,使得 locked_at 归 0。

这种情况主要是由于我们的修改时长过长,超过了预定的T。原先的加锁自动解开, 其他用户可以在我们加锁时刻再过T之后,重新加上自己的锁。换句话说, 此时悲观锁退化为乐观锁。

大致的原理性代码如下:

上面的代码对比乐观锁,主要不同点在于:

新增加了一个加锁方法,一个获取锁定最大时长的方法。 保存时不再是把标识字段+1,而是把标识字段置0。

在具体使用方法上,可以参照以下代码:

上述方法实现的悲观锁,避免了使用数据库自身的锁机制,契合Web应用的特点, 具有一定的适用性,但是也存在一定的缺陷:

最长允许锁定时长会带来一定的副作用。时间定得长了,可能要等很长时间, 才能重新编辑非正常解锁的记录。时间定得短了,则经常退化成乐观锁。 时间戳精度问题。如果精度不够,那么在加锁时,与我们讨论过的乐观锁失效存, 在同样的漏洞。 这种形式的锁定,只是应用层面的锁定,并非数据库层面的锁定。 如果存在应用之外对于数据库的写入操作。这个锁定机制是无效的。

标签: 深入理解llvm

本文链接地址:https://www.jiuchutong.com/biancheng/291024.html 转载请保留说明!

上一篇:浅谈Yii乐观锁的使用及原理(乐观锁用法)

下一篇:PHP SFTP实现上传下载功能(php文件上传用什么请求方法)

  • 有哪些凭证
  • 施工单位缴税实例
  • 汽车申请报废了交强险能退吗
  • 利息费用如何在记账软件输入
  • 企业筹集资金的最常见及最主要的方式是()
  • 出口报关单怎么做会计凭证
  • 出口的商品交消费税吗
  • 公司销售固定资产汽车如何填申报表
  • 取得高新技术企业证书
  • 视同销售收入计入会计利润吗
  • 过渡性税收优惠是什么意思
  • 预付房租发票未到分录
  • 商业会计主要做什么帐
  • 税率调整之后税额的差异
  • 怎样查询单位上月社保缴纳情况
  • 营改增后房地产开发税费一览表
  • 普票3点怎么算
  • 最优五种个人捐赠方式税务比较?
  • 车船税交给谁了
  • 增资有哪些途径
  • 合理损耗如何计算单价?
  • 建筑行业简易征收税率是5%吗?
  • 公司有残疾人员怎么办
  • 高新技术企业研究开发费用加计扣除
  • 资源管理器被关闭了怎么恢复
  • php数组有哪几种类型
  • 接待客人后的感受和过程
  • 营改增后房地产企业账务处理
  • 电冰箱一天用多少电费正常
  • 已认证进项税发票可抵扣么
  • php实现和工作原理
  • 购买办公楼之后怎么入账
  • 工程材料费发票没拿到是否能预提费用
  • YOLOv8(n/s/m/l/x)&YOLOv7(yolov7-tiny/yolov7/yolov7x)&YOLOv5(n/s/m/l/x)不同模型参数/性能对比(含训练及推理速度)
  • 实时视频功能
  • 固定资产清理应交增值税怎么算
  • 网络销售平台优势和劣势
  • 什么情况下应该辞职
  • 如何理解ps中的黑透白不透
  • 会计中金额的正负怎么算
  • SQL查询数据库中表的某一列
  • 减免增值税计入营业外收入明细科目
  • 哪些项目需要交印花税
  • 发票隔月可以重开吗
  • 向投资者分配现金股利的会计分录?
  • 网上银行可以取款吗
  • 股本是什么类科目怎么记
  • 残保金超比例奖励政策
  • 车间不生产折旧计提放哪里
  • 应收及预付款项的坏账损失应当于实际发生时计入
  • 金蝶主要业务及软件产品
  • 集团固定资产管理
  • 加计抵减四项服务是什么?
  • windows sfc命令
  • win7系统最佳性能
  • windows7休眠唤醒设置
  • linux服务器怎么连接wifi
  • centos 7.0截屏快捷键有冲突该怎么更换?
  • rhel7.2安装
  • kvm虚拟机运行方式
  • 怎么把操作系统转移到另一个盘
  • win10系统升级后c盘满了
  • windows隐藏分区
  • window10 蓝牙鼠标
  • linux中的top命令
  • 在linux系统中获取帮助信息的命令为
  • 在linux系统中创建用户账户
  • win10桌面test mode
  • js require()
  • 如何搭建react项目
  • jquery可以实现哪些效果
  • css隐藏显示元素的方法
  • 关于中秋节的古诗
  • jquery从左到右渐渐显示
  • Android事件处理模型
  • Zero Clipboard实现浏览器复制到剪贴板的方法(多个复制按钮)
  • 深圳市税务局陈建新
  • 进项税销项税抵扣公式
  • 国税新规
  • 预缴的税额可以用留底抵扣吗
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设