摘要:完善緩存擊穿問題問題描述,分布式場景中,有時(shí)候存在高并發(fā)訪問,比如秒殺活動。在高并發(fā)訪問下請求全部懟到數(shù)據(jù)庫,可能導(dǎo)致數(shù)據(jù)庫掛掉,這就是緩存擊穿。緩存擊穿解決方案已經(jīng)能解決日常情況,但還是有一定提升的空間的。
“做人、做程序,變則通,不變只能一直死循環(huán)下去” ————尼古斯拉Docker安裝官方Redis
參考文章:Docker安裝官方Redis鏡像并啟用密碼認(rèn)證
拉取最新版的redis鏡像:docker pull redis:latest
啟動容器并帶密碼:
docker run --name redis-test -p 6379:6379 -d --restart=always redis:latest redis-server --appendonly yes --requirepass "your passwd"
查看容器、注意看id: docker ps
查看進(jìn)程: ps -ef|grep redis
進(jìn)入容器執(zhí)行redis客戶端:
docker exec -it a126ec987cfe redis-cli -h yourhost -p 6379 -a "your passwd" 127.0.0.1:6379> ping PONG 127.0.0.1:6379> info ...
能ping通即可正常使用,這個(gè)例子比起原先方式,省去了編譯、配置、開機(jī)啟動服務(wù)一大堆麻煩。docker就是好,docker就是棒,docker頂呱呱。
廣告緩存功能的實(shí)現(xiàn) 添加依賴添加配置org.springframework.boot spring-boot-starter-data-redis 2.1.3.RELEASE
# Redis服務(wù)器地址 spring.redis.host=yourhost # Redis服務(wù)器連接端口 spring.redis.port=6379 # Redis服務(wù)器連接密碼(默認(rèn)為空) spring.redis.password=xxxx # Redis數(shù)據(jù)庫索引(默認(rèn)為0) spring.redis.database=0 # 連接池最大連接數(shù)(使用負(fù)值表示沒有限制) spring.redis.jedis.pool.max-active=8 # 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒有限制) spring.redis.jedis.pool.max-wait=-1 # 連接池中的最大空閑連接 spring.redis.jedis.pool.max-idle=8 # 連接池中的最小空閑連接 spring.redis.jedis.pool.min-idle=0 # 連接超時(shí)時(shí)間(毫秒) spring.redis.timeout=0緩存功能核心邏輯代碼
@Service public class ContentServiceImpl implements ContentService { @Autowired private ContentMapper contentMapper; @Autowired private StringRedisTemplate redis; @Override public Object contentList(Long cid) throws Exception { String test= (String) redis.opsForHash().get("ContentList", cid.toString()); if(test==null) { System.out.println("緩存未命中,執(zhí)行SQL"); ContentExample example=new ContentExample(); example.createCriteria().andCategoryIdEqualTo(cid); List功能測試、調(diào)錯(cuò)list=contentMapper.selectByExample(example); test=new ObjectMapper().writeValueAsString(list); redis.opsForHash().put("ContentList", cid.toString(), test); return test; } System.out.println("緩存命中,直接使用"); return test; } }
首次請求:
二次請求、多次請求:
Tips:
有時(shí)會報(bào)redis連接超時(shí)connect timed out,把鏈接超時(shí)時(shí)間改大一點(diǎn)就好了,建議200以上,一個(gè)小bug,不作過多闡述。完善緩存擊穿問題
問題描述,分布式場景中,有時(shí)候存在高并發(fā)訪問,比如秒殺活動?;蚴怯泻诳兔看喂室獠樵円粋€(gè)在緩存內(nèi)必然不存在的數(shù)據(jù),導(dǎo)致每次請求都要去存儲層去查詢,這樣緩存就失去了意義。在高并發(fā)訪問下請求全部懟到數(shù)據(jù)庫,可能導(dǎo)致數(shù)據(jù)庫掛掉,這就是緩存擊穿。
場景如下圖所示:
方法一:使用synchronized
public Object contentList(Long cid) throws Exception { String test= null; synchronized (redis) { test=(String) redis.opsForHash().get("ContentList", cid.toString()); } if(test==null) { System.out.println("緩存未命中,執(zhí)行SQL"); ContentExample example=new ContentExample(); example.createCriteria().andCategoryIdEqualTo(cid); Listlist=contentMapper.selectByExample(example); test=new ObjectMapper().writeValueAsString(list); redis.opsForHash().put("ContentList", cid.toString(), test); return test; } System.out.println("緩存命中,直接使用"); return test; }
方法二:使用互斥鎖
@Override public Object contentList(Long cid) throws Exception { String test= (String) redis.opsForHash().get("ContentList", cid.toString()); if(test==null) { System.out.println("緩存未命中,執(zhí)行SQL"); if(redis.opsForValue().setIfAbsent("key", "value")){ redis.expire("key", 30000, TimeUnit.MILLISECONDS); ContentExample example=new ContentExample(); example.createCriteria().andCategoryIdEqualTo(cid); Listlist=contentMapper.selectByExample(example); test=new ObjectMapper().writeValueAsString(list); redis.opsForHash().put("ContentList", cid.toString(), test); redis.delete("key"); return test; } } System.out.println("緩存命中,直接使用"); return test; }
方法二原理就第一個(gè)請求進(jìn)來執(zhí)行redis.opsForValue().setIfAbsent("key", "value")沒有值為true才執(zhí)行業(yè)務(wù)邏輯。如果沒有執(zhí)行完業(yè)務(wù)邏輯、delete("key")。第二個(gè)請求就會查到有值為flase,那是進(jìn)不去的,相當(dāng)于被鎖住了。
使用redis.expire("key", 30000, TimeUnit.MILLISECONDS)為了防止調(diào)用setIfAbsent方法之后線程掛掉了,沒有執(zhí)行到delete("key")這一步。這樣的話,如果沒有給鎖設(shè)置過期的時(shí)間,默認(rèn)會永不過期。那么這個(gè)鎖就會一直存在,想查庫也查不到了。
評價(jià):這兩個(gè)解決方案已經(jīng)能應(yīng)對日程大部分情況。方案一一存在一定性能損耗,方案二在極端情況下有死鎖的可能性,那么還是使用方案二吧。
完善數(shù)據(jù)同步的問題問題描述:如果廣告內(nèi)容改變了,即數(shù)據(jù)庫內(nèi)容已經(jīng)改變的,但請求還是從原來的緩存里拿數(shù)據(jù),這顯然是不對的,所以我們更改數(shù)據(jù)庫時(shí)要把緩存清一清。
public Object contentSave(Content content) { Date date =new Date(); content.setCreated(date); content.setUpdated(date); contentMapper.insert(content); redis.delete("ContentList"); return Result.of(200, "添加成功"); }總結(jié)
這個(gè)小案例碰到了不少問題,值得一提的是在云上安裝redis安裝了好幾次都不對,最后改用docker才安成了,做程序還是得學(xué)會靈活變通呀。緩存擊穿解決方案已經(jīng)能解決日常99.9%情況,但還是有一定提升的空間的。加油吧,騷年。
最后,寫這樣一篇文章真的好費(fèi)時(shí)間,有緣人記得點(diǎn)贊哦,筆芯!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/27689.html
摘要:若其他線程發(fā)現(xiàn)獲取鎖失敗,則睡眠后重試。容易造成死鎖問題布隆過濾器,迅速判斷一個(gè)元素是否在一個(gè)集合中。將已存在的緩存放到布隆過濾器中,當(dāng)黑客訪問不存在的緩存時(shí)迅速返回避免緩存及掛掉。 redis理解 A.執(zhí)行流程 showImg(https://user-gold-cdn.xitu.io/2019/1/11/1683aabdeb2fcc3a); 緩存雪崩 showImg(https:/...
摘要:分布式鎖的作用在單機(jī)環(huán)境下,有個(gè)秒殺商品的活動,在短時(shí)間內(nèi),服務(wù)器壓力和流量會陡然上升。分布式集群業(yè)務(wù)業(yè)務(wù)場景下,每臺服務(wù)器是獨(dú)立存在的。這里就用到了分布式鎖這里簡單介紹一下,以的事務(wù)機(jī)制來延生。 Redis 分布式鎖的作用 在單機(jī)環(huán)境下,有個(gè)秒殺商品的活動,在短時(shí)間內(nèi),服務(wù)器壓力和流量會陡然上升。這個(gè)就會存在并發(fā)的問題。想要解決并發(fā)需要解決以下問題 1、提高系統(tǒng)吞吐率也就是qps 每...
閱讀 573·2021-08-31 09:45
閱讀 1688·2021-08-11 11:19
閱讀 918·2019-08-30 15:55
閱讀 854·2019-08-30 10:52
閱讀 2895·2019-08-29 13:11
閱讀 2959·2019-08-23 17:08
閱讀 2867·2019-08-23 15:11
閱讀 3104·2019-08-23 14:33