国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化)

這篇具有很好參考價(jià)值的文章主要介紹了微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化)

1 優(yōu)化需求

視頻播放頁(yè)面用戶(hù)未登錄也可以訪(fǎng)問(wèn),當(dāng)用戶(hù)觀(guān)看試學(xué)課程時(shí)需要請(qǐng)求服務(wù)端查詢(xún)數(shù)據(jù),接口如下:

1、根據(jù)課程id查詢(xún)課程信息。

2、根據(jù)文件id查詢(xún)視頻信息。

這些接口在用戶(hù)未認(rèn)證狀態(tài)下也可以訪(fǎng)問(wèn),如果接口的性能不高,當(dāng)高并發(fā)到來(lái)很可能耗盡整個(gè)系統(tǒng)的資源,將整個(gè)系統(tǒng)壓垮,所以特別需要對(duì)這些暴露在外邊的接口進(jìn)行優(yōu)化。

下邊對(duì) 根據(jù)課程id查詢(xún)課程信息
接口進(jìn)行優(yōu)化,下邊的內(nèi)容將此接口簡(jiǎn)稱(chēng)為課程查詢(xún)接口。

接口地址:http://www.51xuecheng.cn/open/content/course/whole/{courseId}

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

2 壓力測(cè)試

2.1 性能指標(biāo)

對(duì)接口進(jìn)行優(yōu)化之前需要對(duì)接口進(jìn)行壓力測(cè)試,不僅接口需要壓力測(cè)試,整個(gè)微服務(wù)在發(fā)布前也是需要經(jīng)歷壓力測(cè)試的,因?yàn)閴毫y(cè)試可以暴露功能測(cè)試所發(fā)現(xiàn)不了的問(wèn)題。

功能測(cè)試即是對(duì)系統(tǒng)的功能按用戶(hù)需求進(jìn)行測(cè)試,比如:添加一門(mén)課程,根據(jù)需求文檔先準(zhǔn)備測(cè)試數(shù)據(jù),再通過(guò)前端界面將一門(mén)課程添加到系統(tǒng),測(cè)試是否可以操作成功。整個(gè)過(guò)程就是測(cè)試軟件是否可以實(shí)現(xiàn)用戶(hù)的需求。

壓力測(cè)試是通過(guò)測(cè)試工具制造大規(guī)模的并發(fā)請(qǐng)求去訪(fǎng)問(wèn)系統(tǒng),測(cè)試系統(tǒng)是否經(jīng)受住壓力。

比如:一個(gè)在線(xiàn)學(xué)習(xí)網(wǎng)站,上線(xiàn)要求該網(wǎng)站可以支持1萬(wàn)用戶(hù)同時(shí)在線(xiàn),此時(shí)就需要模擬1萬(wàn)并發(fā)請(qǐng)求去訪(fǎng)問(wèn)網(wǎng)站的關(guān)鍵業(yè)務(wù)流程,比如:測(cè)試點(diǎn)播學(xué)習(xí)流程,測(cè)試系統(tǒng)是否可以抗住1萬(wàn)并發(fā)請(qǐng)求。

一些功能測(cè)試時(shí)無(wú)法發(fā)現(xiàn)的問(wèn)題在壓力測(cè)試時(shí)就會(huì)發(fā)現(xiàn),比如:內(nèi)存泄露、線(xiàn)程安全、IO異常等問(wèn)題。

壓力測(cè)試常用的性能指標(biāo)如下:

1、吞吐量

吞吐量是系統(tǒng)每秒可以處理的事務(wù)數(shù),也稱(chēng)為T(mén)PS(Transaction Per Second)。

比如:一次點(diǎn)播流程,從請(qǐng)求進(jìn)入系統(tǒng)到視頻畫(huà)圖顯示出來(lái)這整個(gè)流程就是一次事務(wù)。

所以吞吐量并不是一次數(shù)據(jù)庫(kù)事務(wù),它是完成一次業(yè)務(wù)的整體流程。

2、響應(yīng)時(shí)間

響應(yīng)時(shí)間是指客戶(hù)端請(qǐng)求服務(wù)端,從請(qǐng)求進(jìn)入系統(tǒng)到客戶(hù)端拿到響應(yīng)結(jié)果所經(jīng)歷的時(shí)間。響應(yīng)時(shí)間包括:最大響應(yīng)時(shí)間、最小響應(yīng)時(shí)間、平均響應(yīng)時(shí)間。

3、每秒查詢(xún)數(shù)

每秒查詢(xún)數(shù)即QPS(Queries-per-second),它是衡量查詢(xún)接口的性能指標(biāo),比如:商品信息查詢(xún),
一秒可以請(qǐng)求該接口查詢(xún)商品信息的次數(shù)就是QPS。

拿查詢(xún)接口舉例,一次查詢(xún)請(qǐng)求內(nèi)部不會(huì)再去請(qǐng)求其它接口,此時(shí) QPS=TPS

如果一次查詢(xún)請(qǐng)求內(nèi)容需要遠(yuǎn)程調(diào)用另一個(gè)接口查詢(xún)數(shù)據(jù),此時(shí) QPS=2 * TPS

4、錯(cuò)誤率

錯(cuò)誤率 是一批請(qǐng)求發(fā)生錯(cuò)誤的請(qǐng)求占全部請(qǐng)求的比例。

不同的指標(biāo)其要求不同,比如現(xiàn)在進(jìn)行接口優(yōu)化,優(yōu)化后的接口響應(yīng)時(shí)間應(yīng)該越來(lái)越小,吞吐量越來(lái)越大,以及QPS值也是越大越好,錯(cuò)誤率要保持在一個(gè)很小的范圍。

另外除了關(guān)注這些性能指標(biāo)以外還要關(guān)注系統(tǒng)的負(fù)載情況:

1、CPU使用率,不高于85%

2、內(nèi)存利用率,不高于 85%

3、網(wǎng)絡(luò)利用率,不高于 80%

4、磁盤(pán)IO

磁盤(pán)IO的性能指標(biāo)是IOPS (Input/Output Per
Second)即每秒的輸入輸出量(或讀寫(xiě)次數(shù))。

如果過(guò)大說(shuō)明IO操作密集,IO過(guò)大也會(huì)影響性能指標(biāo)。

2.2 安裝Jmeter

Apache JMeter 是 Apache 組織基于 Java
開(kāi)發(fā)的壓力測(cè)試工具,用于對(duì)軟件做壓力測(cè)試。

下載Jmeter

https://jmeter.apache.org/download_jmeter.cgi

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

下載,解壓,進(jìn)入bin目錄修改jmeter.properties,設(shè)置中文和字體

language=zh_CN
jmeter.hidpi.mode=true
jmeter.hidpi.scale.factor=1.8
jsyntaxtextarea.font.family= Hack
jsyntaxtextarea.font.size=25
jmeter.toolbar.icons.size=32x32

雙擊運(yùn)行bin目錄下的jmeter.bat文件。

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

界面如下圖:

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

2.3 壓力測(cè)試

樣本數(shù):200個(gè)線(xiàn)程,每個(gè)線(xiàn)程請(qǐng)求100次,共20000次

壓力機(jī):通常壓力機(jī)是單獨(dú)的客戶(hù)端。

測(cè)試gateway+content

吞吐量180左右

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

測(cè)試content

吞吐量300左右

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

2.4 優(yōu)化日志

內(nèi)容管理日志級(jí)別改為info級(jí)別.

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

單獨(dú)請(qǐng)求內(nèi)容管理測(cè)試,吞吐量達(dá)到1500左右

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

3 緩存優(yōu)化

3.1 redis緩存

測(cè)試用例是根據(jù)id查詢(xún)課程信息,這里不存在復(fù)雜的SQL,也不存在數(shù)據(jù)庫(kù)連接不釋放的問(wèn)題,暫時(shí)不考慮數(shù)據(jù)庫(kù)方面的優(yōu)化。

課程發(fā)布信息的特點(diǎn)的是查詢(xún)較多,修改很少,這里考慮將課程發(fā)布信息進(jìn)行緩存。

課程信息緩存的流程如下:

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

在nacos配置redis-dev.yaml(group=xuecheng-plus-common)

spring: 
  redis:
    host: 192.168.101.65
    port: 6379
    password: redis
    database: 0
    lettuce:
      pool:
        max-active: 20
        max-idle: 10
        min-idle: 0
    timeout: 10000

在content-api微服務(wù)加載redis-dev.yaml

shared-configs:
    - data-id: redis-${spring.profiles.active}.yaml
      group: xuecheng-plus-common
      refresh: true

在content-service微服務(wù)中添加依賴(lài)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.2</version>
</dependency>

定義查詢(xún)緩存接口:

/**
 * @description 查詢(xún)緩存中的課程信息
 * @param courseId 
 * @return com.xuecheng.content.model.po.CoursePublish
 * @author Mr.M
 * @date 2022/10/22 16:15
*/
public CoursePublish getCoursePublishCache(Long courseId);

接口實(shí)現(xiàn)如下:

public CoursePublish getCoursePublishCache(Long courseId){
    //查詢(xún)緩存
   Object  jsonObj = redisTemplate.opsForValue().get("course:" + courseId);
    if(jsonObj!=null){
    String jsonString = jsonObj.toString();
        System.out.println("=================從緩存查=================");
        CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class);
        return coursePublish;
    } else {
        System.out.println("從數(shù)據(jù)庫(kù)查詢(xún)...");
        //從數(shù)據(jù)庫(kù)查詢(xún)
        CoursePublish coursePublish = getCoursePublish(courseId);
        if(coursePublish!=null){
            redisTemplate.opsForValue().set("course:" + courseId, JSON.toJSONString(coursePublish));
        }
        return coursePublish;
    }
}
}

修改controller接口調(diào)用代碼

@ApiOperation("獲取課程發(fā)布信息")
    @ResponseBody
    @GetMapping("/course/whole/{courseId}")
    public CoursePreviewDto getCoursePublish(@PathVariable("courseId") Long courseId) {
        //查詢(xún)課程發(fā)布信息
        CoursePublish coursePublish = coursePublishService.getCoursePublishCache(courseId);
//        CoursePublish coursePublish = coursePublishService.getCoursePublish(courseId);
        if(coursePublish==null){
            return new CoursePreviewDto();
        }

        //課程基本信息
        CourseBaseInfoDto courseBase = new CourseBaseInfoDto();
        BeanUtils.copyProperties(coursePublish, courseBase);
        //課程計(jì)劃
        List<TeachplanDto> teachplans = JSON.parseArray(coursePublish.getTeachplan(), TeachplanDto.class);
        CoursePreviewDto coursePreviewInfo = new CoursePreviewDto();
        coursePreviewInfo.setCourseBase(courseBase);
        coursePreviewInfo.setTeachplans(teachplans);
        return coursePreviewInfo;
    }

重新測(cè)試請(qǐng)求內(nèi)容管理服務(wù)課程查詢(xún)接口。

吞吐量達(dá)到2700左右,增加了近一倍。

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

3.2 緩存穿透問(wèn)題

3.2.1 什么是緩存穿透

使用緩存后代碼的性能有了很大的提高,雖然性能有很大的提升但是控制臺(tái)打出了很多"從數(shù)據(jù)庫(kù)查詢(xún)"的日志,明明判斷了如果緩存存在課程信息則從緩存查詢(xún),為什么要有這么多從數(shù)據(jù)庫(kù)查詢(xún)的請(qǐng)求的?

這是因?yàn)椴l(fā)數(shù)高,很多線(xiàn)程會(huì)同時(shí)到達(dá)查詢(xún)數(shù)據(jù)庫(kù)代碼處去執(zhí)行。

我們分析下代碼:

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

如果存在惡意攻擊的可能,如果有大量并發(fā)去查詢(xún)一個(gè)不存在的課程信息會(huì)出現(xiàn)什么問(wèn)題呢?

比如去請(qǐng)求/content/course/whole/181,查詢(xún)181號(hào)課程,該課程并不在課程發(fā)布表中。

進(jìn)行壓力測(cè)試發(fā)現(xiàn)會(huì)去請(qǐng)求數(shù)據(jù)庫(kù)。

大量并發(fā)去訪(fǎng)問(wèn)一個(gè)數(shù)據(jù)庫(kù)不存在的數(shù)據(jù),由于緩存中沒(méi)有該數(shù)據(jù)導(dǎo)致大量并發(fā)查詢(xún)數(shù)據(jù)庫(kù),這個(gè)現(xiàn)象要緩存穿透。

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

緩存穿透可以造成數(shù)據(jù)庫(kù)瞬間壓力過(guò)大,連接數(shù)等資源用完,最終數(shù)據(jù)庫(kù)拒絕連接不可用。

3.2.2 解決緩存穿透

如何解決緩存穿透?

1、對(duì)請(qǐng)求增加校驗(yàn)機(jī)制

比如:課程Id是長(zhǎng)整型,如果發(fā)來(lái)的不是長(zhǎng)整型則直接返回。

2、使用布隆過(guò)濾器

什么是布隆過(guò)濾器,以下摘自百度百科:

布隆過(guò)濾器可以用于檢索一個(gè)元素是否在一個(gè)集合中。如果想要判斷一個(gè)元素是不是在一個(gè)集合里,一般想到的是將所有元素保存起來(lái),然后通過(guò)比較確定。鏈表,樹(shù)等等數(shù)據(jù)結(jié)構(gòu)都是這種思路.
但是隨著集合中元素的增加,我們需要的存儲(chǔ)空間越來(lái)越大,檢索速度也越來(lái)越慢(O(n),O(logn))。不過(guò)世界上還有一種叫作散列表(又叫哈希表,Hash
table)的數(shù)據(jù)結(jié)構(gòu)。它可以通過(guò)一個(gè)Hash函數(shù)將一個(gè)元素映射成一個(gè)位陣列(Bit
array)中的一個(gè)點(diǎn)。這樣一來(lái),我們只要看看這個(gè)點(diǎn)是不是1就可以知道集合中有沒(méi)有它了。這就是布隆過(guò)濾器的基本思想。

布隆過(guò)濾器的特點(diǎn)是,高效地插入和查詢(xún),占用空間少;查詢(xún)結(jié)果有不確定性,如果查詢(xún)結(jié)果是存在則元素不一定存在,如果不存在則一定不存在;另外它只能添加元素不能刪除元素,因?yàn)閯h除元素會(huì)增加誤判率。

比如:將商品id寫(xiě)入布隆過(guò)濾器,如果分3次hash此時(shí)在布隆過(guò)濾器有3個(gè)點(diǎn),當(dāng)從布隆過(guò)濾器查詢(xún)?cè)撋唐穒d,通過(guò)hash找到了該商品id在過(guò)濾器中的點(diǎn),此時(shí)返回1,如果找不到一定會(huì)返回0。

所以,為了避免緩存穿透我們需要緩存預(yù)熱將要查詢(xún)的課程或商品信息的id提前存入布隆過(guò)濾器,添加數(shù)據(jù)時(shí)將信息的id也存入過(guò)濾器,當(dāng)去查詢(xún)一個(gè)數(shù)據(jù)時(shí)先在布隆過(guò)濾器中找一下如果沒(méi)有到到就說(shuō)明不存在,此時(shí)直接返回。

實(shí)現(xiàn)方法有:

Google工具包Guava實(shí)現(xiàn)。

redisson 。

2、緩存空值或特殊值

請(qǐng)求通過(guò)了第一步的校驗(yàn),查詢(xún)數(shù)據(jù)庫(kù)得到的數(shù)據(jù)不存在,此時(shí)我們?nèi)匀蝗ゾ彺鏀?shù)據(jù),緩存一個(gè)空值或一個(gè)特殊值的數(shù)據(jù)。

但是要注意:如果緩存了空值或特殊值要設(shè)置一個(gè)短暫的過(guò)期時(shí)間。

public CoursePublish getCoursePublishCache(Long courseId) {

    //查詢(xún)緩存
   Object  jsonObj = redisTemplate.opsForValue().get("course:" + courseId);
    if(jsonObj!=null){
    String jsonString = jsonObj.toString();
        if(jsonString.equals("null"))
            return null;
        CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class);
        return coursePublish;
    } else {
        //從數(shù)據(jù)庫(kù)查詢(xún)
        System.out.println("從數(shù)據(jù)庫(kù)查詢(xún)數(shù)據(jù)...");
        CoursePublish coursePublish = getCoursePublish(courseId);
        //設(shè)置過(guò)期時(shí)間300秒
        redisTemplate.opsForValue().set("course:" + courseId, JSON.toJSONString(coursePublish),30, TimeUnit.SECONDS);
        return coursePublish;
    }
}

再測(cè)試,雖然還存在個(gè)別請(qǐng)求去查詢(xún)數(shù)據(jù)庫(kù),但不是所有請(qǐng)求都去查詢(xún)數(shù)據(jù)庫(kù),基本上都命中緩存。

3.3 緩存雪崩

3.3.1 什么是緩存雪崩

緩存雪崩是緩存中大量key失效后當(dāng)高并發(fā)到來(lái)時(shí)導(dǎo)致大量請(qǐng)求到數(shù)據(jù)庫(kù),瞬間耗盡數(shù)據(jù)庫(kù)資源,導(dǎo)致數(shù)據(jù)庫(kù)無(wú)法使用。

造成緩存雪崩問(wèn)題的原因是是大量key擁有了相同的過(guò)期時(shí)間,比如對(duì)課程信息設(shè)置緩存過(guò)期時(shí)間為10分鐘,在大量請(qǐng)求同時(shí)查詢(xún)大量的課程信息時(shí),此時(shí)就會(huì)有大量的課程存在相同的過(guò)期時(shí)間,一旦失效將同時(shí)失效,造成雪崩問(wèn)題。

3.3.2 解決緩存雪崩

如何解決緩存雪崩?

1、使用同步鎖控制查詢(xún)數(shù)據(jù)庫(kù)的線(xiàn)程

使用同步鎖控制查詢(xún)數(shù)據(jù)庫(kù)的線(xiàn)程,只允許有一個(gè)線(xiàn)程去查詢(xún)數(shù)據(jù)庫(kù),查詢(xún)得到數(shù)據(jù)后存入緩存。

synchronized(obj){
  //查詢(xún)數(shù)據(jù)庫(kù)
  //存入緩存
}

2、對(duì)同一類(lèi)型信息的key設(shè)置不同的過(guò)期時(shí)間

通常對(duì)一類(lèi)信息的key設(shè)置的過(guò)期時(shí)間是相同的,這里可以在原有固定時(shí)間的基礎(chǔ)上加上一個(gè)隨機(jī)時(shí)間使它們的過(guò)期時(shí)間都不相同。

示例代碼如下:

   //設(shè)置過(guò)期時(shí)間300秒
  redisTemplate.opsForValue().set("course:" + courseId, JSON.toJSONString(coursePublish),300+new Random().nextInt(100), TimeUnit.SECONDS);

3、緩存預(yù)熱

不用等到請(qǐng)求到來(lái)再去查詢(xún)數(shù)據(jù)庫(kù)存入緩存,可以提前將數(shù)據(jù)存入緩存。使用緩存預(yù)熱機(jī)制通常有專(zhuān)門(mén)的后臺(tái)程序去將數(shù)據(jù)庫(kù)的數(shù)據(jù)同步到緩存。

3.4 緩存擊穿

3.4.1 什么是緩存擊穿

緩存擊穿是指大量并發(fā)訪(fǎng)問(wèn)同一個(gè)熱點(diǎn)數(shù)據(jù),當(dāng)熱點(diǎn)數(shù)據(jù)失效后同時(shí)去請(qǐng)求數(shù)據(jù)庫(kù),瞬間耗盡數(shù)據(jù)庫(kù)資源,導(dǎo)致數(shù)據(jù)庫(kù)無(wú)法使用。

比如某手機(jī)新品發(fā)布,當(dāng)緩存失效時(shí)有大量并發(fā)到來(lái)導(dǎo)致同時(shí)去訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)。

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

3.4.2 解決緩存擊穿

如何解決緩存擊穿?

1、使用同步鎖控制查詢(xún)數(shù)據(jù)庫(kù)的線(xiàn)程

使用同步鎖控制查詢(xún)數(shù)據(jù)庫(kù)的代碼,只允許有一個(gè)線(xiàn)程去查詢(xún)數(shù)據(jù)庫(kù),查詢(xún)得到數(shù)據(jù)庫(kù)存入緩存。

synchronized(obj){
  //查詢(xún)數(shù)據(jù)庫(kù)
  //存入緩存
}

2、熱點(diǎn)數(shù)據(jù)不過(guò)期

可以由后臺(tái)程序提前將熱點(diǎn)數(shù)據(jù)加入緩存,緩存過(guò)期時(shí)間不過(guò)期,由后臺(tái)程序做好緩存同步。

下邊使用synchronized對(duì)代碼加鎖。

public  CoursePublish getCoursePublishCache(Long courseId){
    synchronized(this){
        //查詢(xún)緩存
        String jsonString = (String) redisTemplate.opsForValue().get("course:" + courseId);
        if(StringUtils.isNotEmpty(jsonString)){
            if(jsonString.equals("null"))
                return null;
            CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class);
            return coursePublish;
        }else{
            System.out.println("=========從數(shù)據(jù)庫(kù)查詢(xún)==========");
            //從數(shù)據(jù)庫(kù)查詢(xún)
            CoursePublish coursePublish = getCoursePublish(courseId);
           //設(shè)置過(guò)期時(shí)間300秒
        redisTemplate.opsForValue().set("course:" + courseId, JSON.toJSONString(coursePublish),300, TimeUnit.SECONDS);
            return coursePublish;
        }
    }

}

測(cè)試,吞吐量有1300左右

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

對(duì)上邊的代碼進(jìn)行優(yōu)化,對(duì)查詢(xún)緩存的代碼不用synchronized加鎖控制,只對(duì)查詢(xún)數(shù)據(jù)庫(kù)進(jìn)行加鎖,如下:

public  CoursePublish getCoursePublishCache(Long courseId){

        //查詢(xún)緩存
         Object  jsonObj = redisTemplate.opsForValue().get("course:" + courseId);
         if(jsonObj!=null){
            String jsonString = jsonObj.toString();
            CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class);
            return coursePublish;
        }else{
            synchronized(this){
                Object  jsonObj = redisTemplate.opsForValue().get("course:" + courseId);
                if(jsonObj!=null){
                   String jsonString = jsonObj.toString();
                    CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class);
                    return coursePublish;
                }
                 System.out.println("=========從數(shù)據(jù)庫(kù)查詢(xún)==========");
                //從數(shù)據(jù)庫(kù)查詢(xún)
                CoursePublish coursePublish = getCoursePublish(courseId);
              //設(shè)置過(guò)期時(shí)間300秒
                redisTemplate.opsForValue().set("course:" + courseId, JSON.toJSONString(coursePublish),300, TimeUnit.SECONDS);
                return coursePublish;
            }
        }


}

測(cè)試,查詢(xún)數(shù)據(jù)庫(kù)只發(fā)生一次,整個(gè)測(cè)試過(guò)程的吞吐量在3800左右。

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

3.4.3 小結(jié)

1)緩存穿透:

去訪(fǎng)問(wèn)一個(gè)數(shù)據(jù)庫(kù)不存在的數(shù)據(jù)無(wú)法將數(shù)據(jù)進(jìn)行緩存,導(dǎo)致查詢(xún)數(shù)據(jù)庫(kù),當(dāng)并發(fā)較大就會(huì)對(duì)數(shù)據(jù)庫(kù)造成壓力。緩存穿透可以造成數(shù)據(jù)庫(kù)瞬間壓力過(guò)大,連接數(shù)等資源用完,最終數(shù)據(jù)庫(kù)拒絕連接不可用。

解決的方法:

緩存一個(gè)null值。

使用布隆過(guò)濾器。

2)緩存雪崩:

緩存中大量key失效后當(dāng)高并發(fā)到來(lái)時(shí)導(dǎo)致大量請(qǐng)求到數(shù)據(jù)庫(kù),瞬間耗盡數(shù)據(jù)庫(kù)資源,導(dǎo)致數(shù)據(jù)庫(kù)無(wú)法使用。

造成緩存雪崩問(wèn)題的原因是是大量key擁有了相同的過(guò)期時(shí)間。

解決辦法:

使用同步鎖控制

對(duì)同一類(lèi)型信息的key設(shè)置不同的過(guò)期時(shí)間,比如:使用固定數(shù)+隨機(jī)數(shù)作為過(guò)期時(shí)間。

3)緩存擊穿

大量并發(fā)訪(fǎng)問(wèn)同一個(gè)熱點(diǎn)數(shù)據(jù),當(dāng)熱點(diǎn)數(shù)據(jù)失效后同時(shí)去請(qǐng)求數(shù)據(jù)庫(kù),瞬間耗盡數(shù)據(jù)庫(kù)資源,導(dǎo)致數(shù)據(jù)庫(kù)無(wú)法使用。

解決辦法:

使用同步鎖控制

設(shè)置key永不過(guò)期

無(wú)中生有是穿透,布隆過(guò)濾null隔離。
緩存擊穿key過(guò)期, 鎖與非期解難題。
大量過(guò)期成雪崩,過(guò)期時(shí)間要隨機(jī)。
面試必考三兄弟,可用限流來(lái)保底。

限流技術(shù)方案:

alibaba/Sentinel

nginx+Lua

3.5 分布式鎖

3.5.1 本地鎖的問(wèn)題

上邊的程序使用了同步鎖解決了緩存擊穿、緩存雪崩的問(wèn)題,保證同一個(gè)key過(guò)期后只會(huì)查詢(xún)一次數(shù)據(jù)庫(kù)。

如果將同步鎖的程序分布式部署在多個(gè)虛擬機(jī)上則無(wú)法保證同一個(gè)key只會(huì)查詢(xún)一次數(shù)據(jù)庫(kù),如下圖:

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

一個(gè)同步鎖程序只能保證同一個(gè)虛擬機(jī)中多個(gè)線(xiàn)程只有一個(gè)線(xiàn)程去數(shù)據(jù)庫(kù),如果高并發(fā)通過(guò)網(wǎng)關(guān)負(fù)載均衡轉(zhuǎn)發(fā)給各個(gè)虛擬機(jī),此時(shí)就會(huì)存在多個(gè)線(xiàn)程去查詢(xún)數(shù)據(jù)庫(kù)情況,因?yàn)樘摂M機(jī)中的鎖只能保證該虛擬機(jī)自己的線(xiàn)程去同步執(zhí)行,無(wú)法跨虛擬機(jī)保證同步執(zhí)行。

我們將虛擬機(jī)內(nèi)部的鎖叫本地鎖,本地鎖只能保證所在虛擬機(jī)的線(xiàn)程同步執(zhí)行。

下邊進(jìn)行測(cè)試:

啟動(dòng)三個(gè)內(nèi)容管理服務(wù):

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

通過(guò)網(wǎng)關(guān)訪(fǎng)問(wèn)課程查詢(xún),網(wǎng)關(guān)通過(guò)負(fù)載均衡將請(qǐng)求轉(zhuǎn)發(fā)給三個(gè)服務(wù)。

通過(guò)測(cè)試發(fā)現(xiàn),有兩個(gè)服務(wù)各有一次數(shù)據(jù)庫(kù)查詢(xún),這說(shuō)明本地鎖無(wú)法跨虛擬機(jī)保證同步執(zhí)行。

3.5.2 什么是分布鎖

本地鎖只能控制所在虛擬機(jī)中的線(xiàn)程同步執(zhí)行,現(xiàn)在要實(shí)現(xiàn)分布式環(huán)境下所有虛擬機(jī)中的線(xiàn)程去同步執(zhí)行就需要讓多個(gè)虛擬機(jī)去共用一個(gè)鎖,虛擬機(jī)可以分布式部署,鎖也可以分布式部署,如下圖:

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

虛擬機(jī)都去搶占同一個(gè)鎖,鎖是一個(gè)單獨(dú)的程序提供加鎖、解鎖服務(wù),誰(shuí)搶到鎖誰(shuí)去查詢(xún)數(shù)據(jù)庫(kù)。

該鎖已不屬于某個(gè)虛擬機(jī),而是分布式部署,由多個(gè)虛擬機(jī)所共享,這種鎖叫分布式鎖。

3.5.3 分布式鎖的實(shí)現(xiàn)方案

實(shí)現(xiàn)分布式鎖的方案有很多,常用的如下:

1、基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布鎖

利用數(shù)據(jù)庫(kù)主鍵唯一性的特點(diǎn),或利用數(shù)據(jù)庫(kù)唯一索引的特點(diǎn),多個(gè)線(xiàn)程同時(shí)去插入相同的記錄,誰(shuí)插入成功誰(shuí)就搶到鎖。

2、基于redis實(shí)現(xiàn)鎖

redis提供了分布式鎖的實(shí)現(xiàn)方案,比如:SETNX、set nx、redisson等。

拿SETNX舉例說(shuō)明,SETNX命令的工作過(guò)程是去set一個(gè)不存在的key,多個(gè)線(xiàn)程去設(shè)置同一個(gè)key只會(huì)有一個(gè)線(xiàn)程設(shè)置成功,設(shè)置成功的的線(xiàn)程拿到鎖。

3、使用zookeeper實(shí)現(xiàn)

zookeeper是一個(gè)分布式協(xié)調(diào)服務(wù),主要解決分布式程序之間的同步的問(wèn)題。zookeeper的結(jié)構(gòu)類(lèi)似的文件目錄,多線(xiàn)程向zookeeper創(chuàng)建一個(gè)子目錄(節(jié)點(diǎn))只會(huì)有一個(gè)創(chuàng)建成功,利用此特點(diǎn)可以實(shí)現(xiàn)分布式鎖,誰(shuí)創(chuàng)建該結(jié)點(diǎn)成功誰(shuí)就獲得鎖。

3.5.4 Redis NX實(shí)現(xiàn)分布式鎖

redis實(shí)現(xiàn)分布式鎖的方案可以在redis.cn網(wǎng)站查閱,地址http://www.redis.cn/commands/set.html

使用命令: SET resource-name anystring NX EX max-lock-time 即可實(shí)現(xiàn)。

NX:表示key不存在才設(shè)置成功。

EX:設(shè)置過(guò)期時(shí)間

這里啟動(dòng)三個(gè)ssh客戶(hù)端,連接redis: docker exec -it redis redis-cli

先認(rèn)證: auth redis

同時(shí)向三個(gè)客戶(hù)端發(fā)送測(cè)試命令如下:

表示設(shè)置lock001鎖,value為001,過(guò)期時(shí)間為30秒

SET lock001 001 NX EX 30

命令發(fā)送成功,觀(guān)察三個(gè)ssh客戶(hù)端發(fā)現(xiàn)只有一個(gè)設(shè)置成功,其它兩個(gè)設(shè)置失敗,設(shè)置成功的請(qǐng)求表示搶到了lock001鎖。

如何在代碼中使用Set nx去實(shí)現(xiàn)分布鎖呢?

使用spring-boot-starter-data-redis 提供的api即可實(shí)現(xiàn)set nx。

添加依賴(lài):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.2</version>
</dependency>

添加依賴(lài)后,在bean中注入restTemplate。

我們先分析一段偽代碼如下:

if(緩存中有){

  返回緩存中的數(shù)據(jù)
}else{

  獲取分布式鎖
  if(獲取鎖成功){
       try{
         查詢(xún)數(shù)據(jù)庫(kù)
      }finally{
         釋放鎖
      }
  }
 
}

1、獲取分布式鎖

使用redisTemplate.opsForValue().setIfAbsent(key,vaue)獲取鎖

這里考慮一個(gè)問(wèn)題,當(dāng)set
nx一個(gè)key/value成功1后,這個(gè)key(就是鎖)需要設(shè)置過(guò)期時(shí)間嗎?

如果不設(shè)置過(guò)期時(shí)間當(dāng)獲取到了鎖卻沒(méi)有執(zhí)行finally這個(gè)鎖將會(huì)一直存在,其它線(xiàn)程無(wú)法獲取這個(gè)鎖。

所以執(zhí)行set nx時(shí)要指定過(guò)期時(shí)間,即使用如下的命令

SET resource-name anystring NX EX max-lock-time

具體調(diào)用的方法是:redisTemplate.opsForValue().setIfAbsent(K var1, V
var2, long var3, TimeUnit var5)

2、如何釋放鎖

釋放鎖分為兩種情況:key到期自動(dòng)釋放,手動(dòng)刪除。

1)key到期自動(dòng)釋放的方法

因?yàn)殒i設(shè)置了過(guò)期時(shí)間,key到期會(huì)自動(dòng)釋放,但是會(huì)存在一個(gè)問(wèn)題就是
查詢(xún)數(shù)據(jù)庫(kù)等操作還沒(méi)有執(zhí)行完時(shí)key到期了,此時(shí)其它線(xiàn)程就搶到鎖了,最終重復(fù)查詢(xún)數(shù)據(jù)庫(kù)執(zhí)行了重復(fù)的業(yè)務(wù)操作。

怎么解決這個(gè)問(wèn)題?

可以將key的到期時(shí)間設(shè)置的長(zhǎng)一些,足以執(zhí)行完成查詢(xún)數(shù)據(jù)庫(kù)并設(shè)置緩存等相關(guān)操作。

如果這樣效率會(huì)低一些,另外這個(gè)時(shí)間值也不好把控。

2)手動(dòng)刪除鎖

如果是采用手動(dòng)刪除鎖可能和key到期自動(dòng)刪除有所沖突,造成刪除了別人的鎖。

比如:當(dāng)查詢(xún)數(shù)據(jù)庫(kù)等業(yè)務(wù)還沒(méi)有執(zhí)行完時(shí)key過(guò)期了,此時(shí)其它線(xiàn)程占用了鎖,當(dāng)上一個(gè)線(xiàn)程執(zhí)行查詢(xún)數(shù)據(jù)庫(kù)等業(yè)務(wù)操作完成后手動(dòng)刪除鎖就把其它線(xiàn)程的鎖給刪除了。

要解決這個(gè)問(wèn)題可以采用刪除鎖之前判斷是不是自己設(shè)置的鎖,偽代碼如下:

if(緩存中有){

  返回緩存中的數(shù)據(jù)
}else{

  獲取分布式鎖: set lock 01 NX
  if(獲取鎖成功){
       try{
         查詢(xún)數(shù)據(jù)庫(kù)
      }finally{
         if(redis.call("get","lock")=="01"){
            釋放鎖: redis.call("del","lock")
         }
         
      }
  }
 
}

以上代碼第11行到13行非原子性,也會(huì)導(dǎo)致刪除其它線(xiàn)程的鎖。

查看文檔上的說(shuō)明:http://www.redis.cn/commands/set.html

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

在調(diào)用setnx命令設(shè)置key/value時(shí),每個(gè)線(xiàn)程設(shè)置不一樣的value值,這樣當(dāng)線(xiàn)程去刪除鎖時(shí)可以先根據(jù)key查詢(xún)出來(lái)判斷是不是自己當(dāng)時(shí)設(shè)置的vlaue,如果是則刪除。

這整個(gè)操作是原子的,實(shí)現(xiàn)方法就是去執(zhí)行上邊的lua腳本。

Lua
是一個(gè)小巧的腳本語(yǔ)言,redis在2.6版本就支持通過(guò)執(zhí)行Lua腳本保證多個(gè)命令的原子性。

什么是原子性?

這些指令要么全成功要么全失敗。

以上就是使用Redis
Nx方式實(shí)現(xiàn)分布式鎖,為了避免刪除別的線(xiàn)程設(shè)置的鎖需要使用redis去執(zhí)行Lua腳本的方式去實(shí)現(xiàn),這樣就具有原子性,但是過(guò)期時(shí)間的值設(shè)置不存在不精確的問(wèn)題。

3.5.5 Redisson實(shí)現(xiàn)分布式鎖
3.5.5.1 什么是Redisson

再查閱 文檔http://www.redis.cn/commands/set.html

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

點(diǎn)擊鏈接查看

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

我們選用Java的實(shí)現(xiàn)方案 https://github.com/redisson/redisson

Redisson的文檔地址:https://github.com/redisson/redisson/wiki/Table-of-Content

Redisson底層采用的是Netty
框架。支持Redis
2.8以上版本,支持Java1.6+以上版本。Redisson是一個(gè)在Redis的基礎(chǔ)上實(shí)現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory
Data
Grid)。它不僅提供了一系列的分布式的Java常用對(duì)象,還提供了許多分布式服務(wù)。其中包括(BitSet,
Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque,
BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish /
Subscribe, Bloom filter, Remote service, Spring cache, Executor service,
Live Object service, Scheduler service) 。

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

使用Redisson可以非常方便將Java本地內(nèi)存中的常用數(shù)據(jù)結(jié)構(gòu)的對(duì)象搬到分布式緩存redis中。

也可以將常用的并發(fā)編程工具如:AtomicLong、CountDownLatch、Semaphore等支持分布式。

使用RScheduledExecutorService 實(shí)現(xiàn)分布式調(diào)度服務(wù)。

支持?jǐn)?shù)據(jù)分片,將數(shù)據(jù)分片存儲(chǔ)到不同的redis實(shí)例中。

支持分布式鎖,基于Java的Lock接口實(shí)現(xiàn)分布式鎖,方便開(kāi)發(fā)。

下邊使用Redisson將Queue隊(duì)列的數(shù)據(jù)存入Redis,實(shí)現(xiàn)一個(gè)排隊(duì)及出隊(duì)的接口。

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

添加redisson的依賴(lài)

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.11.2</version>
</dependency>

從課程資料目錄拷貝singleServerConfig.yaml到config工程下

在redis配置文件中添加:

spring:
  redis:
    redisson:
      #配置文件目錄
      config: classpath:singleServerConfig.yaml
      #config: classpath:clusterServersConfig.yaml

redis集群配置clusterServersConfig.yaml.

Redisson相比set nx實(shí)現(xiàn)分布式鎖要簡(jiǎn)單的多,工作原理如下:

微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化),java,# spring,# 微服務(wù),緩存,微服務(wù),redis

加鎖機(jī)制

線(xiàn)程去獲取鎖,獲取成功: 執(zhí)行l(wèi)ua腳本,保存數(shù)據(jù)到redis數(shù)據(jù)庫(kù)。

線(xiàn)程去獲取鎖,獲取失敗:
一直通過(guò)while循環(huán)嘗試獲取鎖,獲取成功后,執(zhí)行l(wèi)ua腳本,保存數(shù)據(jù)到redis

WatchDog自動(dòng)延期看門(mén)狗機(jī)制

第一種情況:在一個(gè)分布式環(huán)境下,假如一個(gè)線(xiàn)程獲得鎖后,突然服務(wù)器宕機(jī)了,那么這個(gè)時(shí)候在一定時(shí)間后這個(gè)鎖會(huì)自動(dòng)釋放,你也可以設(shè)置鎖的有效時(shí)間(當(dāng)不設(shè)置默認(rèn)30秒時(shí)),這樣的目的主要是防止死鎖的發(fā)生

第二種情況:線(xiàn)程A業(yè)務(wù)還沒(méi)有執(zhí)行完,時(shí)間就過(guò)了,線(xiàn)程A
還想持有鎖的話(huà),就會(huì)啟動(dòng)一個(gè)watch
dog后臺(tái)線(xiàn)程,不斷的延長(zhǎng)鎖key的生存時(shí)間。

lua腳本-保證原子性操作

主要是如果你的業(yè)務(wù)邏輯復(fù)雜的話(huà),通過(guò)封裝在lua腳本中發(fā)送給redis,而且redis是單線(xiàn)程的,這樣就保證這段復(fù)雜業(yè)務(wù)邏輯執(zhí)行的原子性

具體使用RLock操作分布鎖,RLock繼承JDK的Lock接口,所以他有Lock接口的所有特性,比如lock、unlock、trylock等特性,同時(shí)它還有很多新特性:強(qiáng)制鎖釋放,帶有效期的鎖,。

public interface RRLock {
    
   //----------------------Lock接口方法-----------------------
    /**
     * 加鎖 鎖的有效期默認(rèn)30秒
     */
    void lock();
    
     /**
     * 加鎖 可以手動(dòng)設(shè)置鎖的有效時(shí)間
     *
     * @param leaseTime 鎖有效時(shí)間
     * @param unit      時(shí)間單位 小時(shí)、分、秒、毫秒等
     */
    void lock(long leaseTime, TimeUnit unit);
    
    /**
     * tryLock()方法是有返回值的,用來(lái)嘗試獲取鎖,
     * 如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他線(xiàn)程獲?。?,則返回false .
     */
    boolean tryLock();
    
    /**
     * tryLock(long time, TimeUnit unit)方法和tryLock()方法是類(lèi)似的,
     * 只不過(guò)區(qū)別在于這個(gè)方法在拿不到鎖時(shí)會(huì)等待一定的時(shí)間,
     * 在時(shí)間期限之內(nèi)如果還拿不到鎖,就返回false。如果如果一開(kāi)始拿到鎖或者在等待期間內(nèi)拿到了鎖,則返回true。
     *
     * @param time 等待時(shí)間
     * @param unit 時(shí)間單位 小時(shí)、分、秒、毫秒等
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    
    /**
     * 比上面多一個(gè)參數(shù),多添加一個(gè)鎖的有效時(shí)間
     *
     * @param waitTime  等待時(shí)間
     * @param leaseTime 鎖有效時(shí)間
     * @param unit      時(shí)間單位 小時(shí)、分、秒、毫秒等
     * waitTime 大于 leaseTime
     */
    boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
    
    /**
     * 解鎖
     */
    void unlock();
}

lock()

此方法為加鎖,但是鎖的有效期采用默認(rèn)30秒

如果主線(xiàn)程未釋放,且當(dāng)前鎖未調(diào)用unlock方法,則進(jìn)入到watchDog機(jī)制

如果主線(xiàn)程未釋放,且當(dāng)前鎖調(diào)用unlock方法,則直接釋放鎖

3.5.5.2 分布式鎖避免緩存擊穿

下邊使用分布式鎖修改查詢(xún)課程信息的接口。

//Redisson分布式鎖
public  CoursePublish getCoursePublishCache(Long courseId){
        //查詢(xún)緩存
        String jsonString = (String) redisTemplate.opsForValue().get("course:" + courseId);
        if(StringUtils.isNotEmpty(jsonString)){
            if(jsonString.equals("null")){
                return null;
            }
            CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class);
            return coursePublish;
        }else{
            //每門(mén)課程設(shè)置一個(gè)鎖
            RLock lock = redissonClient.getLock("coursequerylock:"+courseId);
            //獲取鎖
            lock.lock();
            try {
                jsonString = (String) redisTemplate.opsForValue().get("course:" + courseId);
                if(StringUtils.isNotEmpty(jsonString)){
                    CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class);
                    return coursePublish;
                }
                System.out.println("=========從數(shù)據(jù)庫(kù)查詢(xún)==========");
                //從數(shù)據(jù)庫(kù)查詢(xún)
                CoursePublish coursePublish = getCoursePublish(courseId);
                redisTemplate.opsForValue().set("course:" + courseId, JSON.toJSONString(coursePublish),1,TimeUnit.DAYS);
                return coursePublish;
            }finally {
                //釋放鎖
                lock.unlock();
            }
        }


}

啟動(dòng)多個(gè)內(nèi)容管理服務(wù)實(shí)例,使用JMeter壓力測(cè)試,只有一個(gè)實(shí)例查詢(xún)一次數(shù)據(jù)庫(kù)。

測(cè)試Redisson自動(dòng)續(xù)期功能。

在查詢(xún)數(shù)據(jù)庫(kù)處添加休眠,觀(guān)察鎖是否會(huì)自動(dòng)續(xù)期。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-653337.html

try {
    Thread.sleep(60000);
} catch (InterruptedException e) {
    throw new RuntimeException(e);
}

到了這里,關(guān)于微服務(wù)實(shí)戰(zhàn)項(xiàng)目-學(xué)成在線(xiàn)-項(xiàng)目?jī)?yōu)化(redis緩存優(yōu)化)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀(guān)點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 《微服務(wù)實(shí)戰(zhàn)》 第十七章 Redis下載與安裝

    《微服務(wù)實(shí)戰(zhàn)》 第十七章 Redis下載與安裝

    第二十八章 分布式鎖框架-Redisson 第二十四章 Spring boot 操作 Redis 第二十三章 Redis RDB AOF 第二十一、二十二章 Redis發(fā)布訂閱、事務(wù);HyperLoglog基數(shù)統(tǒng)計(jì) 第二十章 Redis連接指令 客戶(hù)端指令 服務(wù)器指令 第十九章 Redis key 第十八章 Redis查看配置文件和數(shù)據(jù)類(lèi)型 第十七章 Redis下載與安

    2024年02月06日
    瀏覽(27)
  • 微服務(wù)實(shí)戰(zhàn)項(xiàng)目_天機(jī)學(xué)堂01_初識(shí)項(xiàng)目

    微服務(wù)實(shí)戰(zhàn)項(xiàng)目_天機(jī)學(xué)堂01_初識(shí)項(xiàng)目

    Q:天機(jī)學(xué)堂是什么? A:天機(jī)學(xué)堂是一個(gè) 基于微服務(wù)架構(gòu) 的生產(chǎn)級(jí) 在線(xiàn)教育 項(xiàng)目 主要有兩個(gè)端(項(xiàng)目已上線(xiàn),可以點(diǎn)擊查看): 管理后臺(tái): https://tjxt-admin.itheima.net 其核心業(yè)務(wù)主體包括老師、管理員、其他員工,核心業(yè)務(wù)圍繞著老師展開(kāi) 用戶(hù)端:鏈接: https://tjxt-user.itheima.net/#/main/ind

    2024年01月17日
    瀏覽(24)
  • 《微服務(wù)實(shí)戰(zhàn)》 第二十六章 Java鎖的分類(lèi)

    《微服務(wù)實(shí)戰(zhàn)》 第二十六章 Java鎖的分類(lèi)

    第二十八章 分布式鎖框架-Redisson 第二十七章 CAS 第二十六章 Java鎖的分類(lèi) 第二十五章 Java多線(xiàn)程安全與鎖 第二章 CountDownLatch和Semaphone的應(yīng)用 第一章 Java線(xiàn)程池技術(shù)應(yīng)用 本章節(jié)介紹Java中的幾種常見(jiàn)的鎖:公平鎖和非公平鎖、可重入鎖、獨(dú)享鎖/共享鎖、互斥鎖/讀寫(xiě)鎖、樂(lè)觀(guān)鎖

    2024年02月06日
    瀏覽(30)
  • [golang gin框架] 39.Gin商城項(xiàng)目-微服務(wù)實(shí)戰(zhàn)之微服務(wù)架構(gòu)

    [golang gin框架] 39.Gin商城項(xiàng)目-微服務(wù)實(shí)戰(zhàn)之微服務(wù)架構(gòu)

    單體架構(gòu)在 中小企業(yè)內(nèi)部 用的是非常多的,當(dāng) 業(yè)務(wù)不復(fù)雜 , 團(tuán)隊(duì)規(guī)模不大 的時(shí)候,單體架構(gòu)比微服務(wù)架構(gòu)具有 更高的生產(chǎn)率 單體架構(gòu) 當(dāng) 業(yè)務(wù)比較復(fù)雜 , 并發(fā)量 比較大, 團(tuán)隊(duì)規(guī)模擴(kuò)大的時(shí)候, 就需要引入微服務(wù)架構(gòu)了,它比單體架構(gòu) 具有 更高的生產(chǎn)率, 可以 節(jié)省成本 , 解

    2024年02月12日
    瀏覽(34)
  • 黑馬學(xué)成在線(xiàn)--項(xiàng)目環(huán)境搭建

    黑馬學(xué)成在線(xiàn)--項(xiàng)目環(huán)境搭建

    完整版請(qǐng)移步至我的個(gè)人博客查看:https://cyborg2077.github.io/ 學(xué)成在線(xiàn)–項(xiàng)目環(huán)境搭建 學(xué)成在線(xiàn)–內(nèi)容管理模塊 學(xué)成在線(xiàn)–媒資管理模塊 學(xué)成在線(xiàn)–課程發(fā)布模塊 學(xué)成在線(xiàn)–認(rèn)證授權(quán)模塊 學(xué)成在線(xiàn)–選課學(xué)習(xí)模塊 學(xué)成在線(xiàn)–項(xiàng)目?jī)?yōu)化 Git倉(cāng)庫(kù):https://github.com/Cyborg2077/xuecheng

    2023年04月15日
    瀏覽(26)
  • 學(xué)成在線(xiàn)項(xiàng)目note

    學(xué)成在線(xiàn)項(xiàng)目note

    目錄 一、index.html 1、頭部header 2、輪播圖banner 3、精品推薦 4、精品推薦課程 ?5、footer 二、index.css 1、重要的代碼 !-- 網(wǎng)站的首頁(yè), 所有網(wǎng)站的首頁(yè)都叫index.html, 因?yàn)榉?wù)器找首頁(yè)都是找index.html -- ?!-- 布局: 從外到內(nèi), 從上到下, 從左到右 -- ?link rel=\\\"stylesheet\\\" href=\\\"./css/index.css

    2024年02月07日
    瀏覽(25)
  • 緩存優(yōu)化--使用Redis將項(xiàng)目進(jìn)行優(yōu)化

    問(wèn)題描述: 用戶(hù)數(shù)量多,系統(tǒng)訪(fǎng)問(wèn)量大的時(shí)候,用戶(hù)發(fā)送大量的請(qǐng)求,導(dǎo)致頻繁訪(fǎng)問(wèn)數(shù)據(jù)庫(kù),系統(tǒng)性能下降,用戶(hù)體驗(yàn)差。 maven坐標(biāo) 在項(xiàng)目的pom.xml文件中導(dǎo)入spring data redis的maven坐標(biāo): 配置文件 在項(xiàng)目的application. ym1中加入redis相關(guān)配置: 配置類(lèi) 在項(xiàng)目中加入配置類(lèi)Redisconfig

    2024年02月11日
    瀏覽(16)
  • [golang gin框架] 40.Gin商城項(xiàng)目-微服務(wù)實(shí)戰(zhàn)之Captcha驗(yàn)證碼微服務(wù)

    [golang gin框架] 40.Gin商城項(xiàng)目-微服務(wù)實(shí)戰(zhàn)之Captcha驗(yàn)證碼微服務(wù)

    本次內(nèi)容需要 gin框架基礎(chǔ)知識(shí), golang微服務(wù)基礎(chǔ)知識(shí)才能更好理解 在前面,講解了微服務(wù)的架構(gòu)等,這里,來(lái)講解前面商城項(xiàng)目的 Captcha驗(yàn)證碼 微服務(wù) ,captcha驗(yàn)證碼功能在前臺(tái),后端 都要用到 ,可以把它 抽離出來(lái) ,做成微服務(wù)功能 編輯 這個(gè)驗(yàn)證碼功能封裝代碼captcha.go如下: 把這個(gè)

    2024年02月16日
    瀏覽(30)
  • 【前端實(shí)戰(zhàn)小項(xiàng)目】學(xué)成在線(xiàn)網(wǎng)頁(yè)制作

    【前端實(shí)戰(zhàn)小項(xiàng)目】學(xué)成在線(xiàn)網(wǎng)頁(yè)制作

    網(wǎng)站根目錄 是指存放網(wǎng)站的 第一層 文件夾,內(nèi)部包含當(dāng)前網(wǎng)站的 所有素材 ,包含HTML、CSS、圖片、JavaScript等等。 項(xiàng)目文件夾(如study) images 文件夾:存放 固定使用 的圖片素材,例如: logo、樣式修飾圖等等 uploads 文件夾:存放 非固定使用 的圖片素材,例如:商品圖、宣傳圖需要

    2024年02月21日
    瀏覽(26)
  • [golang gin框架] 45.Gin商城項(xiàng)目-微服務(wù)實(shí)戰(zhàn)之后臺(tái)Rbac微服務(wù)之角色權(quán)限關(guān)聯(lián)

    [golang gin框架] 45.Gin商城項(xiàng)目-微服務(wù)實(shí)戰(zhàn)之后臺(tái)Rbac微服務(wù)之角色權(quán)限關(guān)聯(lián)

    角色和權(quán)限的關(guān)聯(lián)關(guān)系在前面文章中有講解,見(jiàn)[golang gin框架] 14.Gin 商城項(xiàng)目-RBAC管理之角色和權(quán)限關(guān)聯(lián),角色授權(quán),在這里通過(guò)微服務(wù)來(lái)實(shí)現(xiàn) 角色對(duì)權(quán)限的授權(quán) 操作,這里要實(shí)現(xiàn)的有兩個(gè)功能,一個(gè)是進(jìn)入授權(quán),另一個(gè)是,授權(quán)提交操作,頁(yè)面如下: ?這里需要在proto/rbacRole.proto中增加

    2024年02月14日
    瀏覽(30)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包