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.pswp.cn/news/532079.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/532079.shtml
英文地址,請注明出處:http://en.pswp.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剛剛初學乍練,看完了廖雪…