1. 程式人生 > 其它 >Redis做快取

Redis做快取

一、快取一些知識
1.1、快取擊穿、快取穿透、快取雪崩是什麼?
快取擊穿
使用者請求的某個key在DB或者快取中存在,但是可能正好在當這個key在快取中到達了失效時間而過期,而此時大量訪問該資料請求過來,相當於在快取中鑿開一個缺口,一下子全部打在DB上,造成DB壓力壓垮DB。

快取穿透
使用者通過請求一些快取和DB中壓根都不存在的資料,致使每次請求都會繞過快取,請求DB,給DB帶來壓力

快取雪崩
當快取伺服器重啟或者大量快取的keys過期失效,導致客戶端過來的請求全部直接請求到DB中,造成DB壓力大而崩潰(注意這裡和快取擊穿不同的是快取擊穿是單個key失效引起,而雪崩是大量key失效)

1.2 應對快取擊穿
1)互斥鎖訪問資料
針對某一熱點資料,在獲取時可通過加互斥鎖使得只有一個請求進行處理訪問DB,並將資料放置到快取中,後續阻塞的請求將直接命中快取。

2)設定熱點資料永不過期
1.3 應對快取穿透
加強介面入參校驗。將不合法入參抹殺在搖籃中
如果未查出資料,可賦予快取中對應key一個null值,並設定一個過期時間,防止單位時間內大量請求訪問DB。因為設定了過期時間,所以可以保證後續該key有值了,可以獲取到。
1.4 應對快取雪崩
1)設定快取過期時間時,加上隨機時間戳
這樣做的好處就是儘量使得快取key們的過期時間均勻分散,不至於在同一個時間點大面積快取過期失效引起雪崩

2)不設定快取過期時間

3)分散式快取伺服器部署的情況下,可以將熱點資料分散在不同的快取伺服器中

二、redisLRU快取機制
2.1 Redis記憶體淘汰機制
LRU是Least Recently Used的縮寫,即最近最少使用,是一種常用的頁面置換演算法,選擇最近最久未使用的頁面予以淘汰。該演算法賦予每個頁面一個訪問欄位,用來記錄一個頁面自上次被訪問以來所經歷的時間 t,當須淘汰一個頁面時,選擇現有頁面中其 t 值最大的,即最近最少使用的頁面予以淘汰。 ----- 摘自百度百科

LRU快取那就是將最近最少訪問的快取剔除。redis針對LRU機制提供了實現支援。redis在redis.conf中提供了配置選項maxmemory 來配置執行大小的記憶體資料集。或者在伺服器執行時,可在客戶端通過CONFIG SET進行設定。

maxmemory 100mb
如果設定為0,那麼則表示記憶體不受限制。當往redis中存放值時,達到我們指定的maxmemory值時,redis提供了多種不同的剔除值策略。可以通過在redis.conf中配置策略

noeviction xxx
noeviction(default)
即當達到maxmemory記憶體限制時,不做資料剔除,直接返回錯誤

allkeys-lru
即當達到maxmemory記憶體限制時,刪除最近最少使用的key以保證新的值可以新增進來

volatile-lru
即當達到maxmemory記憶體限制時,刪除最近最少使用並且通過expire設定了過期時間的key,以保證新的值可以新增進來

allkeys-random
即當達到maxmemory記憶體限制時,隨機刪除一些key以保證新的值可以新增進來

volatile-random
即當達到maxmemory記憶體限制時,隨機刪除一些設定了過期時間的key以保證新的值可以新增進來

volatile-ttl
即當達到maxmemory記憶體限制時,刪除一些設定了過期時間並且TTK所剩時間最短的keys以保證新的值可以新增進來

其中volatile-lru、volatile-random、volatile-ttl如果沒有滿足條件的key可以進行刪除,那麼它們的行為就和noeviction策略一樣。剔除策略也可以在執行時進行設定。

2.2 Redis LRU淘汰機制精確度
在redis3.0之後,redis針對之前近似LRU演算法進行了效能上的改進提升,以及使得LRU演算法更加接近準確。通過配置引數maxmemory-samples可以調整樣本的數量來為LRU演算法獲取精度。注意redis的LRU演算法並不是LRU真正具體的實現,因為那很耗費記憶體。但是其實redis計算出的近似值於真實演算法值是很相近的。

maxmemory-samples 5


從上述Redis官方給的測試資料可以看出,同樣樣本數未5,redis3.0要比之前好一點,當提高樣本數之後,更加的好一點,但是與真實LRU演算法實現還是差點火候。

我們可以以額外的CPU使用為代價將樣本大小增加到10,以接近真實的LRU,並檢查這是否會對快取漏報率產生影響。

在生產環境中,很容易通過CONFIG SET maxmemory-samples 命令來設定不同的樣本值進行實驗。

三、Redis結合Spring Cache Abstraction實現快取
實現快取的思想其實並不複雜,簡單點說,就是查詢先從快取伺服器中查詢,如果查詢到則立即返回,否則再去DB中查詢返回然後再放至到快取當中;;在對資料進行更新操作時,操作DB成功以後,在決定快取的更新策略(更新快取or刪除快取)。

3.1 配置RedisCacheManager
spring-data-redis提供了spring抽象快取的實現,為了藉助於spring cache實現快取,我們需要載入一個RedisCacheManager。

@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
return RedisCacheManager.create(connectionFactory);
}
通過RedisCacheManager.create創建出的是預設配置的RedisCacheManager,如果我們想設定一些事務或者預定義快取則可通過builder進行構建。

3.1.1 自定義快取配置RedisCacheConfiguration
RedisCacheConfiguration配置類,可以用來配置比如設定快取key的過期時間、是否快取null值、是否啟用key字首、以及key和value快取過程中二進位制資料 的序列化策略。

基本配置
// 預設快取配置
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();

// 設定快取key失效時間為2分鐘
redisCacheConfiguration.entryTtl(Duration.ofMinutes(2));

// 禁用快取null值(預設為啟用)
redisCacheConfiguration.disableCachingNullValues();

// 禁用設定key字首(預設為啟用)
redisCacheConfiguration.disableKeyPrefix();

// 設定key字首
redisCacheConfiguration.prefixCacheNameWith("miaomiao:");
key,value序列化策略
RedisCacheConfiguration預設key的序列化器是StringRedisSerializer,value的序列化器是JdkSerializationRedisSerializer

3.2 程式碼實操Srping Cache Abstranction
大致貼下各層demo。

entity(注意需要序列化)

@Entity
@Table(name = "user")
public class UserInfo implements Serializable {

@Id
@Column(name = "user_id")
private int userId;

@Column(name = "user_name")
private String userName;

get set 省略。。。
}
Dao

@Repository
public interface UserInfoRepository extends JpaRepository<UserInfo,Integer> {
}
Service

public interface UserInfoService {
Optional<UserInfo> getUserInfo(int userId);
}
@Service
public class UserInfoServiceImpl implements UserInfoService {
@Autowired
private UserInfoRepository userInfoRepository;

@Override

@Cacheable("user")
public Optional<UserInfo> getUserInfo(int userId) {
System.out.println("發生了真實呼叫!!!");
return userInfoRepository.findById(userId);
}
}
config(注意@EnableCaching啟用快取)

@Configuration
@EnableCaching
public class RedisCacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 預設快取配置
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();

// 設定快取key失效時間為2分鐘
redisCacheConfiguration.entryTtl(Duration.ofMinutes(2));

// 禁用快取null值(預設為啟用)
redisCacheConfiguration.disableCachingNullValues();


return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(redisCacheConfiguration).build();
}
}
Test 以及輸入

@SpringBootTest
public class UserInfoServiceImplTest {

@Autowired
private UserInfoService userInfoService;

@Test
public void getUserInfo() {
System.out.println("=========start1===========");
System.out.println(userInfoService.getUserInfo(1));
System.out.println("==========end1==========");

System.out.println("=========start2===========");
System.out.println(userInfoService.getUserInfo(1));
System.out.println("==========end2==========");

}
}
=========start1===========
發生了真實呼叫!!!
Hibernate: select userinfo0_.user_id as user_id1_0_0_, userinfo0_.user_name as user_nam2_0_0_ from user userinfo0_ where userinfo0_.user_id=?
Optional[UserInfo{userId=1, userName='cf'}]
==========end1==========


=========start2===========
Optional[UserInfo{userId=1, userName='cf'}]
==========end2==========
3.3 小結
從上述測試輸出我們可以看出,在第一次進行呼叫(快取中未存放相關值時,發生了真實呼叫,查庫,然後返回結果),而第二次因為在第一次呼叫的基礎上,快取中已經有了值,所以直接將快取結果返回,完全未觸發真實的方法邏輯,所以這裡也是很需要注意的地方,如果不太瞭解,只是跟著網上demo去使用,則可能發生將其他業務邏輯包雜在上述方法中,造成有快取清空未呼叫導致bug等。

四、如何保障快取DB一致性以及分散式資料一致性?
4.1 快取和DB資料不一致場景(或者說誘因)
1)快取和DB的操作不在一個事務中進行,很可能快取的操作和DB的操作其中一個成功一個失敗,導致資料不一致。 2)多執行緒對快取進行更新操作時,可能導致舊資料被更新至快取中。

4.2 Cache Aside Pattern
當快取失效時
即讀快取,未命中,則從資料庫中取,成功之後將資料放到快取中

當快取命中時
即讀快取,若命中,則成功返回

當資料更新時
先更新資料庫,然後刪除快取

著重與最後一條,當資料發生變化時,針對快取如何進行處理?可能有以下幾種情況:

先更新快取,再更新資料庫
此方案如果說更新資料庫的時候,資料庫宕機導致DB更新失敗,造成了資料不一致

先更新資料庫,再更新快取
此方案主要存在兩個問題: 1).假如執行緒A先更新了資料,然後準備做更新資料庫,更新快取的操作,此時執行緒B也更新了相同的資料,更新資料庫更新快取。按道理執行緒B的資料應該是最新的,但是因為多執行緒的時序問題,很可能最終快取被更新成了舊值。 2).並不是所有快取是熱點資料,有可能此快取不會被經常讀取,而如果每次更新資料的時候,都要去更新一下快取,就造成了不必要的效能和記憶體消耗。

先刪除快取,再更新資料庫
————————————————
版權宣告:本文為CSDN博主「守夜人愛吃兔子」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/m0_57711043/article/details/119731472