一、什么是分布式鎖
在單體架構(gòu)中,系統(tǒng)只有一個(gè),系統(tǒng)所用的內(nèi)存和進(jìn)程也只有一個(gè),多個(gè)線程可以共享同一份數(shù)據(jù)。這樣只要使用java提供的鎖機(jī)制就可以解決并發(fā)訪問帶來的問題,但是分布式系統(tǒng)中,系統(tǒng)是多個(gè),并且所使用的內(nèi)存也是不同的,每個(gè)系統(tǒng)也都有獨(dú)立的進(jìn)程,這樣Java提供的鎖就沒辦法解決分布式系統(tǒng)中的并發(fā)訪問問題。因此就需要引入分布式鎖來解決分布式系統(tǒng)中共享資源訪問的問題。
1.2 分布式鎖的特性
1.要保證同一時(shí)刻內(nèi),只有一個(gè)服務(wù)獲取到這個(gè)鎖
2.這把鎖要能重入,在某些業(yè)務(wù)下會(huì)出現(xiàn)同一把鎖的重入
3.具備鎖續(xù)命,保證高并發(fā)下不會(huì)因鎖的時(shí)效問題引起數(shù)據(jù)錯(cuò)誤
4.具有非阻塞式獲取鎖,在獲取鎖失敗后立刻返回
1.3 基于Redisson實(shí)現(xiàn)的分布式鎖
Redisson中分布式鎖的架構(gòu):
關(guān)于Redisson分布式鎖的使用:
private Integer setInfo(Long key){
// 獲取分布式鎖
RLock lock = redisson.getLock("Ext_Info:" + key);
// 加鎖
lock.lock();
try{
// 業(yè)務(wù)處理
Product product = productDao.get(key);
return 1;
}catch (Exception e){
e.printStackTrace();
}finally {
// 解鎖
lock.unlock();
}
return -1;
}
1.4 分布式鎖失效問題
當(dāng)主從或集群機(jī)構(gòu)的主節(jié)點(diǎn)掛掉后,該節(jié)點(diǎn)存儲(chǔ)的分布式鎖信息可能會(huì)丟失。而當(dāng)從節(jié)點(diǎn)選舉成新節(jié)點(diǎn)后,其它線程又可以對(duì)新的主節(jié)點(diǎn)進(jìn)行添加分布式鎖信息,這就會(huì)引起并發(fā)安全問題。
為了解決這個(gè)問題,就引入了redlock。
redlock的邏輯就是向多個(gè)節(jié)點(diǎn)寫入加鎖的信息,如果寫入成功,加代表加鎖成功。但是如果寫入的節(jié)點(diǎn)丟失了鎖的信息,仍然是會(huì)出現(xiàn)分布式鎖失效問題。
還有一點(diǎn)就是性能問題,我們使用redis就是為了它的高性能,但是使用redlock后每次加鎖都要寫入多個(gè)節(jié)點(diǎn),這就會(huì)降低redis性能,這樣還不如使用zookeeper。

二、Redisson實(shí)現(xiàn)分布式鎖源碼剖析
加鎖的核心邏輯:



三、分布式鎖的優(yōu)化
對(duì)于讀多寫少的業(yè)務(wù),可以使用readwirte鎖:
public Product update(Product product) {
Product productResult = null;
// 獲取分布式鎖
RReadWriteLock readWriteLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());
// 寫鎖
RLock writeLock = readWriteLock.writeLock();
// 加鎖
writeLock.lock();
try {
productResult = productDao.update(product);
// 設(shè)置過期時(shí)間
redisUtil.set(productResult.getId(), JSON.toJSONString(productResult),
genProductCacheTimeout(), TimeUnit.SECONDS);
} finally {
// 釋放鎖
writeLock.unlock();
}
return productResult;
}
redisson實(shí)現(xiàn)的讀寫鎖,大體上和上面的分布式鎖邏輯相同,只是加了一個(gè)mode,用于區(qū)分是讀還是寫。對(duì)于讀讀而言,就等于是鎖的重入,不會(huì)阻塞;對(duì)于讀寫、寫寫操作,就會(huì)阻塞保證并發(fā)的安全。
四、緩存問題
1、什么是緩存擊穿?
當(dāng)同一時(shí)刻有大量的緩存失效,就會(huì)導(dǎo)致大量的請(qǐng)求打到數(shù)據(jù)庫,會(huì)造成數(shù)據(jù)庫壓力過大甚至宕機(jī)。
解決辦法:在給緩存數(shù)據(jù)設(shè)置過期時(shí)間時(shí),增加一個(gè)隨機(jī)的擾動(dòng)因子,避免讓大量的緩存數(shù)據(jù)都同一時(shí)刻失效。
2、什么是緩存穿透?
緩存層和數(shù)據(jù)庫都沒有數(shù)據(jù),每次請(qǐng)求都會(huì)落到數(shù)據(jù)庫,如果是高并發(fā)場(chǎng)景下,就會(huì)引起數(shù)據(jù)的壓力劇增,甚至宕機(jī)。
解決辦法:
1、對(duì)于不存在的數(shù)據(jù),可以在緩存層面設(shè)置對(duì)應(yīng)key的空值
2、布隆過濾器,向布隆過濾器中添加key時(shí),會(huì)先使用多個(gè)hash函數(shù)進(jìn)行運(yùn)算,然后定位到布隆過濾器的數(shù)組中某個(gè)下標(biāo)。當(dāng)某個(gè)key查詢布隆過濾器數(shù)據(jù)時(shí),會(huì)同樣使用多個(gè)hash函數(shù)進(jìn)行運(yùn)算,然后得到數(shù)組中的下標(biāo)位置。
3、什么是緩存雪崩?
緩存層的作用就是分擔(dān)數(shù)據(jù)庫層面的壓力,如果緩存層宕機(jī),大量的請(qǐng)求就直接打到數(shù)據(jù)庫,高并發(fā)下數(shù)據(jù)庫有可能宕機(jī)。
解決辦法:
1、確保緩存層的高可用,比如搭建集群、主從架構(gòu)
2、在后端應(yīng)用上設(shè)置限流或服務(wù)降級(jí)