天天看點

redis 緩存雪崩、穿透、擊穿

緩存雪崩

緩存雪崩通俗簡單的了解就是:由于原有緩存失效(或者資料未加載到緩存中),新緩存未到期間,所有原本應該通路緩存的請求都去查詢資料庫了,而對資料庫CPU和記憶體造成巨大壓力,嚴重的會造成資料庫當機,造成系統的崩潰。

redis 緩存雪崩解決方案

  1. 分布式鎖(本地鎖)

    在緩存失效後,通過加鎖或者隊列來控制讀資料庫寫緩存的線程數量。比如對某個key隻允許一個線程查詢資料和寫緩存,其他線程等待。

@RequestMapping("/getUsers")
	public Users getByUsers(Long id) {
		// 1.先查詢redis
		String key = this.getClass().getName() + "-" + Thread.currentThread().getStackTrace()[1].getMethodName()
				+ "-id:" + id;
		String userJson = redisService.getString(key);
		if (!StringUtils.isEmpty(userJson)) {
			Users users = JSONObject.parseObject(userJson, Users.class);
			return users;
		}
		Users user = null;
		try {
			lock.lock();
			// 查詢db
			user = userMapper.getUser(id);
			redisService.setSet(key, JSONObject.toJSONString(user));
		} catch (Exception e) {

		} finally {
			lock.unlock(); // 釋放鎖
		}
		return user;
	}
           

注意:加鎖排隊隻是為了減輕資料庫的壓力,并沒有提高系統吞吐量。假設在高并發下,緩存重建期間key是鎖着的,這是過來1000個請求999個都在阻塞的。同樣會導緻使用者等待逾時,這是個治标不治本的方法。

  1. 使用消息中間件 MQ 方式(最靠譜的方式),消息中間件方式可以解決高并發。如果大量請求進行通路的時候,如果 redis 沒有值的情況下,這個會将該消息存放在消息中間件中(異步)
  2. 一級和二級(ehcache+redis)緩存
  3. 均攤配置設定 redis key 的失效時間(比較靠譜),不同的key的失效時間都不同。

redis 緩存穿透

緩存穿透是指使用者查詢資料,在資料庫沒有,自然在緩存中也不會有。這樣就導緻使用者查詢的時候, 在緩存中找不到,每次都要去資料庫再查詢一遍,然後傳回空。這樣請求就繞過緩存直接查資料庫,這也是經常提的緩存命中率問題。

解決的辦法就是:如果查詢資料庫也為空,直接設定一個預設值存放到緩存,這樣第二次到緩沖中擷取就有值了,而不會繼續通路資料庫,這種辦法最簡單粗暴。

public String getByUsers2(Long id) {
		// 1.先查詢redis
		String key = this.getClass().getName() + "-" + Thread.currentThread().getStackTrace()[1].getMethodName()
				+ "-id:" + id;
		String userName = redisService.getString(key);
		if (!StringUtils.isEmpty(userName)) {
			return userName;
		}
		System.out.println("######開始發送資料庫DB請求########");
		Users user = userMapper.getUser(id);
		String value = null;
		if (user == null) {
			// 辨別為null
			value = SIGN_KEY;
		} else {
			value = user.getName();
		}
		redisService.setString(key, value);
		return value;
	}
           

把空結果,也給緩存起來,這樣下次同樣的請求就可以直接傳回空了,即可以避免當查詢的值為空時引起的緩存穿透。同時也可以單獨設定個緩存區域存儲空值,對要查詢的key進行預先校驗,然後再放行給後面的正常緩存處理邏輯。

緩存擊穿

熱點key:某個key通路非常頻繁,當key失效的時候有大量線程來建構緩存,導緻負載增加,系統崩潰。

解決辦法:

  1. 使用鎖,單機用synchronized,lock等,分布式用分布式鎖。
  2. 緩存過期時間不設定,而是設定在key對應的value裡。如果檢測到存的時間超過過期時間則異步更新緩存。
  3. 在value設定一個比過期時間t0小的過期時間值t1,當t1過期的時候,延長t1并做更新緩存操作。

繼續閱讀