<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Redis 性能優(yōu)化的 13 條軍規(guī)!

          共 7412字,需瀏覽 15分鐘

           ·

          2021-03-02 14:10

          點擊關注公眾號,Java干貨及時送達

          Redis 是基于單線程模型實現(xiàn)的,也就是 Redis 是使用一個線程來處理所有的客戶端請求的,盡管 Redis 使用了非阻塞式 IO,并且對各種命令都做了優(yōu)化(大部分命令操作時間復雜度都是 O(1)),但由于 Redis 是單線程執(zhí)行的特點,因此它對性能的要求更加苛刻,本文我們將通過一些優(yōu)化手段,讓 Redis 更加高效的運行。

          本文我們將使用以下手段,來提升 Redis 的運行速度:

          1. 縮短鍵值對的存儲長度;
          2. 使用 lazy free(延遲刪除)特性;
          3. 設置鍵值的過期時間;
          4. 禁用長耗時的查詢命令;
          5. 使用 slowlog 優(yōu)化耗時命令;
          6. 使用 Pipeline 批量操作數(shù)據(jù);
          7. 避免大量數(shù)據(jù)同時失效;
          8. 客戶端使用優(yōu)化;
          9. 限制 Redis 內(nèi)存大??;
          10. 使用物理機而非虛擬機安裝 Redis 服務;
          11. 檢查數(shù)據(jù)持久化策略;
          12. 禁用 THP 特性;
          13. 使用分布式架構來增加讀寫速度。

          1.縮短鍵值對的存儲長度

          鍵值對的長度是和性能成反比的,比如我們來做一組寫入數(shù)據(jù)的性能測試,執(zhí)行結果如下:

          從以上數(shù)據(jù)可以看出,在 key 不變的情況下,value 值越大操作效率越慢,因為 Redis 對于同一種數(shù)據(jù)類型會使用不同的內(nèi)部編碼進行存儲,比如字符串的內(nèi)部編碼就有三種:int(整數(shù)編碼)、raw(優(yōu)化內(nèi)存分配的字符串編碼)、embstr(動態(tài)字符串編碼),這是因為 Redis 的作者是想通過不同編碼實現(xiàn)效率和空間的平衡,然而數(shù)據(jù)量越大使用的內(nèi)部編碼就越復雜,而越是復雜的內(nèi)部編碼存儲的性能就越低。

          這還只是寫入時的速度,當鍵值對內(nèi)容較大時,還會帶來另外幾個問題:

          • 內(nèi)容越大需要的持久化時間就越長,需要掛起的時間越長,Redis 的性能就會越低;
          • 內(nèi)容越大在網(wǎng)絡上傳輸?shù)膬?nèi)容就越多,需要的時間就越長,整體的運行速度就越低;
          • 內(nèi)容越大占用的內(nèi)存就越多,就會更頻繁的觸發(fā)內(nèi)存淘汰機制,從而給 Redis 帶來了更多的運行負擔。

          因此在保證完整語義的同時,我們要盡量的縮短鍵值對的存儲長度,必要時要對數(shù)據(jù)進行序列化和壓縮再存儲,以 Java 為例,序列化我們可以使用 protostuff 或 kryo,壓縮我們可以使用 snappy。

          另外,關注公眾號Java技術棧,在后臺回復:面試,可以獲取我整理的 Redis 系列面試題和答案,非常齊全。

          2.使用 lazy free 特性

          lazy free 特性是 Redis 4.0 新增的一個非常使用的功能,它可以理解為惰性刪除或延遲刪除。意思是在刪除的時候提供異步延時釋放鍵值的功能,把鍵值釋放操作放在 BIO(Background I/O) 單獨的子線程處理中,以減少刪除刪除對 Redis 主線程的阻塞,可以有效地避免刪除 big key 時帶來的性能和可用性問題。

          lazy free 對應了 4 種場景,默認都是關閉的:

          lazyfree-lazy-eviction no
          lazyfree-lazy-expire no
          lazyfree-lazy-server-del no
          slave-lazy-flush no

          它們代表的含義如下:

          • lazyfree-lazy-eviction:表示當 Redis 運行內(nèi)存超過 maxmeory 時,是否開啟 lazy free 機制刪除;
          • lazyfree-lazy-expire:表示設置了過期時間的鍵值,當過期之后是否開啟 lazy free 機制刪除;
          • lazyfree-lazy-server-del:有些指令在處理已存在的鍵時,會帶有一個隱式的 del 鍵的操作,比如 rename 命令,當目標鍵已存在,Redis 會先刪除目標鍵,如果這些目標鍵是一個 big key,就會造成阻塞刪除的問題,此配置表示在這種場景中是否開啟 lazy free 機制刪除;
          • slave-lazy-flush:針對 slave(從節(jié)點) 進行全量數(shù)據(jù)同步,slave 在加載 master 的 RDB 文件前,會運行 flushall 來清理自己的數(shù)據(jù),它表示此時是否開啟 lazy free 機制刪除。

          建議開啟其中的 lazyfree-lazy-eviction、lazyfree-lazy-expire、lazyfree-lazy-server-del 等配置,這樣就可以有效的提高主線程的執(zhí)行效率。

          3.設置鍵值的過期時間

          我們應該根據(jù)實際的業(yè)務情況,對鍵值設置合理的過期時間,這樣 Redis 會幫你自動清除過期的鍵值對,以節(jié)約對內(nèi)存的占用,以避免鍵值過多的堆積,頻繁的觸發(fā)內(nèi)存淘汰策略。

          4.禁用長耗時的查詢命令

          Redis 絕大多數(shù)讀寫命令的時間復雜度都在 O(1) 到 O(N) 之間,在官方文檔對每命令都有時間復雜度說明,地址:https://redis.io/commands,如下圖所示:其中 O(1) 表示可以安全使用的,而 O(N) 就應該當心了,N 表示不確定,數(shù)據(jù)越大查詢的速度可能會越慢。因為 Redis 只用一個線程來做數(shù)據(jù)查詢,如果這些指令耗時很長,就會阻塞 Redis,造成大量延時。

          要避免 O(N) 命令對 Redis 造成的影響,可以從以下幾個方面入手改造:

          • 決定禁止使用 keys 命令;
          • 避免一次查詢所有的成員,要使用 scan 命令進行分批的,游標式的遍歷;
          • 通過機制嚴格控制 Hash、Set、Sorted Set 等結構的數(shù)據(jù)大??;
          • 將排序、并集、交集等操作放在客戶端執(zhí)行,以減少 Redis 服務器運行壓力;
          • 刪除 (del) 一個大數(shù)據(jù)的時候,可能會需要很長時間,所以建議用異步刪除的方式 unlink,它會啟動一個新的線程來刪除目標數(shù)據(jù),而不阻塞 Redis 的主線程。

          5.使用 slowlog 優(yōu)化耗時命令

          我們可以使用 slowlog 功能找出最耗時的 Redis 命令進行相關的優(yōu)化,以提升 Redis 的運行速度,慢查詢有兩個重要的配置項:

          • slowlog-log-slower-than :用于設置慢查詢的評定時間,也就是說超過此配置項的命令,將會被當成慢操作記錄在慢查詢?nèi)罩局校鼒?zhí)行單位是微秒 (1 秒等于 1000000 微秒);
          • slowlog-max-len :用來配置慢查詢?nèi)罩镜淖畲笥涗洈?shù)。

          我們可以根據(jù)實際的業(yè)務情況進行相應的配置,其中慢日志是按照插入的順序倒序存入慢查詢?nèi)罩局?,我們可以使?nbsp;slowlog get n 來獲取相關的慢查詢?nèi)罩?,再找到這些慢查詢對應的業(yè)務進行相關的優(yōu)化。

          6.使用 Pipeline 批量操作數(shù)據(jù)

          Pipeline (管道技術) 是客戶端提供的一種批處理技術,用于一次處理多個 Redis 命令,從而提高整個交互的性能。

          我們使用 Java 代碼來測試一下 Pipeline 和普通操作的性能對比,Pipeline 的測試代碼如下:

          public class PipelineExample {
          public static void main(String[] args) {
          Jedis jedis = new Jedis("127.0.0.1", 6379);
          // 記錄執(zhí)行開始時間
          long beginTime = System.currentTimeMillis();
          // 獲取 Pipeline 對象
          Pipeline pipe = jedis.pipelined();
          // 設置多個 Redis 命令
          for (int i = 0; i < 100; i++) {
          pipe.set("key" + i, "val" + i);
          pipe.del("key"+i);
          }
          // 執(zhí)行命令
          pipe.sync();
          // 記錄執(zhí)行結束時間
          long endTime = System.currentTimeMillis();
          System.out.println("執(zhí)行耗時:" + (endTime - beginTime) + "毫秒");
          }
          }

          以上程序執(zhí)行結果為:

          執(zhí)行耗時:297毫秒

          普通的操作代碼如下:

          public class PipelineExample {
          public static void main(String[] args) {
          Jedis jedis = new Jedis("127.0.0.1", 6379);
          // 記錄執(zhí)行開始時間
          long beginTime = System.currentTimeMillis();
          for (int i = 0; i < 100; i++) {
          jedis.set("key" + i, "val" + i);
          jedis.del("key"+i);
          }
          // 記錄執(zhí)行結束時間
          long endTime = System.currentTimeMillis();
          System.out.println("執(zhí)行耗時:" + (endTime - beginTime) + "毫秒");
          }
          }

          以上程序執(zhí)行結果為:

          執(zhí)行耗時:17276毫秒

          從以上的結果可以看出,管道的執(zhí)行時間是 297 毫秒,而普通命令執(zhí)行時間是 17276 毫秒,管道技術要比普通的執(zhí)行大約快了 58 倍。

          7.避免大量數(shù)據(jù)同時失效

          Redis 過期鍵值刪除使用的是貪心策略,它每秒會進行 10 次過期掃描,此配置可在 redis.conf 進行配置,默認值是 hz 10,Redis 會隨機抽取 20 個值,刪除這 20 個鍵中過期的鍵,如果過期 key 的比例超過 25% ,重復執(zhí)行此流程,如下圖所示:



          如果在大型系統(tǒng)中有大量緩存在同一時間同時過期,那么會導致 Redis 循環(huán)多次持續(xù)掃描刪除過期字典,直到過期字典中過期鍵值被刪除的比較稀疏為止,而在整個執(zhí)行過程會導致 Redis 的讀寫出現(xiàn)明顯的卡頓,卡頓的另一種原因是內(nèi)存管理器需要頻繁回收內(nèi)存頁,因此也會消耗一定的 CPU。

          為了避免這種卡頓現(xiàn)象的產(chǎn)生,我們需要預防大量的緩存在同一時刻一起過期,就簡單的解決方案就是在過期時間的基礎上添加一個指定范圍的隨機數(shù)。

          8.客戶端使用優(yōu)化

          在客戶端的使用上我們除了要盡量使用 Pipeline 的技術外,還需要注意要盡量使用 Redis 連接池,而不是頻繁創(chuàng)建銷毀 Redis 連接,這樣就可以減少網(wǎng)絡傳輸次數(shù)和減少了非必要調(diào)用指令。

          9.限制 Redis 內(nèi)存大小

          在 64 位操作系統(tǒng)中 Redis 的內(nèi)存大小是沒有限制的,也就是配置項 maxmemory <bytes> 是被注釋掉的,這樣就會導致在物理內(nèi)存不足時,使用 swap 空間既交換空間,而當操心系統(tǒng)將 Redis 所用的內(nèi)存分頁移至 swap 空間時,將會阻塞 Redis 進程,導致 Redis 出現(xiàn)延遲,從而影響 Redis 的整體性能。因此我們需要限制 Redis 的內(nèi)存大小為一個固定的值,當 Redis 的運行到達此值時會觸發(fā)內(nèi)存淘汰策略,內(nèi)存淘汰策略在 Redis 4.0 之后有 8 種

          1. noeviction:不淘汰任何數(shù)據(jù),當內(nèi)存不足時,新增操作會報錯,Redis 默認內(nèi)存淘汰策略;
          2. allkeys-lru:淘汰整個鍵值中最久未使用的鍵值;
          3. allkeys-random:隨機淘汰任意鍵值;
          4. volatile-lru:淘汰所有設置了過期時間的鍵值中最久未使用的鍵值;
          5. volatile-random:隨機淘汰設置了過期時間的任意鍵值;
          6. volatile-ttl:優(yōu)先淘汰更早過期的鍵值。

          在 Redis 4.0 版本中又新增了 2 種淘汰策略:

          1. volatile-lfu:淘汰所有設置了過期時間的鍵值中,最少使用的鍵值;
          2. allkeys-lfu:淘汰整個鍵值中最少使用的鍵值。

          其中 allkeys-xxx 表示從所有的鍵值中淘汰數(shù)據(jù),而 volatile-xxx 表示從設置了過期鍵的鍵值中淘汰數(shù)據(jù)。

          我們可以根據(jù)實際的業(yè)務情況進行設置,默認的淘汰策略不淘汰任何數(shù)據(jù),在新增時會報錯。

          10.使用物理機而非虛擬機

          在虛擬機中運行 Redis 服務器,因為和物理機共享一個物理網(wǎng)口,并且一臺物理機可能有多個虛擬機在運行,因此在內(nèi)存占用上和網(wǎng)絡延遲方面都會有很糟糕的表現(xiàn),我們可以通過 ./redis-cli --intrinsic-latency 100 命令查看延遲時間,如果對 Redis 的性能有較高要求的話,應盡可能在物理機上直接部署 Redis 服務器。

          11.檢查數(shù)據(jù)持久化策略

          Redis 的持久化策略是將內(nèi)存數(shù)據(jù)復制到硬盤上,這樣才可以進行容災恢復或者數(shù)據(jù)遷移,但維護此持久化的功能,需要很大的性能開銷。

          在 Redis 4.0 之后,Redis 有 3 種持久化的方式:

          • RDB(Redis DataBase,快照方式)將某一個時刻的內(nèi)存數(shù)據(jù),以二進制的方式寫入磁盤;
          • AOF(Append Only File,文件追加方式),記錄所有的操作命令,并以文本的形式追加到文件中;
          • 混合持久化方式,Redis 4.0 之后新增的方式,混合持久化是結合了 RDB 和 AOF 的優(yōu)點,在寫入的時候,先把當前的數(shù)據(jù)以 RDB 的形式寫入文件的開頭,再將后續(xù)的操作命令以 AOF 的格式存入文件,這樣既能保證 Redis 重啟時的速度,又能減低數(shù)據(jù)丟失的風險。

          RDB 和 AOF 持久化各有利弊,RDB 可能會導致一定時間內(nèi)的數(shù)據(jù)丟失,而 AOF 由于文件較大則會影響 Redis 的啟動速度,為了能同時擁有 RDB 和 AOF 的優(yōu)點,Redis 4.0 之后新增了混合持久化的方式,因此我們在必須要進行持久化操作時,應該選擇混合持久化的方式。

          查詢是否開啟混合持久化可以使用 config get aof-use-rdb-preamble 命令,執(zhí)行結果如下圖所示:



          其中 yes 表示已經(jīng)開啟混合持久化,no 表示關閉,Redis 5.0 默認值為 yes。如果是其他版本的 Redis 首先需要檢查一下,是否已經(jīng)開啟了混合持久化,如果關閉的情況下,可以通過以下兩種方式開啟:

          • 通過命令行開啟
          • 通過修改 Redis 配置文件開啟

          ① 通過命令行開啟

          使用命令 config set aof-use-rdb-preamble yes 執(zhí)行結果如下圖所示:


          命令行設置配置的缺點是重啟 Redis 服務之后,設置的配置就會失效。

          ② 通過修改 Redis 配置文件開啟

          在 Redis 的根路徑下找到 redis.conf 文件,把配置文件中的 aof-use-rdb-preamble no 改為 aof-use-rdb-preamble yes 如下圖所示:


          配置完成之后,需要重啟 Redis 服務器,配置才能生效,但修改配置文件的方式,在每次重啟 Redis 服務之后,配置信息不會丟失。

          需要注意的是,在非必須進行持久化的業(yè)務中,可以關閉持久化,這樣可以有效的提升 Redis 的運行速度,不會出現(xiàn)間歇性卡頓的困擾。

          12.禁用 THP 特性

          Linux kernel 在 2.6.38 內(nèi)核增加了 Transparent Huge Pages (THP) 特性 ,支持大內(nèi)存頁 2MB 分配,默認開啟。

          當開啟了 THP 時,fork 的速度會變慢,fork 之后每個內(nèi)存頁從原來 4KB 變?yōu)?2MB,會大幅增加重寫期間父進程內(nèi)存消耗。同時每次寫命令引起的復制內(nèi)存頁單位放大了 512 倍,會拖慢寫操作的執(zhí)行時間,導致大量寫操作慢查詢。例如簡單的 incr 命令也會出現(xiàn)在慢查詢中,因此 Redis 建議將此特性進行禁用,禁用方法如下:

          echo never >  /sys/kernel/mm/transparent_hugepage/enabled

          為了使機器重啟后 THP 配置依然生效,可以在 /etc/rc.local 中追加 echo never > /sys/kernel/mm/transparent_hugepage/enabled。

          13.使用分布式架構來增加讀寫速度

          Redis 分布式架構有三個重要的手段:

          • 主從同步
          • 哨兵模式
          • Redis Cluster 集群

          使用主從同步功能我們可以把寫入放到主庫上執(zhí)行,把讀功能轉移到從服務上,因此就可以在單位時間內(nèi)處理更多的請求,從而提升的 Redis 整體的運行速度。

          而哨兵模式是對于主從功能的升級,但當主節(jié)點奔潰之后,無需人工干預就能自動恢復 Redis 的正常使用。

          Redis Cluster 是 Redis 3.0 正式推出的,Redis 集群是通過將數(shù)據(jù)庫分散存儲到多個節(jié)點上來平衡各個節(jié)點的負載壓力。

          Redis Cluster 采用虛擬哈希槽分區(qū),所有的鍵根據(jù)哈希函數(shù)映射到 0 ~ 16383 整數(shù)槽內(nèi),計算公式:slot = CRC16(key) & 16383,每一個節(jié)點負責維護一部分槽以及槽所映射的鍵值數(shù)據(jù)。這樣 Redis 就可以把讀寫壓力從一臺服務器,分散給多臺服務器了,因此性能會有很大的提升。

          在這三個功能中,我們只需要使用一個就行了,毫無疑問 Redis Cluster 應該是首選的實現(xiàn)方案,它可以把讀寫壓力自動的分擔給更多的服務器,并且擁有自動容災的能力。






          關注Java技術??锤喔韶?/strong>



          戳原文,獲取精選面試題!
          瀏覽 40
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大香蕉这里只有精品视频 | 日本一级A片色情一区二区 | 日本1234区在线观看 | 亚洲免费aⅴ一一 | 色婷婷A V |