redis可以滿足很多的應用場景,而且因為將所有資料都放到記憶體中,所以它的讀寫效能很好,很多公司都在使用redis。redis給我們帶來便利的同時,使用過程中會存在什麼問題呢,本文將簡單加以總結。
-
阻塞問題
redis使用了單執行緒來處理請求,為什麼單執行緒可以支援如此高的併發呢?主要有如下幾點:- 純記憶體訪問:將所有資料都放到記憶體中,記憶體響應時間為100納秒,是redis達到每秒萬級別訪問的重要基礎
- 非阻塞IO:redis使用epoll作為I/O多路複用技術,redis自身的事件處理模型將epoll中的連線、讀寫、關閉都轉換為事件,不在網路I/O上浪費過多時間
-
單執行緒:避免了執行緒切換和競態產生的消耗,簡化了資料結構和演算法的實現
因此如果某個命令執行時間過長,會造成其他命令阻塞,對redis來說是致命的產生阻塞的場景: A. API或資料結構使用不合理 a. 避免使用某些易造成阻塞的命令如:keys sort hgetall smembers 執行showlog get [n] 可以獲取最近n條執行慢的記錄,對於執行超過一定時間 (預設10ms,線上建議設定為1ms)的命令都會記錄到一個定長佇列(預設128,可調整)中。 b. 防止一次操作獲取過多資料:縮減大物件或者把大物件拆分為多個小物件 發現大物件的命令:redis-cli -h{ip} -p{port} bigkeys 內部原理:採用分段進行scan操作,把歷史掃描過的大物件統計出來 c. 防止大量key同時過期:如果有很多key在同一秒內過期,超過了所有key的25%,redis主執行緒就會阻塞直到過期key比例下降到25%以內, 因此要避免同一時間過期大量key,過期時間可做雜湊處理。 redis4.0引入的lazyfree機制可以避免del、flushdb、flushall、rename等命令引起的redis-server阻塞,提高服務穩定性。 B. CPU飽和 單執行緒的redis處理命令時只能使用一個CPU,CPU飽和是指redis把單核的CPU使用率跑到接近100%。 首先要確定redis的併發量是否達到極限,通過redis-cli-h{ip} -p{port}--stat 獲取redis當前使用情況。 如果達到每秒6w+左右的qps,說明單臺已跑到極限,需要水平擴充套件。 如果qps只有幾百或者幾千CPU就已經飽和,可能使用了高演算法複雜度的命令或者是對記憶體的過度優化 (如放寬了ziplist的使用條件,雖然使用的記憶體會變少,但是更耗CPU)。 C. 持久化操作 持久化引起主執行緒的阻塞操作主要有:fork阻塞、AOF刷盤阻塞、HugePage寫操作阻塞 a. fork阻塞 發生在RDB和AOF重寫時,redis主執行緒呼叫fork操作產生共享記憶體的子執行緒,由子執行緒完成持久化檔案的重寫工作,若fork操作耗時過長會引起阻塞。 避免使用記憶體過大的例項。 b. AOF刷盤阻塞 開啟AOF持久化功能時,一般會採用1次/s的刷盤方式,後臺執行緒每秒對AOF檔案做fsync操作,當硬碟壓力過大時fsync操作需要等待直到寫入完成。 如果主執行緒距離上一次的fsync成功超過2s,為了資料安全會阻塞直到後臺執行緒執行完fsync完成。這種阻塞是由於磁碟壓力引起。 儘量獨立部署 c. HugePage寫操作阻塞 子程式在執行重寫期間利用linux的copyonwrite機制,會拖慢寫操作的執行時間,導致大量寫操作慢查詢。 優化linux配置
-
快取穿透
快取穿透是指查詢一個根本不存在的資料,快取層和儲存層都不命中,且不將空結果寫到快取中。
會導致後端儲存負載變大,造成後端儲存當機等問題。可以在程式中分別統計總呼叫數、快取命中數、儲存命中數,若有大量儲存層空命中,可能是出現了快取穿透。
產生原因:1.自身程式碼或資料出現問題 2.惡意攻擊,爬蟲造成空命中
如何解決:- 快取空物件
儲存層不命中,扔將空物件儲存到快取層。
適用場景:資料頻繁變化、實時性高
帶來問題:
a.快取了空值,會佔用記憶體空間;可以設定較短過期時間,自動剔除。
b.資料不一致,若儲存層新增了此資料,有短暫不一致;可主動清除掉快取的空物件。 - 布隆過濾器
在訪問快取層和資料層之前將存在的key用布隆過濾器提前儲存起來,做第一層攔截。
適用場景:大使用者集,實時性要求較低的場景,如有幾億的資料集,每隔一段時間會新增使用者進去,在更新之前新使用者的訪問會存在快取穿透問題。
缺點:程式碼維護複雜
- 快取空物件