欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

面試官:聊聊你對分布式鎖技術(shù)方案的理解

前言

由于在平時的工作中,線上服務(wù)器是分布式多臺部署的,經(jīng)常會面臨解決分布式場景下數(shù)據(jù)一致性的問題,那么就要利用分布式鎖來解決這些問題。

成都創(chuàng)新互聯(lián)長期為超過千家客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為武安企業(yè)提供專業(yè)的成都網(wǎng)站建設(shè)、做網(wǎng)站武安網(wǎng)站改版等技術(shù)服務(wù)。擁有10多年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。

第一步,自身的業(yè)務(wù)場景:

在我日常做的項目中,目前涉及了以下這些業(yè)務(wù)場景:

場景一:比如分配任務(wù)場景。在這個場景中,由于是公司的業(yè)務(wù)后臺系統(tǒng),主要是用于審核人員的審核工作,并發(fā)量并不是很高,而且任務(wù)的分配規(guī)則設(shè)計成了通過審核人員每次主動的請求拉取,然后服務(wù)端從任務(wù)池中隨機的選取任務(wù)進行分配。這個場景看到這里你會覺得比較單一,但是實際的分配過程中,由于涉及到了按用戶聚類的問題,所以要比我描述的復(fù)雜,但是這里為了說明問題,大家可以把問題簡單化理解。那么在使用過程中,主要是為了避免同一個任務(wù)同時被兩個審核人員獲取到的問題。我最終使用了基于數(shù)據(jù)庫資源表的分布式鎖來解決的問題。

場景二:比如支付場景。在這個場景中,我提供給用戶三個用于保護用戶隱私的手機號碼(這些號碼是從運營商處獲取的,和真實手機號碼看起來是一樣的),讓用戶選擇其中一個進行購買,用戶購買付款后,我需要將用戶選擇的號碼分配給用戶使用,同時也要將沒有選擇的釋放掉。在這個過程中,給用戶篩選的號碼要在一定時間內(nèi)(用戶篩選正常時間范圍內(nèi))讓當前用戶對這個產(chǎn)品具有獨占性,以便保證付款后是100%可以拿到;同時由于產(chǎn)品資源池的資源有限,還要保持資源的流動性,即不能讓資源長時間被某個用戶占用著。對于服務(wù)的設(shè)計目標,一期項目上線的時候至少能夠支持峰值qps為300的請求,同時在設(shè)計的過程中要考慮到用戶體驗的問題。我最終使用了memecahed的add()方法和基于數(shù)據(jù)庫資源表的分布式鎖來解決的問題。

場景三:我有一個數(shù)據(jù)服務(wù),每天調(diào)用量在3億,每天按86400秒計算的qps在4000左右,由于服務(wù)的白天調(diào)用量要明顯高于晚上,所以白天下午的峰值qps達到6000的,一共有4臺服務(wù)器,單臺qps要能達到3000以上。我最終使用了redis的setnx()和expire()的分布式鎖解決的問題。

場景四:場景一和場景二的升級版。在這個場景中,不涉及支付。但是由于資源分配一次過程中,需要保持涉及一致性的地方增加,而且一期的設(shè)計目標要達到峰值qps500,所以需要我們對場景進一步的優(yōu)化。我最終使用了redis的setnx()、expire()和基于數(shù)據(jù)庫表的分布式鎖來解決的問題。

看到這里,不管你覺得我提出的業(yè)務(wù)場景qps是否足夠大,都希望你能繼續(xù)看下去,因為無論你身處一個什么樣的公司,最開始的工作可能都需要從最簡單的做起。不要提阿里和騰訊的業(yè)務(wù)場景qps如何大,因為在這樣的大場景中你未必能親自參與項目,親自參與項目未必能是核心的設(shè)計者,是核心的設(shè)計者未必能獨自設(shè)計。如果能真能滿足以上三條,關(guān)閉頁面可以不看啦,如果不是的話,建議還是看完,我有說的不足的地方歡迎提出建議,我說的好的地方,也希望給我點個贊或者評論一下,算是對我最大的鼓勵哈。

第二步,分布式鎖的解決方式:

  1. 首先明確一點,有人可能會問是否可以考慮采用ReentrantLock來實現(xiàn),但是實際上去實現(xiàn)的時候是有問題的,ReentrantLock的lock和unlock要求必須是在同一線程進行,而分布式應(yīng)用中,lock和unlock是兩次不相關(guān)的請求,因此肯定不是同一線程,因此導(dǎo)致無法使用ReentrantLock。
  2. 基于數(shù)據(jù)庫表做樂觀鎖,用于分布式鎖。
  3. 使用memcached的add()方法,用于分布式鎖。
  4. 使用memcached的cas()方法,用于分布式鎖。(不常用)
  5. 使用redis的setnx()、expire()方法,用于分布式鎖。
  6. 使用redis的setnx()、get()、getset()方法,用于分布式鎖。
  7. 使用redis的watch、multi、exec命令,用于分布式鎖。(不常用)
  8. 使用zookeeper,用于分布式鎖。(不常用)

第三步,基于數(shù)據(jù)庫資源表做樂觀鎖,用于分布式鎖:

1. 首先說明樂觀鎖的含義:

大多數(shù)是基于數(shù)據(jù)版本(version)的記錄機制實現(xiàn)的。何謂數(shù)據(jù)版本號?即為數(shù)據(jù)增加一個版本標識,在基于數(shù)據(jù)庫表的版本解決方案中,一般是通過為數(shù)據(jù)庫表添加一個 “version”字段來實現(xiàn)讀取出數(shù)據(jù)時,將此版本號一同讀出,之后更新時,對此版本號加1。

在更新過程中,會對版本號進行比較,如果是一致的,沒有發(fā)生改變,則會成功執(zhí)行本次操作;如果版本號不一致,則會更新失敗。

2. 對樂觀鎖的含義有了一定的了解后,結(jié)合具體的例子,我們來推演下我們應(yīng)該怎么處理

(1). 假設(shè)我們有一張資源表,如下圖所示: t_resource , 其中有6個字段id, resoource, state, add_time, update_time, version,分別表示表主鍵、資源、分配狀態(tài)(1未分配 2已分配)、資源創(chuàng)建時間、資源更新時間、資源數(shù)據(jù)版本號。

面試官:聊聊你對分布式鎖技術(shù)方案的理解

(2). 假設(shè)我們現(xiàn)在我們對id=5780這條數(shù)據(jù)進行分配,那么非分布式場景的情況下,我們一般先查詢出來state=1(未分配)的數(shù)據(jù),然后從其中選取一條數(shù)據(jù)可以通過以下語句進行,如果可以更新成功,那么就說明已經(jīng)占用了這個資源。

(3). 如果在分布式場景中,由于數(shù)據(jù)庫的update操作是原子是原子的,其實上邊這條語句理論上也沒有問題,但是這條語句如果在典型的“ABA”情況下,我們是無法感知的。有人可能會問什么是“ABA”問題呢?大家可以網(wǎng)上搜索一下,這里我說簡單一點就是,如果在你第一次select和第二次update過程中,由于兩次操作是非原子的,所以這過程中,如果有一個線程,先是占用了資源(state=2),然后又釋放了資源(state=1),實際上最后你執(zhí)行update操作的時候,是無法知道這個資源發(fā)生過變化的。也許你會說這個在你說的場景中應(yīng)該也還好吧,但是在實際的使用過程中,比如銀行賬戶存款或者扣款的過程中,這種情況是比較恐怖的。

(4).那么如果使用樂觀鎖我們?nèi)绾谓鉀Q上邊的問題呢?

a. 先執(zhí)行select操作查詢當前數(shù)據(jù)的數(shù)據(jù)版本號,比如當前數(shù)據(jù)版本號是26:select id, resource, state,version from t_resource where state=1 andid=5780;

b. 執(zhí)行更新操作:update t_resoure set state=2, version=27, update_time=now() where resource=xxxxxx and state=1 and version=26

c. 如果上述update語句真正更新影響到了一行數(shù)據(jù),那就說明占位成功。如果沒有更新影響到一行數(shù)據(jù),則說明這個資源已經(jīng)被別人占位了。

3. 通過2中的講解,相信大家已經(jīng)對如何基于數(shù)據(jù)庫表做樂觀鎖有有了一定的了解了,但是這里還是需要說明一下基于數(shù)據(jù)庫表做樂觀鎖的一些缺點:

(1). 這種操作方式,使原本一次的update操作,必須變?yōu)?次操作: select版本號一次;update一次。增加了數(shù)據(jù)庫操作的次數(shù)。

(2). 如果業(yè)務(wù)場景中的一次業(yè)務(wù)流程中,多個資源都需要用保證數(shù)據(jù)一致性,那么如果全部使用基于數(shù)據(jù)庫資源表的樂觀鎖,就要讓每個資源都有一張資源表,這個在實際使用場景中肯定是無法滿足的。而且這些都基于數(shù)據(jù)庫操作,在高并發(fā)的要求下,對數(shù)據(jù)庫連接的開銷一定是無法忍受的。

(3). 樂觀鎖機制往往基于系統(tǒng)中的數(shù)據(jù)存儲邏輯,因此可能會造成臟數(shù)據(jù)被更新到數(shù)據(jù)庫中。在系統(tǒng)設(shè)計階段,我們應(yīng)該充分考慮到這些情況出現(xiàn)的可能性,并進行相應(yīng)調(diào)整,如將樂觀鎖策略在數(shù)據(jù)庫存儲過程中實現(xiàn),對外只開放基于此存儲過程的數(shù)據(jù)更新途徑,而不是將數(shù)據(jù)庫表直接對外公開。

4.講了樂觀鎖的實現(xiàn)方式和缺點,是不是會覺得不敢使用樂觀鎖了呢???

當然不是,在文章開頭我自己的業(yè)務(wù)場景中,場景1和場景2的一部分都使用了基于數(shù)據(jù)庫資源表的樂觀鎖,已經(jīng)很好的解決了線上問題。所以大家要根據(jù)的具體業(yè)務(wù)場景選擇技術(shù)方案,并不是隨便找一個足夠復(fù)雜、足夠新潮的技術(shù)方案來解決業(yè)務(wù)問題就是好方案?!比如,如果在我的場景一中,我使用zookeeper做鎖,可以這么做,但是真的有必要嗎???答案覺得是沒有必要的?。。?/p>

第四步,使用memcached的add()方法,用于分布式鎖:

對于使用memcached的add()方法做分布式鎖,這個在互聯(lián)網(wǎng)公司是一種比較常見的方式,而且基本上可以解決自己手頭上的大部分應(yīng)用場景。在使用這個方法之前,只要能搞明白memcached的add()和set()的區(qū)別,并且知道為什么能用add()方法做分布式鎖就好。如果還不知道add()和set()方法,請直接百度吧,這個需要自己了解一下。

我在這里想說明的是另外一個問題,人們在關(guān)注分布式鎖設(shè)計的好壞時,還會重點關(guān)注這樣一個問題,那就是是否可以避免死鎖問題????。。?/p>

如果使用memcached的add()命令對資源占位成功了,那么是不是就完事兒了呢?當然不是!我們需要在add()的使用指定當前添加的這個key的有效時間,如果不指定有效時間,正常情況下,你可以在執(zhí)行完自己的業(yè)務(wù)后,使用delete方法將這個key刪除掉,也就是釋放了占用的資源。但是,如果在占位成功后,memecached或者自己的業(yè)務(wù)服務(wù)器發(fā)生宕機了,那么這個資源將無法得到釋放。所以通過對key設(shè)置超時時間,即便發(fā)生了宕機的情況,也不會將資源一直占用,可以避免死鎖的問題。

第五步,使用memcached的cas()方法,用于分布式鎖: 略

第六步,使用redis的setnx()、expire()方法,用于分布式鎖:

對于使用redis的setnx()、expire()來實現(xiàn)分布式鎖,這個方案相對于memcached()的add()方案,redis占優(yōu)勢的是,其支持的數(shù)據(jù)類型更多,而memcached只支持String一種數(shù)據(jù)類型。除此之外,無論是從性能上來說,還是操作方便性來說,其實都沒有太多的差異,完全看你的選擇,比如公司中用哪個比較多,你就可以用哪個。

首先說明一下setnx()命令,setnx的含義就是SET if Not Exists,其主要有兩個參數(shù) setnx(key, value)。該方法是原子的,如果key不存在,則設(shè)置當前key成功,返回1;如果當前key已經(jīng)存在,則設(shè)置當前key失敗,返回0。但是要注意的是setnx命令不能設(shè)置key的超時時間,只能通過expire()來對key設(shè)置。

具體的使用步驟如下:

  1. setnx(lockkey, 1) 如果返回0,則說明占位失?。蝗绻祷?,則說明占位成功
  2. expire()命令對lockkey設(shè)置超時時間,為的是避免死鎖問題。
  3. 執(zhí)行完業(yè)務(wù)代碼后,可以通過delete命令刪除key。
    這個方案其實是可以解決日常工作中的需求的,但從技術(shù)方案的探討上來說,可能還有一些可以完善的地方。比如,如果在第一步setnx執(zhí)行成功后,在expire()命令執(zhí)行成功前,發(fā)生了宕機的現(xiàn)象,那么就依然會出現(xiàn)死鎖的問題,所以如果要對其進行完善的話,可以使用redis的setnx()、get()和getset()方法來實現(xiàn)分布式鎖。

第七步,使用redis的setnx()、get()、getset()方法,用于分布式鎖:

這個方案的背景主要是在setnx()和expire()的方案上針對可能存在的死鎖問題,做了一版優(yōu)化。

那么先說明一下這三個命令,對于setnx()和get()這兩個命令,相信不用再多說什么。那么getset()命令?這個命令主要有兩個參數(shù)getset(key,newValue)。該方法是原子的,對key設(shè)置newValue這個值,并且返回key原來的舊值。假設(shè)key原來是不存在的,那么多次執(zhí)行這個命令,會出現(xiàn)下邊的效果:

  1. getset(key, "value1") 返回nil 此時key的值會被設(shè)置為value1
  2. getset(key, "value2") 返回value1 此時key的值會被設(shè)置為value2
  3. 依次類推!

介紹完要使用的命令后,具體的使用步驟如下:

  1. setnx(lockkey, 當前時間+過期超時時間),如果返回1,則獲取鎖成功;
    如果返回0則沒有獲取到鎖,轉(zhuǎn)向2。
  2. get(lockkey)獲取值oldExpireTime ,并將這個value值與當前的系統(tǒng)時間進行比較,如果小于當前系統(tǒng)時間,則認為這個鎖已經(jīng)超時,可以允許別的請求重新獲取,轉(zhuǎn)向3。
  3. 計算newExpireTime=當前時間+過期超時時間,然后getset(lockkey, newExpireTime) 會返回當前l(fā)ockkey的值currentExpireTime。
  4. 判斷currentExpireTime與oldExpireTime 是否相等,如果相等,說明當前getset設(shè)置成功,獲取到了鎖。如果不相等,說明這個鎖又被別的請求獲取走了,那么當前請求可以直接返回失敗,或者繼續(xù)重試。
  5. 在獲取到鎖之后,當前線程可以開始自己的業(yè)務(wù)處理,當處理完畢后,比較自己的處理時間和對于鎖設(shè)置的超時時間,如果小于鎖設(shè)置的超時時間,則直接執(zhí)行delete釋放鎖;如果大于鎖設(shè)置的超時時間,則不需要再鎖進行處理。

注意:這個方案我當初在線上使用的時候是沒有問題的,所以當初寫這篇文章時也認為是沒有問題的。但是截止到2017.05.13(周六),自己在重新回顧這篇文章時,看了文章下網(wǎng)友的很多評論,我發(fā)現(xiàn)有兩個問題比較集中:

問題1:在“get(lockkey)獲取值oldExpireTime”這個操作與“getset(lockkey, newExpireTime)”這個操作之間,如果有N個線程在get操作獲取到相同的oldExpireTime后,然后都去getset,會不會返回的newExpireTime都是一樣的,都會是成功,進而都獲取到鎖???

我認為這套方案是不存在這個問題的。依據(jù)有兩條: 第一,redis是單進程單線程模式,串行執(zhí)行命令。 第二,在串行執(zhí)行的前提條件下,getset之后會比較返回的currentExpireTime與oldExpireTime 是否相等。

問題2:在“get(lockkey)獲取值oldExpireTime”這個操作與“getset(lockkey, newExpireTime)”這個操作之間,如果有N個線程在get操作獲取到相同的oldExpireTime后,然后都去getset,假設(shè)第1個線程獲取鎖成功,其他鎖獲取失敗,但是獲取鎖失敗的線程它發(fā)起的getset命令確實執(zhí)行了,這樣會不會造成第一個獲取鎖的線程設(shè)置的鎖超時時間一直在延長???

我認為這套方案確實存在這個問題的可能。但我個人認為這個微笑的誤差是可以忽略的,不過技術(shù)方案上存在缺陷,大家可以自行抉擇哈。

第八步,使用redis的watch、multi、exec命令,用于分布式鎖:

第九步,使用zookeeper,用于分布式鎖:

第十步,總結(jié)

綜上,關(guān)于分布式鎖的第一篇文章我就寫到這兒了,在文章中主要說明了日常項目中會比較常用到四種方案,大家掌握了這四種方案,其實在日常的工作中就可以解決很多業(yè)務(wù)場景下的分布式鎖的問題。從文章開頭我自己的實際使用中,也可以看到,這么說完全是有一定的依據(jù)。對于另外那三種方案,我會在下一篇關(guān)于分布式鎖的文章中,和大家再探討一下。

常用的四種方案:

  1. 基于數(shù)據(jù)庫表做樂觀鎖,用于分布式鎖。
  2. 使用memcached的add()方法,用于分布式鎖。
  3. 使用redis的setnx()、expire()方法,用于分布式鎖。
  4. 使用redis的setnx()、get()、getset()方法,用于分布式鎖。

不常用但是可以用于技術(shù)方案探討的:

  1. 使用memcached的cas()方法,用于分布式鎖。
  2. 使用redis的watch、multi、exec命令,用于分布式鎖。
  3. 使用zookeeper,用于分布式鎖。

寫在最后

原文鏈接:https://shimo.im/docs/f2ajdNJBQJItSobT/

面試官:聊聊你對分布式鎖技術(shù)方案的理解

當前名稱:面試官:聊聊你對分布式鎖技術(shù)方案的理解
標題鏈接:http://aaarwkj.com/article8/jjgpip.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App設(shè)計、網(wǎng)站維護、電子商務(wù)域名注冊、網(wǎng)站排名品牌網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)公司
久久热这里只有精品网站| 99热视频这里只有精品| 丰满人妻被黑人猛烈进入免费| 中文字幕一区二区av| 粉嫩极品美女国产精品| 日本91一区二区不卡| 亚洲免费av一区二区| 色哟哟免费在线观看视频| 激情小说婷婷亚洲综合| 久久久国产一区二区三区| 国内成人午夜激情视频| 国产婷婷综合一区二区| 亚洲一区精品中文字幕| 18禁黄网站禁片免费视频| 亚洲欧美国产精品日韩| 欧美日韩亚洲1区2区| 国产91极品尤物白丝美女| 麻豆精品国产粉嫩av| 91美女黑丝免费国产视频| 91精品人妻一区二区三区| 欧美日本在线区一区二| 国产精品 亚洲精品| 中文字幕日本专区人妻| 欧美老熟妇精品一区二区| 人妻av天堂综合一区| 国产成人久久精品二区三区| 亚洲熟女乱妇爽到高潮| 18岁下禁止看的视频| 亚洲婷婷综合久久一区二区| av在线观看亚洲天堂| 亚洲偷拍自拍在线观看| 欧美+亚洲+精品+三区| 亚洲精品第一国产综合| 99热成人精品热久久| 日本道二区视频中文字幕| 亚洲精品在线免费av| 91精品人妻互换一区二区| 九九在线免费视频蜜臀| 国产精品白浆大屁股一区二区三| 日韩 在线一区二区| 一本色道久久亚洲综合精品蜜桃|