laravel mysql 锁表_Laravel中MySQL的乐观锁与悲观锁

MySQL/InnoDB的加锁,是一个老生常谈的话题。在数据库高并发请求下,如何兼顾数据完整性与用户体验的敏捷性是一代又一代程序员一直在思考的问题。

乐观锁

乐观锁之所以叫乐观,是因为这个模式不会对数据加锁。而是对数据操作保持一种乐观的心态,认为不会产生并发操作问题(即不会有其他线程同时对数据进行修改)。乐观锁查询数据时直接进行查询,更新时会判断其他线程有没有对数据进行修改,如果没有则进行更新,反之则拒绝更新。

cd08d4ec1e44fa145faf7dde4a48b8c1.png

乐观锁最常用数据版本(Version)的记录机制实现。即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加1。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。

示例:

1、数据库表三个字段,分别是id、value、versionselect id,value,version from TABLE where id = #{id}

2、每次更新表中的value字段时,为了防止发生冲突,需要这样操作:update TABLE

set value=2,version=version+1

where id=#{id} and version=#{version}

在larave中,我们可以在数据库维护一个lock_version字段,每次更新操作时,校验lock_version,并在更新完成后增加lock_version的值。

悲观锁

悲观锁就比较狠了,悲观锁对数据做“有罪推定”。即在操作数据时,默认此操作会出现数据冲突,所以在进行每次操作时都要加锁才能进行对相同数据的操作。一旦加锁,不同线程同时执行时,只能有一个线程执行,其他的线程在入口处等待,直到锁被释放。

1ad7c1129b8f7b83116984b55722ed74.png

悲观锁可以由数据库语句实现,分为共享锁和排它锁。

共享锁 (lock in share mode)

共享锁又称读锁 (read lock),是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。如果事务对读锁进行修改操作,很可能会造成死锁。

在Laravel中,我们在构造查询时,可以使用 sharedLock 方法为运行语句增加一把”共享锁“。DB::table('users')->where('votes', '>', 100)->sharedLock()->get();

上面这个查询等价于下面这条 SQL 语句:select * from `users` where `votes` > '100' lock in share mode

注意

在查询语句后面增加 LOCK IN SHARE MODE 后,Mysql会对查询结果中的每行都加一个共享(读)锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。 其他线程也可以读取使用了共享锁的表,而且这些线程读取的是同一个版本的数据。

加上共享锁后,对于update,insert,delete语句会自动加排它锁。

排它锁 (for update)

排他锁又称写锁(exclusive lock or writer lock)。若某个事务对某一行加上了排他锁,只能这个事务对其进行读写,在此事务结束之前,其他事务不能对其进行加任何锁,其他进程可以读取,不能进行写操作,需等待其释放。排它锁会阻塞所有的排它锁和共享锁。

在Laravel中,我们在构造查询时,可以使用 lockForUpdate 方法为运行语句增加一把“排它锁”,避免选择行被其它共享锁修改或删除:DB::table('users')->where('votes', '>', 100)->lockForUpdate()->get();

上面这个查询等价于下面这条 SQL 语句:select * from `users` where `votes` > '100' for update

注意

for update 与 lock in share mode 都是用于确保被选中的记录值不能被其它事务更新(上锁),两者的区别在于 lock in share mode 不会阻塞其它事务读取被锁定行记录的值,而 for update 会阻塞其他锁定性读对锁定行的读取(非锁定性读仍然可以读取这些记录,lock in share mode 和 for update 都是锁定性读)。

总结

乐观锁适用于读多写少的情况,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。

但如果经常产生冲突,还是使用悲观锁更稳定、可靠一些。

参考链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/356141.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql 超长记录_谁记录了mysql error log中的超长信息(记pt-stalk一个bug的定位过程)...

【问题】最近查看MySQL的error log文件时,发现有很多服务器的文件中有大量的如下日志,内容很长(大小在200K左右),从记录的内容看,并没有明显的异常信息。有一台测试服务器也有类似的问题,为什么会记录这些信息&#xf…

glassfish发布应用_Arquillian 1.0.0.Final正式发布! 准备使用GlassFish和WebLogic! 所有虫子死亡!...

glassfish发布应用红帽公司和JBoss社区今天宣布的1.0.0.Final发布的Arquillian ,其屡获殊荣的建在Java虚拟机(JVM)运行测试平台。 Arquillian大大减少了编写和执行Java中间件集成和功能测试所需的工作。 它甚至使测试工程师能够解决以前认为无…

使用ADF列表视图的主从数据

最近,从UI角度来看,ADF Faces 表组件不再被认为很酷。 对于显示数据集合, 列表视图今天应该很酷。 这并不意味着我们根本不应该使用af:table 。 在某些情况下(经常是:)),表比列表视图更适合。 但…

java 调用私有方法_公开调用私有Java方法?

java 调用私有方法我们是Java开发人员,在Java中已知4种访问修饰符:私有,受保护,公共和包。 好吧,除了私有以外,最后三个可以通过继承,相同的包或实例从类外部调用。 现在,常见的问题…

港航环境变化引起的错误解决方法

1.serlvet API缺少,pom.xml中引入坐标; 2.web.xml中出现错误,将所有的filter调到filtermapping上面去; 3.依赖导入完成后项目依然有红叉,右击项目Propreties->myeclipse->Project Facets->java换成1.6就可以了…

flutter 国际化_从0开始设计Flutter独立APP | 第二篇: 完整的国际化语言支持

鉴于Flutter高性能渲染和跨平台的优势,闪点清单在移动端APP上,使用了完整的Flutter框架来开发。既然是完整APP,架构搭建完全不受历史Native APP的影响,没有历史包袱的沉淀,设计也能更灵活和健壮。国际化语言的支持&…

将旧版本从Java EE 5减少到7

Java EE 5于2005年首次引入,而Java EE 7于2013年问世。这两个版本之间有7年的差距,从技术角度来说,这就像一个世纪。 许多组织仍然对使用Java EE 5感到困惑,并且有很多正当理由选择不升级。 不过,如果您考虑一些前进的…

sql插入临时表数据的方法

方法有两种,主要看需求。 方法1:定义好临时表的字段和类型、插入对应的值 create table #Tmp --创建临时表#Tmp (City varchar(50), --Country varchar(50), -- );insert #Tmp select 北京,中国 union select 东京,日本 union select 纽约,美国 se…

gulp

1.gulp是什么? gulp是前端开发过程中一种基于流的代码构建工具,是自动化项目的构建利器;她不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成;使用她,不仅可以很愉快的编…

往vxe-table添加渲染器怎么添_赚大了!飘窗上装书桌,加扇折叠窗,等于为家里又多添一间房...

阅读本文前,请您先点击上面蓝色字体,再点关 注这样您就可以继续免费收到文章注:本文转载自网络,如有侵权,请在后台留言联系我们进行删除,谢谢! …

【六大排序详解】中篇 :选择排序 与 堆排序

选择排序 与 堆排序 选择排序 选择排序 与 堆排序1 选择排序1.1 选择排序原理1.2 排序步骤1.3 代码实现 2 堆排序2.1 堆排序原理2.1.1 大堆与小堆2.1.2 向上调整算法2.1.3 向下调整算法 2.2 排序步骤2.3 代码实现 3 时间复杂度分析 Thanks♪(・ω・)&#…

java中contains的用法_java容器中所有接口和类的用法

我这里讲一下如何下载java的api文档还有就是容器和容器之间进行的操作每一个地方称之为一个节点,每一个节点包含了3部分(上一个节点,下一个节点,以及我们自己的数据部分)需要多个线程共享的时候通过键对象来找值对象1 java中的length属性是针…

lcs文本相似度_具有LCS方法的通用文本比较工具

lcs文本相似度常见的问题是检测并显示两个文本的差异(尤其是几百行或几千行)。 使用纯java.lang.String类方法可能是一种解决方案,但是对于此类操作最重要的问题是,“性能”将不能令人满意。 我们需要一种有效的解决方案&#xff…

MySQL--开发技巧(一)

Inner Join: Left Outer Join: Right Outer Join: Full Join: Cross Join: SELECT t1.attrs ,t2.attrs FROM t1 CROSS JOIN t2 使用Join更新表: UPDATE table1 SET attr2 WHERE attr1 IN (SELECT table2.attr1 FROM table1 INNER JOIN table2 ON tab…

js url解码gbk_使用js解码gbk编码的字符串

如下字符串为 “产后恢复肚子”%B2%FA%BA%F3%BB%D6%B8%B4%B2%D9%CA%D3%C6%B5%BD%CC%B3%CC直接使用js的解码函数解码得到的都是乱码,可以使用下面的函数进行解码/*** js解码gbk url编码的字符串* param {[type]} str gbk编码字符串* param {[type]} charset 字符串的…

mysql 5.7 insert_MySQL5.7 支持一个表有多个INSERT/DELETE/UPDATE触发器

在MySQL5.6版本里,不支持一个表有多个INSERT/DELETE/UPDATE触发器。例如创建t1表两个INSERT的触发器:DELIMITER $$USE test$$DROP TRIGGER /*!50032 IF EXISTS */ t1_1$$CREATE/*!50017 DEFINER ‘admin‘‘%‘ */TRIGGER t1_1 AFTER INSERT ON t1FOR E…

Java回调机制解读

模块间调用 在一个应用系统中,无论使用何种语言开发,必然存在模块之间的调用,调用的方式分为几种: (1)同步调用 同步调用是最基本并且最简单的一种调用方式,类A的方法a()调用类B的方法b()&#…

Java TDD简介–第1部分

欢迎来到测试驱动开发 (TDD)系列的介绍。 我们将在TDD上下文中讨论Java和JUnit ,但这只是工具。 本文的主要目的是使您全面了解TDD,而无论使用哪种编程语言和测试框架。 如果您在项目中不使用TDD,那么您要么很懒&…

状态模式 处理订单状态_将状态机模式实现为流处理器

状态模式 处理订单状态在我的上一个博客中,我说过我真的以为某些“四人行”(GOF)模式已经过时了,如果不是过时的话肯定不受欢迎。 特别是我说过StateMachine没什么用,因为您通常会想到另一种更简单的方式来执行您正在执…

mysql 连续签到天数_最大连续签到天数-sql

SELECT MIN(rq) as 起始日期, MAX(rq) as 终止日期, MAX(id1) - MIN(id1) 1 as 持续天数,id3 as 累计签到天数,nameFROM (SELECT datediff(rq,2020-02-01 )id1, (SELECT COUNT(1)FROM tmptableWHERE rq < a.rq andtype 是) id2,(SELECT COUNT(1)FROM tmptableWHEREtype 是…