大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

面試題首頁(yè) > Java分布式面試題

分布式鎖面試題

001有哪些方案實(shí)現(xiàn)分布式鎖?

1)基于數(shù)據(jù)庫(kù)做分布式鎖--樂(lè)觀鎖(基于版本號(hào))和悲觀鎖(基于排它鎖) 
2)基于redis做分布式鎖:setnx(key,當(dāng)前時(shí)間+過(guò)期時(shí)間)和Redlock機(jī)制 
3)基于zookeeper做分布式鎖:臨時(shí)有序節(jié)點(diǎn)來(lái)實(shí)現(xiàn)的分布式鎖,Curator 
4)基于 Consul 做分布式鎖 

002MySQL如何做分布式鎖?

基于數(shù)據(jù)庫(kù)(MySQL)的分布式鎖方案,一般分為3類:基于表記錄、樂(lè)觀鎖和悲觀鎖。

003基于表記錄的數(shù)據(jù)庫(kù)鎖的原理。

該方法是最簡(jiǎn)單的,就是直接創(chuàng)建一張鎖表。當(dāng)我們想要獲得鎖的時(shí)候,就可以在該鎖表中增加一條記錄,想要釋放鎖的時(shí)候就刪除鎖表的這條記錄。
總結(jié):
1.這種鎖沒(méi)有失效時(shí)間,一旦釋放鎖操作失敗就會(huì)導(dǎo)致鎖記錄一直在數(shù)據(jù)庫(kù)中,其它線程無(wú)法獲得鎖。這個(gè)缺陷也很好解決,比如可以做一個(gè)定時(shí)任務(wù)去定時(shí)清理。
2.這種鎖的可靠性依賴于數(shù)據(jù)庫(kù)。建議設(shè)置備庫(kù),避免單點(diǎn),進(jìn)一步提高可靠性。
3.這種鎖是非阻塞的。因?yàn)椴迦霐?shù)據(jù)失敗之后會(huì)直接報(bào)錯(cuò),想要獲得鎖就需要再次操作。如果需要阻塞式的,可以弄個(gè)for循環(huán)、while循環(huán)之類的,直至INSERT成功再返回。
4.這種鎖是非可重入的。因?yàn)閿?shù)據(jù)庫(kù)中鎖表的一份記錄就是一把鎖,想要實(shí)現(xiàn)可重入鎖,可以在數(shù)據(jù)庫(kù)中添加一些字段,比如獲得鎖的主機(jī)信息、線程信息等,那么在再次獲得鎖的時(shí)候可以先查詢數(shù)據(jù),如果當(dāng)前的主機(jī)信息和線程信息等能被查到的話,可以直接把鎖分配給它。

004分布式中樂(lè)觀鎖的原理。

樂(lè)觀鎖大多數(shù)是基于數(shù)據(jù)版本(version)的記錄機(jī)制實(shí)現(xiàn)的。通過(guò)對(duì)數(shù)據(jù)庫(kù)表添加一個(gè) “version”字段來(lái)實(shí)現(xiàn)的。讀數(shù)據(jù)時(shí)會(huì)將此版本號(hào)一同讀出,之后更新數(shù)據(jù)時(shí)會(huì)對(duì)此版本號(hào)加1。在更新過(guò)程中,會(huì)對(duì)版本號(hào)進(jìn)行比較,如果是一致的,沒(méi)有發(fā)生改變,則會(huì)成功執(zhí)行更新操作;如果版本號(hào)不一致,則執(zhí)行不會(huì)更新。
當(dāng)然借助更新時(shí)間戳(updated_at)也可以實(shí)現(xiàn)樂(lè)觀鎖,和采用version字段的方式相似:更新操作執(zhí)行前先記錄當(dāng)前的更新時(shí)間,在提交更新時(shí),檢測(cè)當(dāng)前更新時(shí)間是否與更新開(kāi)始時(shí)獲取的更新時(shí)間戳相等。

005樂(lè)觀鎖的優(yōu)點(diǎn)和缺點(diǎn)?

樂(lè)觀鎖的優(yōu)點(diǎn):由于在檢測(cè)數(shù)據(jù)沖突時(shí)并不依賴數(shù)據(jù)庫(kù)本身的鎖機(jī)制,不會(huì)影響請(qǐng)求的性能,當(dāng)產(chǎn)生并發(fā)且并發(fā)量較小的時(shí)候只有少部分請(qǐng)求會(huì)失敗。
樂(lè)觀鎖的缺點(diǎn):需要對(duì)表的設(shè)計(jì)增加額外的字段,增加了數(shù)據(jù)庫(kù)的冗余。另外,當(dāng)應(yīng)用并發(fā)量高的時(shí)候,version值在頻繁變化,會(huì)對(duì)數(shù)據(jù)庫(kù)產(chǎn)生很大的寫(xiě)壓力。并且也會(huì)導(dǎo)致大量請(qǐng)求失敗,影響系統(tǒng)的可用性。所以數(shù)據(jù)庫(kù)樂(lè)觀鎖比較適合并發(fā)量不高,并且寫(xiě)操作不頻繁的場(chǎng)景。

006悲觀鎖的實(shí)現(xiàn)原理。

悲觀鎖是數(shù)據(jù)庫(kù)中自帶的。在查詢語(yǔ)句后面增加FOR UPDATE,數(shù)據(jù)庫(kù)會(huì)在查詢過(guò)程中給數(shù)據(jù)庫(kù)表增加悲觀鎖,也稱排他鎖。悲觀鎖就會(huì)比較悲觀,總是假設(shè)最壞的情況,它認(rèn)為數(shù)據(jù)的更新在大多數(shù)情況下是會(huì)產(chǎn)生沖突的。
在使用悲觀鎖的同時(shí),我們需要注意一下鎖的級(jí)別。MySQL InnoDB在加鎖的時(shí)候,只有明確地指定主鍵(或索引)的才會(huì)執(zhí)行行鎖 (只鎖住被選取的數(shù)據(jù)),否則將會(huì)執(zhí)行表鎖(將整個(gè)數(shù)據(jù)表單給鎖住)。
在使用悲觀鎖時(shí),我們必須關(guān)閉MySQL數(shù)據(jù)庫(kù)的自動(dòng)提交屬性(參考下面的示例),因?yàn)镸ySQL默認(rèn)使用autocommit(自動(dòng)提交)模式。這樣在使用FOR UPDATE獲得鎖之后可以執(zhí)行相應(yīng)的業(yè)務(wù)邏輯,執(zhí)行完之后再使用COMMIT來(lái)釋放鎖。

007悲觀鎖的優(yōu)點(diǎn)和缺點(diǎn)?

悲觀鎖優(yōu)點(diǎn):可以嚴(yán)格保證數(shù)據(jù)訪問(wèn)的安全。
悲觀鎖缺點(diǎn):即每次請(qǐng)求都會(huì)額外產(chǎn)生加鎖的開(kāi)銷且未獲取到鎖的請(qǐng)求將會(huì)阻塞等待鎖的獲取,在高并發(fā)環(huán)境下,容易造成大量請(qǐng)求阻塞,影響系統(tǒng)可用性。另外,悲觀鎖使用不當(dāng)還可能產(chǎn)生死鎖的情況。

008基于 ZooKeeper 的分布式鎖實(shí)現(xiàn)原理是什么?

ZooKeeper是一個(gè)分布式的,開(kāi)放源碼的分布式應(yīng)用程序協(xié)調(diào)服務(wù),Zookeeper在本質(zhì)上就像一個(gè)文件管理系統(tǒng)。其用類似文件路徑的方式管理來(lái)監(jiān)聽(tīng)多個(gè)節(jié)點(diǎn)(Znode),同時(shí)判斷當(dāng)前每個(gè)節(jié)點(diǎn)上機(jī)器的狀態(tài)(是否宕機(jī)、是否斷開(kāi)連接等),從而達(dá)到分布式協(xié)同的操作。
ZooKeeper 可以根據(jù)有序節(jié)點(diǎn)+watch實(shí)現(xiàn),實(shí)現(xiàn)思路,如:為每個(gè)線程生成一個(gè)有序的臨時(shí)節(jié)點(diǎn),為確保有序性,在排序一次全部節(jié)點(diǎn),獲取全部節(jié)點(diǎn),每個(gè)線程判斷自己是否最小,如果是的話,獲得鎖,執(zhí)行操作,操作完刪除自身節(jié)點(diǎn)。如果不是第一個(gè)的節(jié)點(diǎn)則監(jiān)聽(tīng)它的前一個(gè)節(jié)點(diǎn),當(dāng)它的前一個(gè)節(jié)點(diǎn)被刪除時(shí),則它會(huì)獲得鎖,以此類推。

009ZooKeeper和Reids做分布式鎖的區(qū)別?

1. Redis分布式鎖需要不斷去嘗試獲取鎖,比較消耗性能。而ZooKeeper分布式鎖,獲取不到鎖會(huì)注冊(cè)個(gè)監(jiān)聽(tīng)器,不需要不斷主動(dòng)嘗試獲取鎖因此性能開(kāi)銷較小;
2. 如果是Redis獲取鎖的那個(gè)客戶端bug了或者掛了,那么只能等待超時(shí)時(shí)間之后才能釋放鎖;而ZooKeeper的話,因?yàn)閯?chuàng)建的是臨時(shí)znode,只要客戶端掛了,znode就沒(méi)了,此時(shí)就自動(dòng)釋放鎖;

010Redis分布式鎖之SETNX + EXPIRE

SETNX 是SET IF NOT EXISTS的簡(jiǎn)寫(xiě).日常命令格式是SETNX key value,如果 key不存在,則SETNX成功返回1,如果這個(gè)key已經(jīng)存在了,則返回0。
偽代碼實(shí)現(xiàn)如下:假設(shè)某電商網(wǎng)站的某商品做秒殺活動(dòng),key可以設(shè)置為key_resource_id,value設(shè)置任意值

if(jedis.setnx(key_resource_id,lock_value) == 1){ //加鎖 ? ??
? ? expire(key_resource_id,100);//設(shè)置過(guò)期時(shí)間 ? ??
? ? try { ? ? ? ??
? ? ? ? do something ?//業(yè)務(wù)請(qǐng)求 ? ??
? ? }catch(){ ??
? ? }finally { ? ? ? ?
? ? ? ? jedis.del(key_resource_id); //釋放鎖 ? ??
? ? }
}

缺點(diǎn):setnx和expire兩個(gè)命令分開(kāi)了,不是原子操作。如果執(zhí)行完setnx加鎖,正要執(zhí)行expire設(shè)置過(guò)期時(shí)間時(shí),進(jìn)程crash或者要重啟維護(hù)了,那么這個(gè)鎖就永遠(yuǎn)釋放不了,別的線程永遠(yuǎn)獲取不到鎖啦。

011Redis分布式鎖之SETNX + (系統(tǒng)時(shí)間+過(guò)期時(shí)間)

為了解決發(fā)生異常鎖得不到釋放的場(chǎng)景,可以把過(guò)期時(shí)間放到setnx的value值里面。如果加鎖失敗,再拿出value值校驗(yàn)一下即可。加鎖代碼如下:

long expires = System.currentTimeMillis() + expireTime; //系統(tǒng)時(shí)間+設(shè)置的過(guò)期時(shí)間
String expiresStr = String.valueOf(expires);
// 如果當(dāng)前鎖不存在,返回加鎖成功
if (jedis.setnx(key_resource_id, expiresStr) == 1) {
        return true;
} 
// 如果鎖已經(jīng)存在,獲取鎖的過(guò)期時(shí)間
String currentValueStr = jedis.get(key_resource_id);

// 如果獲取到的過(guò)期時(shí)間,小于系統(tǒng)當(dāng)前時(shí)間,表示已經(jīng)過(guò)期
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
     // 鎖已過(guò)期,獲取上一個(gè)鎖的過(guò)期時(shí)間,并設(shè)置現(xiàn)在鎖的過(guò)期時(shí)間(不了解redis的getSet命令的小伙伴,可以去官網(wǎng)看下哈)
    String oldValueStr = jedis.getSet(key_resource_id, expiresStr);
    if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
         // 考慮多線程并發(fā)的情況,只有一個(gè)線程的設(shè)置值和當(dāng)前值相同,它才可以加鎖
         return true;
    }
}     
//其他情況,均返回加鎖失敗
return false;
}

缺點(diǎn):
1.過(guò)期時(shí)間是客戶端自己生成的(System.currentTimeMillis()是當(dāng)前系統(tǒng)的時(shí)間),必須要求分布式環(huán)境下,每個(gè)客戶端的時(shí)間必須同步。
2.如果鎖過(guò)期的時(shí)候,并發(fā)多個(gè)客戶端同時(shí)請(qǐng)求過(guò)來(lái),都執(zhí)行jedis.getSet(),最終只能有一個(gè)客戶端加鎖成功,因此某個(gè)客戶端加的鎖可能被別的客戶端所覆蓋。
3.該鎖沒(méi)有保存持有者的唯一標(biāo)識(shí),可能被別的客戶端釋放/解鎖。

012Redis分布式鎖之SET擴(kuò)展命令。

SET key value[EX seconds][PX milliseconds][NX|XX]
● EX seconds :設(shè)定key的過(guò)期時(shí)間,時(shí)間單位是秒。
● PX milliseconds: 設(shè)定key的過(guò)期時(shí)間,單位為毫秒。
● NX :表示key不存在的時(shí)候,才能set成功,也即保證只有第一個(gè)客戶端請(qǐng)求才能獲得鎖,而其他客戶端請(qǐng)求只能等其釋放鎖,才能獲取。
● XX: 僅當(dāng)key存在時(shí)設(shè)置值;

if(jedis.set(key_resource_id,lock_value,"NX","EX",100s)==1){//加鎖  
    try{ 
        dosomething  
        //業(yè)務(wù)處理     
    }catch(){   
    }finally {        
        jedis.del(key_resource_id); //釋放鎖     
    } 
}

缺點(diǎn):
問(wèn)題一:鎖過(guò)期釋放了,業(yè)務(wù)還沒(méi)執(zhí)行完。假設(shè)線程a獲取鎖成功,一直在執(zhí)行臨界區(qū)的代碼。但是100s過(guò)去后,它還沒(méi)執(zhí)行完。但是,這時(shí)候鎖已經(jīng)過(guò)期了,此時(shí)線程b又請(qǐng)求過(guò)來(lái)。顯然線程b就可以獲得鎖成功,也開(kāi)始執(zhí)行臨界區(qū)的代碼。那么問(wèn)題就來(lái)了,臨界區(qū)的業(yè)務(wù)代碼都不是嚴(yán)格串行執(zhí)行的啦。
問(wèn)題二:鎖被別的線程誤刪。假設(shè)線程a執(zhí)行完后,去釋放鎖。但是它不知道當(dāng)前的鎖可能是線程b持有的(線程a去釋放鎖時(shí),有可能過(guò)期時(shí)間已經(jīng)到了,此時(shí)線程b進(jìn)來(lái)占有了鎖)。那線程a就把線程b的鎖釋放掉了,但是線程b臨界區(qū)業(yè)務(wù)代碼可能都還沒(méi)執(zhí)行完呢。

013Redis分布式鎖之SET EX PX NX + 校驗(yàn)唯一隨機(jī)值,再刪除。

既然鎖可能被別的線程誤刪,那我們給value值設(shè)置一個(gè)標(biāo)記當(dāng)前線程唯一的隨機(jī)數(shù),在刪除的時(shí)候,校驗(yàn)一下:

if(jedis.set(key_resource_id,uni_request_id,"NX","EX",100s)==1){ 
    //加鎖     
    try {         
        do something  //業(yè)務(wù)處理     
    }catch(){   
    }finally {        
        //判斷是不是當(dāng)前線程加的鎖,是才釋放        
        if (uni_request_id.equals(jedis.get(key_resource_id))) {         
            jedis.del(lockKey); //釋放鎖         
        }     
    } 
}

014Redis分布式鎖之Redisson框架

方案四中還是可能存在鎖過(guò)期釋放,業(yè)務(wù)沒(méi)執(zhí)行完的問(wèn)題。開(kāi)源框架Redisson解決了這個(gè)問(wèn)題。先來(lái)看下Redisson底層原理圖:

只要線程一加鎖成功,就會(huì)啟動(dòng)一個(gè)watch dog看門狗,它是一個(gè)后臺(tái)線程,會(huì)每隔10秒檢查一下,如果線程1還持有鎖,那么就會(huì)不斷的延長(zhǎng)鎖key的生存時(shí)間。因此Redisson解決了鎖過(guò)期釋放,業(yè)務(wù)沒(méi)執(zhí)行完問(wèn)題。

015Redis多機(jī)版分布式鎖之Redlock+Redisson

前面五種方案都是基于單機(jī)版的,其實(shí)Redis一般都是集群部署的。為了解決這個(gè)問(wèn)題,Redis作者 antirez提出一種高級(jí)的分布式鎖算法:Redlock。

目錄

返回頂部
主站蜘蛛池模板: 毛片在线免费视频 | 久久伊人影院 | 日本h在线精品免费观看 | 成 人 免费 黄 色 视频 | 狠狠躁夜夜躁人人爽天天天天 | 成人国产片免费 | 精品久久久久久中文字幕一区 | 91久久爱| 免费人成在线观看69式小视频 | 波多野结衣亚洲一区 | 亚洲欧美卡通成人制服动漫 | 一道本不卡免费视频 | 日韩专区亚洲精品欧美专区 | 综合激情五月婷婷 | 五月婷婷之综合激情 | 天天做天天爱夜夜爽毛片毛片 | 国产精品综合 | 午夜影院0606 | 高清欧美色欧美综合网站 | 999这里只有精品 | 变态捡到女婴h养成调教 | 国内自拍tv在线 | 九九碰| 亚洲六月丁香色婷婷综合久久 | 老司机深夜影院入口aaaa | 99视频在线精品 | 最新99国产成人精品视频免费 | 九九天天影视 | 亚洲欧洲日韩国产一区二区三区 | 天天爽影院一区二区在线影院 | 日本中文在线 | 四虎在线免费观看视频 | 中文字幕视频一区 | 国产精品一区二区三区四区 | aⅴ在线免费观看 | 免费高清在线爱做视频 | 亚洲综合在线观看视频 | 久久99影院网久久久久久 | 国产成人久久精品二区三区牛 | 久久99国产精品成人 | 久久精品国产精品亚洲人人 |