别吵吵,分布式锁也是锁

pcb印刷电路缺陷检测----数据集准备 PKU-Market-PCB 印刷电路板 (PCB) 瑕疵数据集是一个公共合成 PCB 数据集,包含 1,386 张图像,具有 6 种缺陷(漏孔、鼠咬、开路、短路、杂散、杂铜),可用于图像检测、分类和配准任务。 阅读详情

640?wx_fmt=jpeg

Tomcat的锁

Tomcat是这个系统的核心组成部分, 每当有用户请求过来,Tomcat就会从线程池里找个线程来处理,有的执行登录,有的查看购物车,有的下订单,看着属下们尽心尽职地工作,完成人类的请求,Tomcat就很有成就感。 

与此同时,它也很得意,所有的业务逻辑尽在掌握。MySQL算啥!不就是一个保存数据的地方吗? Redis算啥!不就是一个加快速度的缓存吗?   

没有他们,我也能找到替代品,而我不可替代的, Tomcat经常这么想。 

昨天MySQL偶然说起隔壁机器入驻了一个叫做Node.js的家伙,居然只用一个线程来执行JavaScript代码,实现各种业务逻辑,JavaScript也能到后端来?还用回调? 这不是胡闹吗?不过得小心,别被他把业务都给抢走了。  

想到此处,Tomcat立刻去查看各个线程活干得怎么样,有没有人故意偷懒。 

线程0x9527和0x7954又在吵架了,原因非常简单,他们俩都去做扣减库存的操作:读取库存,修改库存,写回数据库。 


线程的并发执行导致三个操作交织在了一起,最后数据出现了不一致。 

640?wx_fmt=png

Tomcat说:“你们怎么搞的,为什么要把库存读出来,直接update 库存不行吗? 让MySQL老头儿去保证正确性。要学会甩锅啊!” 

0x7954回答道:“没办法,张大胖的代码就是这么写的,好像是业务要求的,扣减库存之前要检查库存够不够。”  

Tomcat一阵牙疼, 不由得想起了Redis的处理办法, 对于每个读写缓存的请求,Redis都把他们给排成了队,用一个线程挨个去处理,肯定没有这个并发的问题了。  

可是自己这里不行啊,访问数据库是极慢的操作,如果只用一个线程,一个个地处理请求,所有的请求都得等待,人类会急死的。 

没办法,Tomcat扔给他们俩一个Java对象:“这是一把锁,以后谁先抢到谁才能执行扣减库存的三个操作。” 

“如果抢不到怎么办?” 

“阻塞等待,别人释放了锁,JVM自然会唤醒你,然后再去抢! 什么时候抢到,什么时候执行。”

分布式的锁

张大胖觉得有点不对劲, 这几天程序执行怎么有点儿慢了呢?  

他还以为是机器性能不够,就申请了几台新机器,又安装了几个Tomcat,组成了一个集群。 

640?wx_fmt=png

这下可好,三个Tomcat, 每个Tomcat都有一把锁来控制对库存的访问。 

在Tomcat这个JVM进程内部,同一个时刻只有一个幸运儿线程可以扣减库存,可是现在有三个Tomcat,出现了三个幸运儿。 

这三个幸运儿在扣减库存的时候,仍然会出现0x7954和0x9527那样的错误,只不过现在他们互不知晓,连吵架的机会都没有了。 

三个Tomcat都觉得头大,在这个分布式的环境中,多个进程在运行,原来那种进程内的锁已经失效,当务之急是找一个客观、公正、独立的第三方来实现锁的功能。 

MySQL提议: “到我这里来找锁啊!”

“你那里能提供一个锁服务? 暴露出来让我们使用? ” Tomcat A问道。

“不不,不是一个锁服务,我给你们一个数据库表,这个表中的字段lock_name有个唯一性约束。”

640?wx_fmt=png

“你的意思是,我们的线程每次想获得锁的时候,都去数据库插入一条数据? ”  Tomcat A 反映很快。 

insert into locks(lock_name,...) values('stock',...);

“对啊,我的唯一性约束只能保证一个成功,其他的都失败,就相当于获得锁了。 当然那个线程的操作完成以后,需要释放锁。”

delete from locks where lock_name='stock' 

640?wx_fmt=png

这倒是一个简单的办法, 但也是一个重量级的办法:每次获得锁都得访问一次数据库! 

假设来自TomcatA的0x9527捷足先登,插入了一条数据,获得了锁, 那来自Tomcat B的0x7954操作肯定失败,这时候0x7954该怎么办? 能阻塞等待TomcatB来唤醒他吗? 不行,因为连TomcatB 都不知道0x9527什么时候操作完成, 除非MySQL来通知各个Tomcat, 这是肯定不行的。

那0x7954@TomcatB只能做一件事情: 等待一会儿,然后重试! 如此循环下去,直到获得锁为止。 

可是如果0x9527获得了锁,在执行的过程中TomcatA 挂掉了,那数据库记录一直存在,无人删除,那锁就永远也无法释放了! 还得弄一个清理者, 清理那些过期没释放的锁, 这实在是太麻烦了。 

Redis

这时候Redis说道:“千万别上MySQL的贼船!他的办法太笨重了,不就是找个第三方来保存锁的信息吗? 用我的缓存多好!” 

“Redis这小子操作的是内存,速度会快很多!” Tomcat B说道。 

 “对,MySQL不是给你们提供了一张表让你们插入数据吗? 我这里不用那么麻烦,你们Tomcat的线程,都可以尝试到我的缓存中设置一个值,比如stock_lock=true, 谁先设置成功,谁就获得了锁,可以去扣减库存。” 

640?wx_fmt=png

“ 如果有多个线程去设置,你能保证只有一个成功,别的都失败吗? ” 

 Redis拍拍胸脯: “绝对保证!” 

(码农翻身老刘注:其实就是setnx命令了) 

MySQL撇撇嘴:“和我的方案本质上是一样的。人家Tomcat 的线程对库存做了修改以后,也还得去解锁,去删除这个stock_lock。” 

Redis说:“我这里还能设置过期时间,如果Tomcat A上线程获得了锁,然后Tomcat A挂掉了, 到了过期时间,我就可以自动把这个stock_lock删除,别的线程又可以获得锁了!” 

“嗯,是比MySQL先进,并且速度更快,我们还是用这个锁吧。” 三个Tomcat都表示同意。 

定期自动释放的问题

“且慢,这个自动删除过期的锁有问题啊 !” MySQL突然反击。 

“什么问题?”  Redis没想到数据库老头儿还想负隅顽抗。 


“假设Tomcat A上的0x9527获得了锁, 去执行扣减库存的操作,然后由于某种原因被阻塞了,阻塞的时间超过了过期时间,锁被你释放掉了,最终还是会出现不一致!”

640?wx_fmt=png

“你这是吹毛求疵,绝对是小概率事件!” Redis叫道!

“再说了,用你的数据库方案,也得定期清理那些锁,道理是一样的。” 

行锁

第二天, MySQL高兴得去找Tomcat:“兄弟们,我昨天晚上和Quartz(一个著名的定时执行框架)聊了半宿,他告诉了我一个新的用数据库实现分布式锁的办法, 行锁。”

640?wx_fmt=png

 “看到没有, 通过添加一个for udpate ,这个SQL语句会把这一行给锁定,就是获得了锁! 只要事务一提交,这个行锁就自动释放了。”  

“那没有获得锁的别的线程呢? ”

“自然是阻塞住了,等到别的线程释放了行锁,它可以自动去获取,代码中都不用循环重试,你看,之前的方案都做不到这一点吧。” MySQL说道。 

“那要是有个线程迟迟不释放行锁,会发生什么问题?” Tomcat最关心这个。

“那其他线程都会等待,并且占用着数据库连接不释放,嗯,如果连接被占用得过多,连接池就要出问题了......” MySQL底气不足了,这可是个致命的问题。

“哈哈,看你出的什么馊注意!还是用我的锁吧!” Redis笑道。

“那人家Quartz为什么可以用?”MySQL不死心。

“估计Quartz业务单一,并且锁释放得很快,不会出问题吧。” 

CAS

正在这时,Node.js悄悄地走过来, 把数据库老头儿拉走了:“前辈,别给他们一般见识,不就是扣减库存吗,用啥分布式锁!, 咱们这么做:”  

#old_num = 先获取现有的库存数量 

#new_num = #old_num - 10  

update stock set stock_num = #new_num where product_id=#product_id and stock_num = #old_num

MySQL眼前一亮, 是啊,每次把这个#old_num 作为条件传进去调用update语句,如果能成功,说明在这段时间内没有别的线程更新库存; 

如果不成功,那就重新执行这三条语句,直到成功为止, 就这个么简单, 完全不用锁,真是太爽了。 

过了几天,Tomcat他们也听说了这个方案, 惊讶地说:“这不就是我们Java常用的Compare And Set(CAS)吗?” 

总结

与此同时,张大胖开始做总结:分布式锁和进程内的锁本质上是一样的。 

1.  要互斥,同一时刻只能被一台机器上的一个线程获得。 

2.  最好支持阻塞,然后唤醒,这样那些等待的线程不用循环重试。 

3.  最好可以重入(本文没有涉及,参见《编程世界的那把锁》) 

4.  获得锁和释放锁速度要快 

5.  对于分布式锁,需要找到一个集中的“地方”(数据库,Redis, Zookeeper等)来保存锁,这个地方最好是高可用的。 

6. 考虑到“不可靠的”分布式环境, 分布式锁需要设定过期时间 

7.  CAS的思想很重要。  

往期精彩回顾

我是一个线程

我是一个Java Class

TCP/IP之大明邮差

CPU阿甘

我是一个网卡

我是一个路由器

一个故事讲完HTTPs

编程语言的巅峰

面向对象圣经

加锁还是不加锁,这是一个问题

一个著名的任务调度系统是怎么设计的?

Java:一个帝国的诞生

JavaScript:一个屌丝的逆袭

PX4飞控CAN总线通信深度配置:从menuconfig到驱动加载的完整指南 本文详细解析了PX4飞控中CAN总线通信的深度配置流程,从menuconfig选项设置到驱动加载的完整指南。涵盖硬件抽象层、驱动层和应用层的配置细节,提供实际验证方法和故障排查技巧,帮助开发者高效实现CAN通信,特别适用于无人机和机器人底盘运动控制等场景。 阅读详情

相关推荐

【RViz2】导入urdf模型时报错:Could not load resource xxx,Unable to open file xxx,Error retrieving file xxx

初学ROS2,想要在RViz2中显示urdf模型。其中.urdf文件引用了外部的meshes文件夹。

qq_43557907的博客 9488

你用对锁了吗?浅谈 Java “锁” 事

每个时代,都不会亏待会学习的人大家好,我是yes。本来打算继续写消息队列的东西的,但是最近在带新同事,发现新同事对于锁这方面有一些误解,所以今天就来谈谈“锁”事和 Java 中的并发安全...

架构师小秘圈 360

从数学到机床:RTCP背后的几何变换与工业实现

本文深入解析RTCP(Rotary Tool Center Point)技术在五轴数控加工中的核心作用,通过几何变换与运动学原理实现刀尖点跟随,确保加工精度。从数学基础到工业实现,详细探讨了算法架构、机床结构适配及精度保障方法,为数控系统开发和高效加工提供关键技术支撑。

jump7的博客 258

java队列_java里的管程Monitor

什么是管程管程是一个抽象的概念模型,其封装了一套对共享资源访问的模型,目的是通过一个模型来管理共享资源的访问过程,让可能存在多个进程或线程同时访问一个共享资源时能达到"互斥"和"同步"的效果,管程实现管程模型必须达到下面两点要求1、管程中的共享变量对于外部都是不可见的,只能通过管程才能访问对应的共享资源。2、管程是互斥的,某个时刻只能允许一个进程或线程访问共享资源。3、管程中需要有线程等待队列和相...

weixin_39615643的博客 247

FPGA从入门到精通(2) - LUT

LUT:(look up table) 查找表 今天我要讲的与FPGA CLB相关的第一节LUT。根据上一节课的课程大纲,讲CLB应该是讲SLICEL(SLICEM)。不过当我打开用户手册,看了一下SLICE的结构(图一),一眼望去,这是分分钟会劝退入门者。为了让读者更好的接受以及理解,所以我打算先讲清楚它里面的每一个基本单元(LUT, 进位链,MUX,寄存器,几乎所有的FPGA器件的SLICE都是这四大件组成,只是组合不太一样而已),然后再将他们联系在一起,和读者一步步推倒出SLICE的结构。 图1​

Jasper灬兰的博客 1万+

从入门到精通:dirmap 高级 Web 目录扫描实战指南

本文提供了一份从环境搭建到高级配置的dirmap Web目录扫描实战指南。详细解析了工具安装、核心配置文件dirmap.conf的深度调优,包括递归扫描、四种扫描模式(字典、爆破、爬虫、Fuzz)的实战应用,并演示了分阶段资产发现的完整工作流程,帮助安全人员高效发现隐藏目录与敏感文件。

rnn9storyteller的博客 481

死锁的产生原因解决方法

死锁的产生原因 1.竞争不可抢占性资源 如果 p1已经打开F1,现在想去打开F2,p2已经打开F2,现在想去打开F1,但是F1和F2都是不可抢占的,这是发生死锁。 2.竞争可消耗资源引起死锁 进程间通信,如果顺序不当,会产生死锁,比如p1发消息m1给p2,p1接收p3的消息m3,p2接收p1的m1,发m2给p3,p3,以此类推,如果进程之间是先发信息的那么可以完成通信,但是如果是先接收信息就会产生...

weixin_44473895的博客 330

麒麟操作系统安装达梦数据库ARM版

Linux安装达梦数据库ARM版

运维@小兵的博客 8769

通过一个简单的java示例,来学习解决“线程不安全”的思路

一、什么是线程安全? 线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。多个线程同时运行同一段代码,如果每次运行结果和单线程运行的结果是一样的,结果和预期相同,就是线程安全的,反之则是不安全的。 二、解决线程安全 先举个线程不安全的示例: 场景:商品抢购(抢购就是多线程,并发请求) 示例:假设在抢购场景中,我们一共只有10个商品,在最后一刻,我们已经卖出了9个商品

美奇软件开发工作室 4383

《SOC架构-ARM架构-文档导读》-目录

小白请看“Armv8/Armv9架构从入门到精通”专栏。以翻译为主的,深度深度深度深度/深入深入深入 学习ARM架构的文章。

baron-周贺贺-代码改变世界ctw 2085

使用 Python 调用 GitHub REST API 爬取 GitHub 仓库信息(完整指南)

GitHub 提供了功能强大的 REST API,允许开发者通过编程方式访问 GitHub 上的仓库、用户、提交记录、PR(Pull Request)等信息。GitHub REST API 是 GitHub 官方提供的 API 接口,支持通过 HTTP 请求来操作 GitHub 上的资源。在本文中,我们将使用 Python 编写一个完整的爬虫程序,调用 GitHub 的 REST API 来爬取 GitHub 仓库的信息。目前 GitHub 使用的主要是 v3 版本的 REST API。

2201_76125261的博客 1111

什么是死锁及死锁的必要条件和解决方法【转】

进程死锁及解决办法操作系统2009-09-24 16:48:58阅读767评论1 字号:大中小订阅 一、要点提示(1) 掌握死锁的概念和产生死锁的根本原因。(2) 理解产生死锁的必要条件--以下四个条件同时具备:互斥条件、不可抢占条件、占有且申请条件、循环等待条件。(3) 记住解决死锁的一般方法,掌握死锁的预防和死锁的避免二者的基本思想。(4) 掌握死锁的预防策略中资源有序...

weixin_34209851的博客 840

【WorkBuddy专栏61】字节的AI打法为什么一直在变——WorkBuddy/CodeBuddy vs 字节Trae/TRAE Work/豆包全维度对比

【WorkBuddy专栏61】字节的AI打法为什么一直在变——WorkBuddy/CodeBuddy vs 字节Trae/TRAE Work/豆包全维度对比

AI人人懂 2757

mysql 查看锁_别吵吵,分布式锁也是锁

Tomcat是这个系统的核心组成部分, 每当有用户请求过来,Tomcat就会从线程池里找个线程来处理,有的执行登录,有的查看购物车,有的下订单,看着属下们尽心尽职地工作,完成人类的请求,Tomcat就很有成就感。Tomcat的锁Tomcat是这个系统的核心组成部分, 每当有用户请求过来,Tomcat就会从线程池里找个线程来处理,有的执行登录,有的查看购物车,有的下订单,看着属下们尽心尽职地工作,完...

weixin_39657094的博客 57

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

快速深入理解zookeeper特性及核心基本原理

下次你再听到ZooKeeper,脑子里就立刻浮现出这个“绝不出错的共享备忘录”,上面有人用铅笔写字,有人用钢笔写字,还有一堆人盯着看,等着名字被擦掉的那一刻。想象一下,你有一个团队,团队成员分散在全国各地远程办公。这个形象,就是你对ZooKeeper最深刻、最彻底的理解!它们之间通过神奇的心灵感应保持绝对同步。现在,请你忘掉所有技术细节,只记住这个。为了解决这个问题,你们决定使用一个。服务(这就是ZooKeeper)。:这个备忘录不是一篇文章,而是一个。:它在全球有3、5、7个。

随记 424

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

架构师一口气说透分布式数据一致性问题

这是我的第42篇原创今天聊聊分布式环境下数据一致性的各种解决方案。有点烧脑,但是很有趣。单体环境中的一致性问题在传统关系型数据库环境(单体环境,或者叫单机)中,自己记录自己的就行了,单机环...

weixin_52346300的博客 251

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

怎么保证读取最新数据_架构师一口气说透分布式数据一致性问题

这是我的第42篇原创今天聊聊分布式环境下数据一致性的各种解决方案。有点烧脑,但是很有趣。单体环境中的一致性问题在传统关系型数据库环境(单体环境,或者叫单机)中,自己记录自己的就行了,单机环境不存在环境数据一致性的问题,倒是有并发操作数据一致性的问题。对于一个数据库操作(专业术语叫:事务)来说,总结出4个特性:原子性、一致性、隔离性和持久性,合称ACID,我们不需要记那么多,只需要知道这些...

weixin_40000999的博客 284

JAVA常用锁

java常用锁

weixin_41087124的博客 644

分布式锁

以下内容纯粹是个人理解,并不一定正确。在网上没有搜到特别满意的资料,先总结一下,记录下来,日后再出发。 一、什么是分布式锁 “分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。” 这是百度百...

左直拳的马桶_日用桶 755

Redis分布式锁

但是一旦持有锁的服务器挂掉,就会导致setNX里面的键一直有值,使得其他服务器无法正常请求,造成死锁。与分布式锁相对的是单机锁,单机锁是指在单个计算机或服务器上使用的锁机制,避免同时操作一个共享变量产生数据问题,用于控制多个线程或进程对共享资源的访问。但问题来了,如果是集群,每一个实例都会有一个自己的JVM运行环境,因此即便是同一个用户,如果并发的发起了多个请求,由于。此时锁给了线程2,等线程1结束释放锁,释放的就是线程2的锁。多服务器-------------加锁(分布式锁)这样的锁就是分布式锁。

我亦无他,唯手熟尔 656

java实现行级锁表级锁

Java实现行级锁和表级锁 在数据库中,锁是一种用来控制并发访问的机制,可以防止多个用户同时修改同一行或同一张表的数据。在Java程序中,我们可以通过使用行级锁和表级锁来实现对数据库的并发控制。 行级锁 行级锁是对数据库中的某一行数据进行加锁,只有拥有锁的事务才能对该行数据进行修改。在Java中,我们可以使用JDBC来实现...

weixin_40647721的博客 461

MySQL的锁机制,Java中关于表锁和行锁的使用

数据库提供了行级锁和表级锁来管理并发访问,在Java程序中,可以通过数据库的锁机制来控制对表的并发访问。数据库锁机制需要配合事务一起使用,锁的释放依赖事务的结束,如commit或者rollback。表级锁是一种数据库级别的锁机制,属于一种悲观锁机制,它要求在同一时刻只允许一个事务对整张表进行修改。通过LOCK TABLE语句锁定整个表,避免多个事务并发对表中的数据进行修改。

优快云_YYD_997的博客 1477

java 行锁_用java实现一个行锁(RowLock)

java 版本的数据库行锁,使用wait/notify实现,当然可以使用别的方式如Lock下的await/signal需求使用java写一个类,这个类有一个lock(String identifier)方法,用于传入一个唯一的标识标识数据库的某一行或者某个集合的某一条记录,当上锁之后,如果不适用unLock(String identifier)方法解锁,那么用于就不能访问这一条数据直接上代码:pa...

weixin_35789885的博客 2417

java行锁表锁_java_行级锁

悲观锁(行级锁):事务必须排队执行 数据按住了 不允许并发(行级锁:select后面添加for update)乐观锁: 支持并发 事务也不需要排队 只不过需要一个版本号开启俩个程序进行演示1.程序01启动后开启悲观锁进行查询 在没有提交之前 02会被卡住不能查询2.等待程序01commit完成后,程序02才能继续执行public class lockTest01 {public static vo...

weixin_29547309的博客 1134

java中行锁的使用

Java中行锁的使用 作为一名刚入行的开发者,你可能对Java中的并发控制不太熟悉。在Java中,行锁是一种用于控制对共享资源(如数据库表的行)的访问的同步机制。本文将指导你如何实现Java中的行锁。 行锁实现流程 以下是实现行锁的步骤: 步骤 描述 1 引入必要的库 2 创建锁对象 3 在访问共享资...

weixin_41423801的博客 156

MySql悲观锁(行锁)和乐观锁

一、悲观锁VS乐观锁 引言 为什么需要锁(并发控制) 在并发的环境中,会存在多个用户同时更新同一条数据,这时就会产生冲突。 冲突结果: 丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。 脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。 因此为了解决上述问题,引入了并发控制机制。 乐观锁(乐观并发控制)和悲观锁(悲观并发控制)是并发控制的主要手段,其实不仅关系型数据库中有乐观锁和悲观锁的概念,像redis,memcached等都有类似的概念。所以,不要把乐观.

Java后端技术栈 3万+
上一篇: 你从未听说过的最重要的数据库,人类登月计划的功臣
下一篇: 网络创世纪
码农翻身
码农翻身 《码农翻身》作者 《码农翻身》作者
博客等级 码龄8年 3万+粉丝 1084原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值