Redis高併發可能產生的問題即個人見解解決方案
1、 如果redis宕機了,或者連結不上,怎麼辦?
解決方法:
①配置主從複製,配置哨兵模式(相當於古代門派的長老級別可以選擇掌門人的權利),一旦發現主機宕機,讓下一個從機當做主機。
②如果最壞的情況,只能關閉Redis連線,去往資料庫連線。但由於資料量大,這樣SQL資料庫也會宕掉的。
2、 如果redis快取在高峰期到期失效,在這個時刻請求會向雪崩一樣,直接訪問資料庫如何處理?
設定條件查詢判斷,判斷redis快取裡是否有資料,如果沒有,則去往資料庫連線。當然要加分散式鎖,利用redis的單執行緒+多路IO複用技術,原子性原理,讓其它的執行緒請求等待,假若第一個執行緒進去獲取到分散式鎖在查詢資料的途中宕掉了,不能讓其它執行緒一直等待,設定等待一定時間判斷是否取回資料,如果沒有,遞迴呼叫自己的方法讓第二個執行緒繼續拿分散式鎖查詢資料庫。當第二個鎖從資料庫拿到資料時,把資料值設定到redis資料庫快取中,設定失效時間,避免佔記憶體,方便使用提高效率。
3. 如果使用者不停地查詢一條不存在的資料,快取沒有,資料庫也沒有,那麼會出現什麼
如果資料不存在,快取中沒有,資料庫也沒有,當然如果不設定判斷,會一直呼叫資料庫,使資料庫效率降低,訪問量大時甚至會宕機。
解決方案:從資料庫查詢,如果資料庫沒有,則返回值為Null,判斷資料庫返回的值,如果為Null,則自定義把標識的欄位存到Redis中,用key,value的方法,jedis.setex(key,"empty"),設定失效時間跟具體情況而定,然後呼叫String json=jedis.get(key),判斷是否獲取的值"empty".equal(json),如果相等,則丟擲自定義異常,給使用者提示,或者直接return null。這樣使用者再次查詢的時候由於先從reids快取中查詢,redis會有對應的Key獲取之前設定的value值,這樣就不會再次呼叫資料庫,影響效率等問題。
具體程式碼如下:
@Override |
public SkuInfo getSkuInfo(String skuId) { |
try { |
Thread.sleep(3*1000); |
//自定義從redis工具類中獲取jedis物件 |
Jedis jedis = redisUtil.getJedis(); |
//拼接字串建立Redis裡面的Key值 |
String skuInfoKey= JedisConst.SKU_PREFIX+skuId+JedisConst.SKU_SUFFIX; |
//根據key值獲取value值 |
String skuInfoJson = jedis.get(skuInfoKey); |
//如果返回為空,則呼叫本地資料庫連線 |
if |
System.out.println(Thread.currentThread().getName()+"當前快取中未找到資料"); |
//判斷是否有人去取鎖 |
String skuLockKey=JedisConst.SKU_PREFIX+skuId+JedisConst.SKULOCK_SUFFIX; |
String result = jedis.set(skuLockKey, "OK", "NX", "PX", JedisConst.SKULOCK_EXPIRE_PX); |
if ("OK".equals(result)){ |
System.out.println(Thread.currentThread().getName()+"獲得分散式鎖"); |
SkuInfo skuInfo= getSkuInfoDB(skuId); |
//如果資料庫裡面沒有值,別人惡意攻擊的話,直接設定到redis快取中 |
if (skuInfo==null){ |
jedis.setex(skuInfoKey,JedisConst.TIME_OUT,"empty"); |
return null; |
} |
skuInfoJson = JSON.toJSONString(skuInfo); |
jedis.setex(skuInfoKey, JedisConst.TIME_OUT, skuInfoJson); |
jedis.close(); |
return skuInfo; |
} else { |
//假設之後有人取鎖後dang掉了,遞迴呼叫自己去尋找鑰匙。 |
System.out.println(Thread.currentThread().getName()+"未獲得分散式鎖,開啟自旋模式俗稱遞迴."); |
//等待1秒鐘 |
Thread.sleep(1*1000); |
jedis.close(); |
return getSkuInfo(skuId); |
} |
}else if (skuInfoJson.equals("empty")){ |
return null; |
} |
else { |
System.out.println(Thread.currentThread().getName()+"快取中已有資料正在查詢"); |
SkuInfo skuInfo = JSON.parseObject(skuInfoJson, SkuInfo.class); |
jedis.close(); |
return skuInfo; |
} |
}catch (JedisConnectionException e){ |
e.printStackTrace(); |
} catch (InterruptedException e) { |
e.printStackTrace(); |
} |
return getSkuInfoDB(skuId); |