Redis性能調優

Redis性能調優

儘管Redis是一個很是快速的內存數據存儲媒介,也並不表明Redis不會產生性能問題。
前文中提到過,Redis採用單線程模型,全部的命令都是由一個線程串行執行的,因此當某個命令執行耗時較長時,會拖慢其後的全部命令,這使得Redis對每一個任務的執行效率更加敏感。git

針對Redis的性能優化,主要從下面幾個層面入手:github

  • 最初的也是最重要的,確保沒有讓Redis執行耗時長的命令
  • 使用pipelining將連續執行的命令組合執行
  • 操做系統的Transparent huge pages功能必須關閉:
    echo never > /sys/kernel/mm/transparent_hugepage/enabled
  • 若是在虛擬機中運行Redis,可能自然就有虛擬機環境帶來的固有延遲。能夠經過./redis-cli --intrinsic-latency 100命令查看固有延遲。同時若是對Redis的性能有較高要求的話,應儘量在物理機上直接部署Redis。
  • 檢查數據持久化策略
  • 考慮引入讀寫分離機制

長耗時命令

Redis絕大多數讀寫命令的時間複雜度都在O(1)到O(N)之間,在文本和官方文檔中均對每一個命令的時間複雜度有說明。redis

一般來講,O(1)的命令是安全的,O(N)命令在使用時須要注意,若是N的數量級不可預知,則應避免使用。例如對一個field數未知的Hash數據執行HGETALL/HKEYS/HVALS命令,一般來講這些命令執行的很快,但若是這個Hash中的field數量極多,耗時就會成倍增加。
又如使用SUNION對兩個Set執行Union操做,或使用SORT對List/Set執行排序操做等時,都應該嚴加註意。算法

避免在使用這些O(N)命令時發生問題主要有幾個辦法:sql

  • 不要把List當作列表使用,僅當作隊列來使用
  • 經過機制嚴格控制Hash、Set、Sorted Set的大小
  • 可能的話,將排序、並集、交集等操做放在客戶端執行
  • 絕對禁止使用KEYS命令
  • 避免一次性遍歷集合類型的全部成員,而應使用SCAN類的命令進行分批的,遊標式的遍歷

Redis提供了SCAN命令,能夠對Redis中存儲的全部key進行遊標式的遍歷,避免使用KEYS命令帶來的性能問題。同時還有SSCAN/HSCAN/ZSCAN等命令,分別用於對Set/Hash/Sorted Set中的元素進行遊標式遍歷。SCAN類命令的使用請參考官方文檔:https://redis.io/commands/scan數據庫

Redis提供了Slow Log功能,能夠自動記錄耗時較長的命令。相關的配置參數有兩個:緩存

slowlog-log-slower-than xxxms  #執行時間慢於xxx毫秒的命令計入Slow Log slowlog-max-len xxx #Slow Log的長度,即最大紀錄多少條Slow Log

使用SLOWLOG GET [number]命令,能夠輸出最近進入Slow Log的number條命令。
使用SLOWLOG RESET命令,能夠重置Slow Log安全

網絡引起的延遲

  • 儘量使用長鏈接或鏈接池,避免頻繁建立銷燬鏈接
  • 客戶端進行的批量數據操做,應使用Pipeline特性在一次交互中完成。具體請參照本文的Pipelining章節

數據持久化引起的延遲

Redis的數據持久化工做自己就會帶來延遲,須要根據數據的安全級別和性能要求制定合理的持久化策略:性能優化

  • AOF + fsync always的設置雖然可以絕對確保數據安全,但每一個操做都會觸發一次fsync,會對Redis的性能有比較明顯的影響
  • AOF + fsync every second是比較好的折中方案,每秒fsync一次
  • AOF + fsync never會提供AOF持久化方案下的最優性能
  • 使用RDB持久化一般會提供比使用AOF更高的性能,但須要注意RDB的策略配置
  • 每一次RDB快照和AOF Rewrite都須要Redis主進程進行fork操做。fork操做自己可能會產生較高的耗時,與CPU和Redis佔用的內存大小有關。根據具體的狀況合理配置RDB快照和AOF Rewrite時機,避免過於頻繁的fork帶來的延遲

Redis在fork子進程時須要將內存分頁表拷貝至子進程,以佔用了24GB內存的Redis實例爲例,共須要拷貝24GB / 4kB * 8 = 48MB的數據。在使用單Xeon 2.27Ghz的物理機上,這一fork操做耗時216ms。bash

能夠經過INFO命令返回的latest_fork_usec字段查看上一次fork操做的耗時(微秒)

Swap引起的延遲

當Linux將Redis所用的內存分頁移至swap空間時,將會阻塞Redis進程,致使Redis出現不正常的延遲。Swap一般在物理內存不足或一些進程在進行大量I/O操做時發生,應儘量避免上述兩種狀況的出現。

/proc/<pid>/smaps文件中會保存進程的swap記錄,經過查看這個文件,可以判斷Redis的延遲是否由Swap產生。若是這個文件中記錄了較大的Swap size,則說明延遲頗有多是Swap形成的。

數據淘汰引起的延遲

當同一秒內有大量key過時時,也會引起Redis的延遲。在使用時應儘可能將key的失效時間錯開。

引入讀寫分離機制

Redis的主從複製能力能夠實現一主多從的多節點架構,在這一架構下,主節點接收全部寫請求,並將數據同步給多個從節點。
在這一基礎上,咱們可讓從節點提供對實時性要求不高的讀請求服務,以減少主節點的壓力。
尤爲是針對一些使用了長耗時命令的統計類任務,徹底能夠指定在一個或多個從節點上執行,避免這些長耗時命令影響其餘請求的響應。

關於讀寫分離的具體說明,請參見後續章節

主從複製與集羣分片

主從複製

Redis支持一主多從的主從複製架構。一個Master實例負責處理全部的寫請求,Master將寫操做同步至全部Slave。
藉助Redis的主從複製,能夠實現讀寫分離和高可用:

  • 實時性要求不是特別高的讀請求,能夠在Slave上完成,提高效率。特別是一些週期性執行的統計任務,這些任務可能須要執行一些長耗時的Redis命令,能夠專門規劃出1個或幾個Slave用於服務這些統計任務
  • 藉助Redis Sentinel能夠實現高可用,當Master crash後,Redis Sentinel可以自動將一個Slave晉升爲Master,繼續提供服務

啓用主從複製很是簡單,只須要配置多個Redis實例,在做爲Slave的Redis實例中配置:

slaveof 192.168.1.1 6379 #指定Master的IP和端口

當Slave啓動後,會從Master進行一次冷啓動數據同步,由Master觸發BGSAVE生成RDB文件推送給Slave進行導入,導入完成後Master再將增量數據經過Redis Protocol同步給Slave。以後主從之間的數據便一直以Redis Protocol進行同步

使用Sentinel作自動failover

Redis的主從複製功能自己只是作數據同步,並不提供監控和自動failover能力,要經過主從複製功能來實現Redis的高可用,還須要引入一個組件:Redis Sentinel

Redis Sentinel是Redis官方開發的監控組件,能夠監控Redis實例的狀態,經過Master節點自動發現Slave節點,並在監測到Master節點失效時選舉出一個新的Master,並向全部Redis實例推送新的主從配置。

Redis Sentinel須要至少部署3個實例才能造成選舉關係。

關鍵配置:

sentinel monitor mymaster 127.0.0.1 6379 2 #Master實例的IP、端口,以及選舉須要的同意票數 sentinel down-after-milliseconds mymaster 60000 #多長時間沒有響應視爲Master失效 sentinel failover-timeout mymaster 180000 #兩次failover嘗試間的間隔時長 sentinel parallel-syncs mymaster 1 #若是有多個Slave,能夠經過此配置指定同時重新Master進行數據同步的Slave數,避免全部Slave同時進行數據同步致使查詢服務也不可用

另外須要注意的是,Redis Sentinel實現的自動failover不是在同一個IP和端口上完成的,也就是說自動failover產生的新Master提供服務的IP和端口與以前的Master是不同的,因此要實現HA,還要求客戶端必須支持Sentinel,可以與Sentinel交互得到新Master的信息才行。

集羣分片

爲什麼要作集羣分片:

  • Redis中存儲的數據量大,一臺主機的物理內存已經沒法容納
  • Redis的寫請求併發量大,一個Redis實例以沒法承載

當上述兩個問題出現時,就必需要對Redis進行分片了。
Redis的分片方案有不少種,例如不少Redis的客戶端都自行實現了分片功能,也有向Twemproxy這樣的以代理方式實現的Redis分片方案。然而首選的方案還應該是Redis官方在3.0版本中推出的Redis Cluster分片方案。

本文不會對Redis Cluster的具體安裝和部署細節進行介紹,重點介紹Redis Cluster帶來的好處與弊端。

Redis Cluster的能力

  • 可以自動將數據分散在多個節點上
  • 當訪問的key不在當前分片上時,可以自動將請求轉發至正確的分片
  • 當集羣中部分節點失效時仍能提供服務

其中第三點是基於主從複製來實現的,Redis Cluster的每一個數據分片都採用了主從複製的結構,原理和前文所述的主從複製徹底一致,惟一的區別是省去了Redis Sentinel這一額外的組件,由Redis Cluster負責進行一個分片內部的節點監控和自動failover。

Redis Cluster分片原理

Redis Cluster中共有16384個hash slot,Redis會計算每一個key的CRC16,將結果與16384取模,來決定該key存儲在哪個hash slot中,同時須要指定Redis Cluster中每一個數據分片負責的Slot數。Slot的分配在任什麼時候間點均可以進行從新分配。

客戶端在對key進行讀寫操做時,能夠鏈接Cluster中的任意一個分片,若是操做的key不在此分片負責的Slot範圍內,Redis Cluster會自動將請求重定向到正確的分片上。

hash tags

在基礎的分片原則上,Redis還支持hash tags功能,以hash tags要求的格式明明的key,將會確保進入同一個Slot中。例如:{uiv}user:1000和{uiv}user:1001擁有一樣的hash tag {uiv},會保存在同一個Slot中。

使用Redis Cluster時,pipelining、事務和LUA Script功能涉及的key必須在同一個數據分片上,不然將會返回錯誤。如要在Redis Cluster中使用上述功能,就必須經過hash tags來確保一個pipeline或一個事務中操做的全部key都位於同一個Slot中。

有一些客戶端(如Redisson)實現了集羣化的pipelining操做,能夠自動將一個pipeline裏的命令按key所在的分片進行分組,分別發到不一樣的分片上執行。可是Redis不支持跨分片的事務,事務和LUA Script仍是必須遵循全部key在一個分片上的規則要求。

主從複製 vs 集羣分片

在設計軟件架構時,要如何在主從複製和集羣分片兩種部署方案中取捨呢?

從各個方面看,Redis Cluster都是優於主從複製的方案

  • Redis Cluster可以解決單節點上數據量過大的問題
  • Redis Cluster可以解決單節點訪問壓力過大的問題
  • Redis Cluster包含了主從複製的能力

那是否是表明Redis Cluster永遠是優於主從複製的選擇呢?

並非。

軟件架構永遠不是越複雜越好,複雜的架構在帶來顯著好處的同時,必定也會帶來相應的弊端。採用Redis Cluster的弊端包括:

  • 維護難度增長。在使用Redis Cluster時,須要維護的Redis實例數倍增,須要監控的主機數量也相應增長,數據備份/持久化的複雜度也會增長。同時在進行分片的增減操做時,還須要進行reshard操做,遠比主從模式下增長一個Slave的複雜度要高。
  • 客戶端資源消耗增長。當客戶端使用鏈接池時,須要爲每個數據分片維護一個鏈接池,客戶端同時須要保持的鏈接數成倍增多,加大了客戶端自己和操做系統資源的消耗。
  • 性能優化難度增長。你可能須要在多個分片上查看Slow Log和Swap日誌才能定位性能問題。
  • 事務和LUA Script的使用成本增長。在Redis Cluster中使用事務和LUA Script特性有嚴格的限制條件,事務和Script中操做的key必須位於同一個分片上,這就使得在開發時必須對相應場景下涉及的key進行額外的規劃和規範要求。若是應用的場景中大量涉及事務和Script的使用,如何在保證這兩個功能的正常運做前提下把數據平均分到多個數據分片中就會成爲難點。

因此說,在主從複製和集羣分片兩個方案中作出選擇時,應該從應用軟件的功能特性、數據和訪問量級、將來發展規劃等方面綜合考慮,只在確實有必要引入數據分片時再使用Redis Cluster。
下面是一些建議:

  1. 須要在Redis中存儲的數據有多大?將來2年內可能發展爲多大?這些數據是否都須要長期保存?是否可使用LRU算法進行非熱點數據的淘汰?綜合考慮前面幾個因素,評估出Redis須要使用的物理內存。
  2. 用於部署Redis的主機物理內存有多大?有多少能夠分配給Redis使用?對比(1)中的內存需求評估,是否足夠用?
  3. Redis面臨的併發寫壓力會有多大?在不使用pipelining時,Redis的寫性能能夠超過10萬次/秒(更多的benchmark能夠參考 https://redis.io/topics/benchmarks )
  4. 在使用Redis時,是否會使用到pipelining和事務功能?使用的場景多很少?

綜合上面幾點考慮,若是單臺主機的可用物理內存徹底足以支撐對Redis的容量需求,且Redis面臨的併發寫壓力距離Benchmark值還尚有距離,建議採用主從複製的架構,能夠省去不少沒必要要的麻煩。同時,若是應用中大量使用pipelining和事務,也建議儘量選擇主從複製架構,能夠減小設計和開發時的複雜度。

Redis Java客戶端的選擇

Redis的Java客戶端不少,官方推薦的有三種:Jedis、Redisson和lettuce。

在這裏對Jedis和Redisson進行對比介紹

Jedis:

  • 輕量,簡潔,便於集成和改造
  • 支持鏈接池
  • 支持pipelining、事務、LUA Scripting、Redis Sentinel、Redis Cluster
  • 不支持讀寫分離,須要本身實現
  • 文檔差(真的不好,幾乎沒有……)

Redisson:

  • 基於Netty實現,採用非阻塞IO,性能高
  • 支持異步請求
  • 支持鏈接池
  • 支持pipelining、LUA Scripting、Redis Sentinel、Redis Cluster
  • 不支持事務,官方建議以LUA Scripting代替事務
  • 支持在Redis Cluster架構下使用pipelining
  • 支持讀寫分離,支持讀負載均衡,在主從複製和Redis Cluster架構下均可以使用
  • 內建Tomcat Session Manager,爲Tomcat 6/7/8提供了會話共享功能
  • 能夠與Spring Session集成,實現基於Redis的會話共享
  • 文檔較豐富,有中文文檔

對於Jedis和Redisson的選擇,一樣應遵循前述的原理,儘管Jedis比起Redisson有各類各樣的不足,但也應該在須要使用Redisson的高級特性時再選用Redisson,避免形成沒必要要的程序複雜度提高。

Jedis:
github:https://github.com/xetorthio/jedis
文檔:https://github.com/xetorthio/jedis/wiki

Redisson:
github:https://github.com/redisson/redisson
文檔:https://github.com/redisson/redisson/wiki

 

 

 

 

1. 什麼是redis

Redis是一個nosql的高性能Key-Value內存數據庫,支持網絡,亦可本地持久化。3.0.0Beta版已支持集羣。

詳細資料可見http://www.redis.cn/

2. Redis關鍵參數

Ø 客戶端最大鏈接數(maxclients)

可能的錯誤信息:max number of clients reached。

 

默認爲0,即不限制,通常不須要更改,因此客戶端鏈接限制,取決於操做系統參數ulimit -n(max open files),可經過修改/etc/security/limits.conf文件以永久生效。

 

如下場景在性能壓測時出現,涉及三個參數:

可能的錯誤信息:scheduled to be closed ASAP for overcoming of output buffer limits。

有時候明明master/slave都活得好好的,忽然間就說要從新進行全同步了:

1.Slave顯示:# MASTER time out: no data nor PING received…

2.Master顯示:# Client addr=10.175.162.123:44670 flags=S oll=104654 omem=2147487792 events=rw cmd=sync scheduled to be closed ASAP for overcoming of output buffer limits.

 

Ø 主從響應策略(repl-ping-slave-period/repl-timeout)

slave會每隔repl-ping-slave-period(默認10秒)ping一次master,若是超過repl-timeout(默認 60秒)都沒有收到響應,就會認爲Master掛了。若是Master明明沒掛但被阻塞住了也會報這個錯。能夠適當調大repl-timeout。

 

Ø 客戶端輸出緩衝區(client-output-buffer-limit)

該參數有三種場景策略,主要是第二種slave場景。當使用主從複製時,性能壓測下,數據量會急劇增加,致使從節點須要複製的數據很大,消耗時長增長。slave沒掛但被阻塞住了,好比正在loading Master發過來的RDB, Master的指令不能馬上發送給slave,就會放在output buffer中(見oll是命令數量,omem是大小),在配置文件中有以下配置:client-output-buffer-limit slave 256mb 64mb 60, 這是說負責發數據給slave的client,若是buffer超過256m或者連續60秒超過64m,就會被馬上強行關閉。因此此時應該相應調大數值,不然就會出現很悲劇的循環:Master傳輸一個很大的RDB給Slave,Slave努力地裝載,但還沒裝載完,Master對client的緩存滿了,再來一次。

平時能夠在master執行 redis-cli client list 找那個cmd=sync,flag=S的client,注意OMem的變化。

 

Ø 日誌級別和輸出(loglevel、logfile)

 

生產可調整爲warning級別,並重定向到某個文件。這對排除問題頗有幫助。

 

3. 性能調優

Ø 內存分配限制

可能的錯誤信息:Cannot allocate memory

 

Redis在主從複製時,須要fork子進程來進行操做,若是你的應用堆積了很大數據在內存中,那麼就須要針對這個子進程申請相應的內存空間,此時會受到操做系統的限制。經過更改系統配置文件/etc/sysctl.conf的vm.overcommit_memory=1以永久生效。該參數有0、一、2三個值。1表示容許分配全部的物理內存,而無論當前的內存狀態如何。

 

Ø 客戶端頻繁獲取鏈接限制

可能的錯誤信息:Cannot assign requested address

 

頻繁地連服務器,但每次鏈接都在短期內結束,致使不少的TIME_WAIT,以致於用光端口號,因此新鏈接沒辦法綁定端口。修改以下2個內核參數:

sysctl -w net.ipv4.tcp_timestamps=1,開啓對於TCP時間戳的支持,若該項設置爲0,則下面一項設置不起做用;

sysctl -w net.ipv4.tcp_tw_recycle=1,表示開啓TCP鏈接中TIME-WAIT sockets的快速回收。

 

 

 

1、 Redis部署結構優化建議

1. Master不作AOF或RDB持久化,Slave作AOF持久化,建議同時作RDB持久化 
2. 全部Master所有增長Slave 
3. Master掛載Slave不超過2個,採用M-S-S方式掛載。若想保證高可用,即主從切換,可採用Keepalived機制.

備註:以上是基於Redis部署結構不合理提出的建議,同時也參考了新浪微博、淘寶架構中Redis優化方案給出

2、 Redis配置優化建議

1.tcp-keepalive 60 阻止因爲某個command執行過長達到timeout超時時間而被斷開鏈接,且能夠提升鏈接錯誤的檢測.
  • 1
  • 2
2.stop-writes-on-bgsave-error no 當bgsave快照操做出錯時中止寫數據到磁盤,這樣後面寫操做均會失敗,爲了避免影響後續寫操做,故需將該項值改成no.
  • 1
  • 2
3.rdbchecksum no 檢查RDB數據的正確性,會犧牲10%的性能,故建議關閉.
  • 1
  • 2
4.auto-aof-rotate-max-size 20gb auto-aof-rotate-max-total 4 auto-aof-rewrite-percentage 0 (關閉rewrite模式) 將AOF rewrite模式改成rotate模式,即將AOF在線實時Rewrite的功能,切換到線下操做,1份AOF文件切割成多份(相似日誌切割),這樣提高了redis性能的同時提高內存的利用率.
  • 1
  • 2
  • 3
  • 4
5.no-appendfsync-on-rewrite yes 避免新修改數據刷磁盤時出現IO阻塞
  • 1
  • 2

備註:以上是基於Redis配置不合理提出的優化建議

3、 系統內核配置優化建議

1.開啓了AOF模式,爲了緩解IO阻塞 
編輯/etc/sysctl.conf ,添加以下配置:

vm.dirty_background_ratio = 5 vm.dirty_ratio = 10
  • 1
  • 2

而後sysctl -p 使配置文件生效.

2.開啓了RDB模式,爲了不Fork失敗

編輯/etc/sysctl.conf ,改vm.overcommit_memory=1, 
而後sysctl -p 使配置文件生效

備註:以上是基於測試結果給出的系統內核優化建議

後續計劃:

以上是從架構角度提出的優化建議,後續會從業務角度,分析內存類型是否合理、冷熱數據劃分是否合理等

備註: 
關於冷熱數據劃分,可以使用以下Redis命令進行統計分析:

OBJECT REFCOUNT 該命令主要用於調試(debugging),它可以返回指定key所對應value被引用的次數.

OBJECT ENCODING 該命令返回指定key對應value所使用的內部表示(representation)(譯者注:也能夠理解爲數據的壓縮方式).

OBJECT IDLETIME 該命令返回指定key對應的value自被存儲以後空閒的時間,以秒爲單位(沒有讀寫操做的請求) ,這個值返回以10秒爲單位的秒級別時間,這一點可能在之後的實現中改善