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

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

bba25072d4aacdb7d5463644348aaa0d.png

Tomcat的锁

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

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

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

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

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

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

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

140cd4f709fe1b4b3bbf3607f2ac2746.png

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

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

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

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

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

“如果抢不到怎么办?”

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

分布式的锁

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

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

3b7d4685c8f310ee3fe3b74a8e55dee5.png

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

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

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

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

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

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

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

7efd43b7e71dde073752ae257de00b00.png

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

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

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

  1. delete from locks where lock_name='stock'

c677aebc08719aa20df0ecaa460d5bf0.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, 谁先设置成功,谁就获得了锁,可以去扣减库存。”

bab2cc4ba388965e44a91d1bf3c84c9d.png

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

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

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

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

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

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

定期自动释放的问题

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

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

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

06fcc401d5cd7752cb35cd0af36e13f2.png

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

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

行锁

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

22500a7eae5b53c7c7755d8f6d5e224d.png

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

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

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

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

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

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

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

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

CAS

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

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

  1. #new_num = #old_num - 10
  2. 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的思想很重要。

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

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

相关文章

php解析js的 arraybuffer_JS的所谓的第七种数据类型Symbol

首先,为什么说叫所谓呢?因为在2007年之前Js给予我们typeof解析数据类型的一共有六种(一直有争议,但是我们暂时就按typeof来算)functionNumberObjectbooleanStringundefined但当我们去 typeof Symbol () 的时候,会惊奇的发现&#…

JAVA MYSQL从数据库中提取图片_java web将图片存到储数据库和从数据库中读取图片...

(Notice:以下所有经验也是我根据网上的经验整理的,如有侵权可以联系我删除,Wx:IT_Ezra,QQ 654303408。 有问题讨论也可联系我,QQ同上。)一、分析一下基本流程从前台页面获取图片,后台接收图片文件转化成数据…

Ubuntu系统如何安装和卸载CUDA和CUDNN

背景 最近在学习PaddlePaddle在各个显卡驱动版本的安装和使用,所以同时也学习如何在Ubuntu安装和卸载CUDA和CUDNN,在学习过程中,顺便记录学习过程。在供大家学习的同时,也在加强自己的记忆。本文章以卸载CUDA 8.0 和 CUDNN 7.05 …

session.merge 缓存不更新_如何保证缓存与数据库双写时的数据一致性?

在做系统优化时,想到了将数据进行分级存储的思路。因为在系统中会存在一些数据,有些数据的实时性要求不高,比如一些配置信息。基本上配置了很久才会变一次。而有一些数据实时性要求非常高,比如订单和流水的数据。所以这里根据数据…

java替换图片中文字_Java 添加、替换、删除Word中的图片

文档中,可以通过图文混排的方式来增加内容的可读性,相比纯文本文档,在内容展现方式上也更具美观性。在给文档添加图片时,可设置图片的文本环绕方式、旋转角度、图片高度/宽度等;另外,也可对文档中已有的图片…

kafka如何保证不重复消费又不丢失数据_Kafka写入的数据如何保证不丢失?

我们暂且不考虑写磁盘的具体过程,先大致看看下面的图,这代表了 Kafka 的核心架构原理。Kafka 分布式存储架构那么现在问题来了,如果每天产生几十 TB 的数据,难道都写一台机器的磁盘上吗?这明显是不靠谱的啊!所以说,这…

不允许输入特殊字符的正则表达式_JavaScript正则表达式常用技巧

正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript 中,正则表达式也是对象。这些模式被用于 RegExp 的 exec 和 test 方法, 以及 String 的 match、matchAll、replace、search 和 split 方法。正则表达式的掌握程度能粗略地看出程序员的技术底子&#xff…

latex 算法_GitHub项目awesome-latex-drawing新增内容(四):绘制贝叶斯网络

近期,我们整理和开源了一个基于LaTeX的科技绘图项目,并将其取名为awesome-latex-drawing(GitHub网址为:https://github.com/xinychen/awesome-latex-drawing),案例包括贝叶斯网络、图模型、矩阵/张量示意图…

python123动物重量排序_python进阶

面向对象oopclass Student(object):def __init__(self,name,score)self.name nameself.score scoredef print_score(self)print(%s: %s % (self.name,self.score))给对象发消息实际上就是调用对象对应的关联函数,我们称之为对象的方法(Method)。面向对象的程序写出…

mysql中的生日应该是什么类型_MySQL中的定点数类型

上一篇文章我们唠叨了浮点数,知道了浮点数存储小数是不精确的。本篇继续唠叨一下MySQL中的另一种存储小数的方式 —— 定点数。浮点数文章闪现:什么, 0.3 - 0.2 ≠ 0.1 ? 什么鬼定点数类型正因为用浮点数表示小数可能会有不精确的情况,在一些…

python怎么制作图像_python数字图像处理(5):图像的绘制

实际上前面我们就已经用到了图像的绘制,如:io.imshow(img)这一行代码的实质是利用matplotlib包对图片进行绘制,绘制成功后,返回一个matplotlib类型的数据。因此,我们也可以这样写:importmatplotlib.pyplot …

axios代理跨域 cli4_vuecli 3.0之跨域请求代理配置及axios路径配置 莫小龙

vue-cli 3.0之跨域请求代理配置及axios路径配置问题:在前后端分离的跨域请求中,报跨域问题配置:vue.config.js:module.exports {runtimeCompiler: true,publicPath: /, // 设置打包文件相对路径devServer: {// open: process.pla…

string转为char数组_StringBuilder的区别是什么?String是不可变?一点课堂(多岸学院)...

String和StringBuffer、StringBuilder的区别可变性简单的来说:String 类中使用 final 关键字字符数组保存字符串,private final char value[],所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuild…

python去年软件排行_2017年编程语言排行榜,Python位居榜首(C语言需求最大)

最近IEEE Spectrum 发布了编程语言交互式排行榜,为很多学习代码的朋友们详解各类代码语言的需求和占有率。为学习代码的朋友们能更加重视哪一种编程语言而有一个明确的方向。下面排行榜123网为你公布2017年编程语言排行榜,Python位居榜首(C语言需求最大)。2017年编程…

mysql test数据库_mysql数据库test

Re介绍一下CentOS下MySQL数据库的安装与配置方法MySQL数据库配置的具体步骤:1、编辑MySQL的配置文件,使用vi /etc/my.cnf[rootsample ~]# vi /etc/my.cnf  ← 编辑MySQL的配置文件[mysqld]datadir/var/lib/mysqlsocket/var/lib/mysql/mysql.sock# Defau…

mysql 升级 openssl_【1分钟教程】LNMP架构应用实战 Openssl升级操作

由于实际生产环境需求,需要将LNMP环境中的openssl版本升级至目前最新版本openssl-1.1.0c,这玩意升级还真的不是一般的麻烦,由于它与系统各种服务都有相关的联系,比如ssh服务等,因此,升级非常的繁琐,所以今天…

miui秒解bl锁_MIUI12解锁bl篇(原谅我的过失,接上篇文章)

求原谅真心求原谅由于我的疏忽,上期教程不完整,对大家造成不便在这里给大家真诚道歉!对不起!请收下我的膝盖!!!我的上个教程小米手机MIUI系统降级任意版本通用教程,MIUI12→MIUI9因为…

腐蚀rust服务器命令_【使用 Rust 写 Parser】2. 解析Redis协议

系列所有文章https://zhuanlan.zhihu.com/p/115017849​zhuanlan.zhihu.comhttps://zhuanlan.zhihu.com/p/139387293​zhuanlan.zhihu.comhttps://zhuanlan.zhihu.com/p/146455601​zhuanlan.zhihu.comhttps://zhuanlan.zhihu.com/p/186217695​zhuanlan.zhihu.com在基本熟悉 n…

python中dic_python之dic {字典}(重要指数*****)

1. 什么是字典{name: 汪峰, age: 18} 键:值 别的语言键值对数据键: 必须是可哈希(不可变的数据类型),并且是唯一的值: 任意可以保存任意类型的数据字典是无序的python3.6版本以上,默认定义了顺序,python3.5以下是随机显示不能进⾏切片⼯作. 它只能通过key来获取dict中的数据字典…

python装饰器带参数函数二阶导数公式_一文搞定Python装饰器,看完面试不再慌

本文始发于个人公众号:TechFlow,原创不易,求个关注今天是Python专题的第12篇文章,我们来看看Python装饰器。一段囧事差不多五年前面试的时候,我就领教过它的重要性。那时候我Python刚刚初学乍练,看完了廖雪…