前言
Redis的各種思想跟機組Cache和操作系統對進程的管理非常類似!
一:看到你的簡歷上寫了你的項目里面用到了redis,為啥用redis?
因為傳統的關系型數據庫如Mysql,已經不能適用所有的場景,比如秒殺的庫存扣減,APP首頁的訪問流量高峰等,都很容易把數據庫打崩,所以引入了緩存中間件,redis。
Redis是用C語言開發的一個開源的高性能鍵值對(key-value)數據庫;跟機組內存中的cache很相似。
集群:復制模式,每個機器做同一件事
分布式:每臺機器分工合作,做的不是同一件事。
二、Redis數據類型
字符串類型String
哈希類型hash
列表類型
集合類型
有序集合類型
三:Redis的使用
1:下載軟件
2:Spring-data-redis:提供了通過簡單配置訪問redis服務,對redis底層開發包jedis進行了高度封裝,RedisTemplate類提供了redis各種操作。
? ? ?redisTemplate.opsForValue().set("name", "gao1");
? ??redisTemplate.opsForList().rightPush("nameList1", "劉備");
? ??redisTemplate.opsForHash().put("nameHash", "c", "八戒");
四、Redis常見問題
1:緩存穿透:惡意請求或者頻繁查詢一個不存在的數據,導致每次請求都要訪問數據庫。
解決:緩存空對象,并設置合適的過期時間。
2:緩存擊穿:在高并發的情況下,一個熱點數據的緩存過期或被刪除,恰好有大量的請求同時訪問該數據,導致無法從緩存中獲取數據,從而直接訪問數據庫。
解決:互斥鎖;單機環境用synchronized,集群環境則使用分布式鎖(redis的setnx,Redisson)
3:緩存雪崩:在同一時段大量的緩存Key同時失效或者Redis服務宕機,導致大量請求到達數據庫。(與緩存擊穿的區別,雪崩是很多key,擊穿是某一個key緩存)
解決:合理設置緩存過期時間,給不同的key的·TTL添加隨機值;建立redis集群
緩存預熱:緩存預熱是指在系統啟動或高峰期之前,提前將一些熱點數據加載到緩存中,以減少后續請求對數據庫的訪問壓力,提高系統的性能和響應速度
五、緩存擊穿問題解決
分布式環境下解決:使用 Redisson 會更加合適。
1:引入依賴;配置文件??
//1、獲取一把鎖,只要鎖的名字一樣,就是同一把鎖RLock lock = redissonClient.getLock("my-lock");//2、加鎖 阻塞式等待(沒有獲得鎖的線程都要等待)。默認加的鎖都是30s,lockWatchdogTimeout = 30 * 1000 【看門狗默認時間】lock.lock();//1)、鎖的自動續期,如果業務超長,運行期間自動續期,不用擔心業務時間長,鎖自動過期被刪掉//只要占鎖成功,就會啟動一個定時任務【重新給鎖設置過期時間,新的過期時間就是看門狗的默認時間】,每隔10秒都會自動的再次續期,續成30秒// internalLockLeaseTime 【看門狗時間】 / 3, 10s//2)、加鎖的業務只要運行完成,就不會給當前鎖續期,即使不手動解鎖,鎖默認會在30s內自動過期,不會產生死鎖問題try {System.out.println("加鎖成功,執行業務..." + Thread.currentThread().getId());try { TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }} finally {//3、解鎖 假設解鎖代碼沒有運行,Redisson會不會出現死鎖System.out.println("釋放鎖..." + Thread.currentThread().getId());lock.unlock();}return "hello"; }
六、redis與數據庫不一致問題
失效模式:修改數據庫后,刪除緩存后面再重新建立緩存。
雙寫模式:修改數據庫后,修改緩存。
七、SpringCache簡化緩存開發
springcache提供了一些核心概念和注解來管理緩存。
? ? Cache Manager? :是一個緩存的抽象接口。
? ? Cache:基本操作單元,定義了常見的緩存操作,例如get,put,evict(刪除)
? ?緩存注解:
@Cacheable:觸發將數據放入緩存的操作。
@CacheEvict:觸發將數據從緩存刪除的操作。失效模式
@CachePut:不影響方法執行更新緩存。雙寫模式。
// value:分組,區分不同模塊的緩存 //@Cacheable(value = "product", key = "'selectById:' + #id") @Cacheable(value = "product", key = "#root.methodName + ':' + #id") @Override public Product selectById(Long id) {return productMapper.selectById(id); }
//加鎖解決緩存擊穿問題:sync = true //@Cacheable(value = "product", key = "'selectById'") //多個參數這種方式用“,”分割就不用字符串拼接了, key = "{#root.methodName, #id, #name}" @Cacheable(value = "product", key = "#root.methodName + ':' + #id", sync = true) @Override public Product selectById(Long id) {return productMapper.selectById(id); }//@CacheEvict(value = "product", allEntries = true) //刪除分區中所有數據 @CacheEvict(value = "product", key = "'selectById:' + #product.id") @Override public void update(Product product) {productMapper.updateById(product); }
八、Redis持久化機制
1:RDB:Redis Database Backup file(Redis數據備份文件),也被叫做Redis數據快照。
簡單來說就是把內存中的所有數據都記錄到磁盤中。當Redis實例故障重啟后,從磁盤讀取快照文件,恢復數據。
2:AOF:Append Only File(追加文件)
Redis處理的每一個寫命令都會記錄在AOF文件,可以看做是命令日志文件(不記錄讀命令)。
AOF默認是關閉的,需要修改redis.conf配置文件來開啟AOF
AOF重寫
比如我們有個計數的服務,有很多自增的操作,比如有一個key自增到1個億,對AOF文件來說就是一億次incr。AOF重寫就只用記1條記錄。
九、Redis過期數據刪除策略
???????惰性刪除?
只會在取出key的時候才對數據進行過期檢查,如果過期,我們就刪掉它,反之返回該key。
優點 :對CPU友好,只會在使用該key時才會進行過期檢查,對于很多用不到的key不用浪費時間進行過期檢查
缺點 :對內存不友好,如果一個key已經過期,但是一直沒有使用,那么該key就會一直存在內存中,內存永遠不會釋放
???????定期刪除?
每隔一段時間抽取一批 key進行檢查,刪除里面過期的key。
優點:可以通過限制刪除操作執行的時長和頻率來減少刪除操作對 CPU 的影響。也能有效釋放過期鍵占用的內存。
缺點:難以確定刪除操作執行的時長和頻率。
Redis的過期刪除策略:惰性刪除 + 定期刪除兩種策略進行配合使用
十、Redis數據淘汰策略(和操作系統的進程管理類似)
當Redis中的內存不夠用時,此時在向Redis中添加新的key,那么Redis就會按照某一種規則將內存中的數據刪除掉,這種數據的刪除規則被稱之為內存的淘汰策略。
Redis支持8種不同策略來選擇要刪除的key:
noeviction:當內存不足時,Redis 不會淘汰任何數據,而是直接返回錯誤信息。這是默認的淘汰策略。
allkeys-lru: 對全體key,基于LRU算法進行淘汰
allkeys-lfu: 對全體key,基于LFU算法進行淘汰
allkeys-random:對全體key ,隨機進行淘汰。
volatile-lru: 對設置了TTL的key,基于LRU算法進行淘汰
volatile-lfu: 對設置了TTL的key,基于LFU算法進行淘汰
volatile-random:對設置了TTL的key ,隨機進行淘汰。
volatile-ttl: 對設置了TTL的key,比較key的剩余TTL值,TTL越小越先被淘汰