<一> redis-緩存中間件
-
什么是redis
redis是c語言開發的,一個高性能key-value鍵值對內存數據庫,可以用來做數據庫、緩存、消息中間件的一種非關系型數據庫。 -
redis數據存儲在哪里
內存和磁盤中,但是redis的讀寫都在內存中,這也是速度快的原因之一。
對于高頻繁訪問的數據,存儲在內存中方便訪問。
為了避免服務宕機或者重啟數據丟失,也可以將數據存儲在磁盤中持久化。 -
redis的數據類型
String、set、zset、list、hash -
redis為什么響應速度快
1) 基于內存存儲,redis的讀寫都是基于內存的key-value操作。內存遠大于磁盤響應速度。
2) 單線程操作,避免了多線程cpu來回切換和各種加鎖和解鎖的資源消耗。
redis的單線程操作是指一個模塊的操作單線程,其他該多線程的還是使用多線程。
3)豐富的數據結構
redis是基于key-velue鍵值對存儲的緩存數據庫,對于key都是string類型,但是vlue有多種數據類型
4)高性能的多復用IO模型 -
為什么需要使用redis
redis的響應速度快讀10w+,寫8w,對于一些業務場景(可以將計算的結果-菜單等存儲到緩存中,下次直接getfromredis) -
redis怎么配置、步驟
1) pom文件加上依賴
org.springframework.boot
spring-boot-starter-data-redis
io.lettuce
lettuce-core
2) 配置文件加上redis服務器的host port user psw等信息
3)使用springboot自動配置好的StringRedisTemplete工具來操作redis進行讀寫操作,
@Autowired
StringRedisTemplete stringRedisTemplete;
// 讀
stringRedisTemplete.opsForValue.get(“keyName”)
// 寫
stringRedisTemplate.opsForValue().set(“keyName”, value, 1, TimeUnit.DAYS); // 設置過期時間 防止緩存數據與數據庫不一致
-
redis怎么進行get,set,讀寫數據
使用StringRedisTemplete .get(“keyname”) /.set(“keyname”,“value”,“time”,“時間單位”) -
redis有哪些參數
key-String
value-list,string、set、zset、hash
過期時間-過期刪除,重新sql獲取set到緩存中,與數據庫數據保持一致
過期時間的單位 -
redis緩存容易出現的問題
1)緩存穿透
reason: 大量并發請求一個不存在的數據,到緩存中null,之后大量請求到了數據庫中,數據庫壓力過大崩了。這種是緩存穿透。
way: 即使數據庫中為null的數據,也存儲一個標識到緩存中,并且設置一個過期時間。
2)緩存雪崩
r:大量不同業務模塊的請求到緩存,但是對應的數據同時過期了,導致所有的請求到db-壓力大。
w:設置不同的過期時間,可以在配置文件中統一配置,因為不同業務key存儲的時間不同,過期點也不會相同。
3)緩存擊穿
r:大量惡意請求到一個熱點詞,剛好這個key過期了。db壓力過大
w:加鎖-記得雙重驗證,每次只允許一個請求進入db-get->db,其他沒有進去鎖的去訪問緩存即可。
-
本地鎖(不是redis分布式鎖,加關鍵字Synchronized等)
本地鎖只能鎖住同一個進程里面的線程,如果是分布式下,即使每個服務僅僅放一個線程進來,相互是隔離的,不能只鎖住一個。這個時候需要分布式鎖-redisson(使用redis分布式鎖-沒有看門狗機制) -
redis怎么保證與數據庫數據一致(更新數據庫)
1)雙寫模式
先更新數據庫,再去更新緩存對應的key,這種緩存中可能會短暫出現臟數據,穩定后緩存過期-重新讀取db即可保證最新數據。
2)失效模式
先寫數據庫,后刪除緩存。這種也可能短暫出現緩存有臟數據,穩定后即可無論是更新緩存還是刪除緩存都會有臟數據,解決如下
1)失效模式-使用雙刪模式
先刪除緩存->更新db-隔幾秒再去刪除緩存
2)使用MQ消息隊列,消費者監聽queue消息去刪除緩存
生產者(更新db)->MQ->queue有數據->customer監聽到->刪除緩存->告知queue是否刪除成功,失敗queue再次發送。(不需要寫代碼,mq本身會重復出隊。解耦修改數據庫和查詢數據庫解耦,不會造成緩存有臟數據)
11.緩存鎖使用的原則
正常數據,緩存加上過期時間,就可以實現數據一致性。
對于實時性要求高的,最好是到數據庫中讀取。
對于頻繁修改的數據,不建議加讀寫鎖,性能會慢。
<二> redisson-分布式鎖
-
redisson是什么
redisson是基于redis的分布式框架,提供了分布式鎖等服務 -
為什么需要分布式鎖
本地鎖只能鎖住當前進程-一個服務的線程,所以需要分布式鎖。 -
redission配置步驟
1)pom文件加上依賴(前提是有redis依賴)
org.redisson
redisson
3.12.0
2)配置redis配置信息 -
redisson提供的分布式鎖與redis自己的分布式鎖區別
1)redisson提供的分布式鎖
-分布式下也只能有一個線程占用鎖,執行業務代碼,直至解鎖,下一個thread才能進來執行。
使用RedissonClient加鎖—不會出現死鎖----鎖會自動過期
@Autowried
RedissonClient client;
// 1. 獲取鎖mylock 只要鎖的名字相同 就是同一把鎖
RLock myLock = redisson.getLock(“myLock”);
// 2. 加鎖
myLock.lock(); // 阻塞鎖
// 業務代碼
try {
System.out.println(“加鎖成功,執行業務…” + Thread.currentThread().getId());
Thread.sleep(3000);
} catch (Exception e) {
} finally {
// 3. 解鎖
// 即使業務代碼執行失敗或者斷電,沒有解鎖成功,也不會出現死鎖,
// RedissonClient不會出現死鎖
System.out.println(“釋放鎖” + Thread.currentThread().getId());
myLock.unlock();
}
2)redis提供的分布式鎖
使用StringRedisTemplate加鎖
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(
“lockValue”,
uuid,
5,
TimeUnit.SECONDS);
3) redis-StringRedisTemplate分布式鎖與redisson-RedissonClient 分布式鎖區別。
區別在于
1.–redisson分布式鎖有看門狗機制,一個線程占用鎖之后,如果鎖沒有釋放之前,redisson會自動給鎖續期(10s檢查一次鎖是否釋放,續期time-30s),不會過期,直至線程結束鎖釋放。
2- -redis 的stringRedisTemplate分布式鎖不會自動續期。
redisson加鎖的業務代碼只要完成,就不會自動給鎖續期,即使不unlock,鎖默認在30s后自動刪除。即使服務宕機-業務結束,也不會出現死鎖,鎖會自動過期。
-
讀寫鎖相關
// 6.1 讀寫鎖(可以獲read-write)
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(“catalogJson-lock”);
// 6.2 寫鎖
RLock wLock = readWriteLock.writeLock();
// 6.3 讀鎖
RLock rLock = lock.readLock();
注意
1)一個讀鎖可以被多個thread線程占用不互斥。
2)寫鎖與其他鎖都互斥,一次只能有一個線程占用鎖,其他寫或者讀必須等待。 -
分布式鎖的幾個參數
創建鎖的過期時間,時間單位,redisson分布式鎖默認是10s查看一次鎖是否還在,如果在自動延長30s(前提是沒有手動設置過期時間) -
redisson分布式鎖的特點
1)一次只能有一個進程的一個線程進來
2)有看門狗機制,redisson–自動續期30s,業務未執行完,其他鎖進來造成數據紊亂
3)自動過期-避免死鎖(什么是死鎖,加鎖->執行業務->執行完解鎖,沒有解鎖業務代碼出異常或者服務掛了,鎖就一直存在redis緩存數據庫中-死鎖) -
redisson看門狗機制(給線程看門,線程進去執行業務,redisson看門)
redisson-redissonClient提供的分布鎖,一旦某個線程進入到占了鎖,在業務代碼沒有執行完成,釋放鎖之前,redisson沒10s會自動檢測鎖是否還在,并續期30s。直至業務完成,解鎖,不再自動續期。這種情況只針對沒有手動配置過期時間的前提,如果配置了則失效。 -
redission的可重入鎖機制
Redisson 可重入鎖是一種分布式鎖,它基于 Redis 實現。可重入指的是同一個線程在持有鎖的情況下,可以多次獲取該鎖而不會造成死鎖。
<三>SpringCache-分布式緩存使用
-
springcache是什么
spring提供的一個緩存框架,使用一些注解解決緩存的問題,不需要關心底層代碼邏輯如何實現 -
使用redis作為緩存工具,cache使用步驟
1)pom文件加上依賴
引入依賴 spring-boot-starter-cache
spring-boot-starter-data-redis
2)手動在配置文件加上緩存類型(其他配置后面使用場景再說)
spring.cache.type=redis
3)開啟緩存@EnableCaching // 開啟緩存,加在對應Configuration類上面
3.springcache 主要的注解
1)@EnableCaching-開啟緩存
2)@Cacheable 加上方法上面,觸發方法更新緩存(如果緩存不是null則不存儲db數據到緩存中)
3)@CacheEvict,更新數據庫觸發刪除緩存對應的數據-失效模式
4)@CachePut,更新數據庫,在不影響更新數據庫的情況下,更新緩存數據-雙寫模式。
-
springcache有哪些不足之處?該緩存框架默認不加鎖
1)讀模式(@Cacheable-讀db->set->redis)
只要涉及讀db數據到redis緩存,且不加鎖的情況,大并發就會暴露redis存儲缺陷。
a. 緩存穿透-開啟db空值也存到redis中
配置文件加
spring.cache.redis.cache-null-values=trueb. 緩存雪崩-配置設置緩存的過期時間
spring.cache.redis.time-to-live=600000c. 緩存擊穿-加鎖
springCache默認是不加分布式鎖的,可以對應方法上
@Cacheable注解上面加上sync = “true”,線程加鎖-一次只允許一個線程占用鎖訪問db-避免熱點擊穿db。
@Cacheable(value = “category”, key = “#root.method.name”, sync = true)2)寫模式
數據寫模式對應的是更新數據,勢必涉及更新db與更新緩存,兩者存在數據一致性問題。
解決方法:對于讀多寫少的數據,直接使用springcache緩存即可,key設置過期時間,查詢觸發再次更新db數據到緩存redis中。
總結
redis-緩存中間件-暫時的非關系key-value鍵值對數據庫,StringRedisTemplate操作。
redisson-基于redis操作的數據庫,提供分布式鎖,解決分布式下操作redis缺陷。-使用redissonClient操作分布式鎖(單體不需要redisson,本地鎖就可以解決)
springcache-spring提供的分布式緩存框架,可以使用多種緩存中間件存儲數據,本文type是redis,直接使用一些注解實現redis讀-存儲,寫-寫/刪數據。不需要再單獨使用redis執行一些列繁瑣的代碼實現。