序言
上一篇文章《Redis爲什麼這麼快》介紹了Redis性能評估工具,以及Redis高性能的原因。詳細請見: 這篇我們將從業務的視角,講解下影響Redis性能的因素以及如何提升Redis使用的性能。
從用戶到Redis請求過程分析
以最常用場景緩存爲例,流量從用戶到Redis Server的過程如下所示:
image
用戶訪問後端服務器,調用對應的Controller
Controller命中緩存記錄,通過Jedis客戶端調用Reids從緩存獲取記錄。 如果使用的Jedis連接池獲取Jedis對象,從Jedis連接池獲取一個Jedis連接實例。
Jedis使用Redis序列化協議(RESP)將命令編碼,放到Redis Server輸入緩衝區中。
Redis Server從輸入緩衝區獲取命令並執行。
執行結束後將執行結果放入到輸出緩衝區。
Jedis客戶端從輸出緩衝區獲取執行結果並返回給Controller。
Controller執行完業務邏輯相應用戶的請求。
從上面時序圖可以看出,用戶請求通過Redis client經由網路到達Redis Server。
因此在考慮使用Redis性能的時候要從客戶端和服務端兩個角度考慮。 對於業務方來說, 合理使用Redis特性比Redis服務器的優化可操作性更強,也更容易獲得好的效果。
下面將從業務優化和服務器優化兩個方面介紹Redis的優化。
業務優化
查詢本地redis的延遲通常低於1毫秒,而查詢同一個數據中心的redis的延遲通常低於5毫秒。也就是說,網絡傳輸的損耗爲實際操作用時的5倍。
因此,從客戶端角度,如何減少網絡耗時至關重要。
使用連接池減少建立連接和銷燬連接的時間開銷
Jedis是Java語言使用最多的Redis客戶端。 Jedis支持直連和連接池的兩種方式。
直連的方式:
# 1. 生成一個Jedis對象,這個對象負責和指定Redis實例進行通信 Jedis jedis = new Jedis("127.0.0.1", 6379); # 2. jedis執行set操作 jedis.set("hello", "world"); # 3. jedis執行get操作 value="world" String value = jedis.get("hello");
所謂直連是指Jedis每次都會新建TCP 連接,使用後再斷開連接。 我們都知道新建TCP連接經過3次握手,釋放TCP連接經過4次揮手,新建和回收是非常耗時操作。對於頻繁訪問Redis的場景顯然不是高效的使用方式。
Jedis也提供了連接池的方式。
節選自:《Redis開發和運維》
// common-pool連接池配置,這裏使用默認配置GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); // 初始化Jedis連接池 JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);Jedis jedis = null; try { // 1. 從連接池獲取jedis對象 jedis = jedisPool.getResource(); // 2. 執行操作 jedis.get("hello"); } catch (Exception e) { logger.error(e.getMessage(),e); } finally { if (jedis != null) { // 如果使用JedisPool,close操作不是關閉連接,代表歸還連接池 jedis.close(); } }
使用Pipeline或者Lua腳本減少請求次數
通過連接池,減少建立和斷開TCP連接的時間開銷。 另外,redis提供了其他三種方式,通過減少請求次數提升性能。 (1) 批量操作的命令,如mget,mset等 (2) pipeline方式 (3) Lua腳本
pipeline方式
使用redis-benchmark在Intel(R) Xeon(R) CPU E5520 @ 2.27GHz對比pipeline(每次16個命令)和普通請求。
使用pipeline的情況:
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -P 16 -qSET: 552028.75 requests per secondGET: 707463.75 requests per secondLPUSH: 767459.75 requests per secondLPOP: 770119.38 requests per secondIntel(R) Xeon(R) CPU E5520 @ 2.27GHz (without pipelining)
無pipeline的情況:
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -qSET: 122556.53 requests per secondGET: 123601.76 requests per secondLPUSH: 136752.14 requests per secondLPOP: 132424.03 requests per second
從benchmark的結果可以看出,使用pipeline技術比沒有使用性能提升5-10倍左右。
Jedis支持Pipeline特性,我們知道 Redis提供了mget、mset方法,但是並沒有提供mdel方法,如果想實現這個功 能,可以藉助Pipeline來模擬批量刪除,雖然不會像mget和mset那樣是一個原 子命令,但是在絕大數場景下可以使用。
public void mdel(List<String> keys) { Jedis jedis = new Jedis("127.0.0.1"); // 1)生成pipeline對象 Pipe line pipeline = jedis.pipelined(); // 2)pipeline執行命令,注意此時命令並未真正執行 for (String key : keys) { pipeline.del(key); } // 3)執行命令 pipeline.sync(); }
將del命令封裝到pipeline中,可以調用pipeline.del(String key),此時不會真正的 執行命令。
使用pipeline.sync()完成此次pipeline對象的調用。
除了pipeline.sync(),還可以使用pipeline.syncAndReturnAll()將 pipeline的命令進行返回。
pipeline提升性能的原因
pipeline提升性能的一個原因是減少了命令總的RTT時間(往返時延), 另外一方面減少 總的系統調用的次數。
RTT(Round-Trip Time): 往返時延。在計算機網絡中它是一個重要的性能指標,表示從發送端發送數據開始,到發送端收到來自接收端的確認(接收端收到數據後便立即發送確認),總共經歷的時延。往返延時(RTT)由三個部分決定:即鏈路的傳播時間、末端系統的處理時間以及路由器的緩存中的排隊和處理時間。其中,前面兩個部分的值作爲一個TCP連接相對固定,路由器的緩存中的排隊和處理時間會隨着整個網絡擁塞程度的變化而變化。所以RTT的變化在一定程度上反映了網絡擁塞程度的變化。簡單來說就是發送方從發送數據開始,到收到來自接受方的確認信息所經歷的時間。
pipline和lua腳本的不同
Redis原生支持Lua語言,並且提供了通過客戶端執行lua腳本的命令。
Redis Lua腳本相關命令腦圖
比如我們可以用Lua腳本在低版本的Redis上實現分佈式鎖。
local current current = redis.call('incr',KEYS[1]) if tonumber(current) == 1 then redis.call('expire',KEYS[1], ARGV[1]) end return current
調用EVAL命令可以傳入不定的KEY和ARGS的值, 這些值被可以通過KEY[i]和ARGV[i]訪問對應的入參,並且通過return返回執行結果。
更多的Lua腳本,會在其他文章中介紹。
可以關注微信公衆號:非典型理科男,查看全部文章列表閱讀Lua腳本相關的文章。
pipeline和Lua比較:
(1) 返回結果不同: pipeline會把命令執行結果都返回出來, lua腳本只有一個返回結果。
(2) 使用場景不同: lua腳本可以提供複雜邏輯運算並且提供了緩存腳本的功能,提升像原生命令一樣的性能體驗。 因此lua腳本可以用在處理邏輯複雜,不需要返回或者只返回操作結果的場景。 pipeline用在合併命令減少執行開銷和redis server壓力的場景下。
在使用pipeline時有幾個注意事項:
(1) pipeline執行命令雖然沒有明確的執行命令數量的限制,但是建議限制執行命令數量。 執行命令數量過多一方面佔用網絡帶寬,另一方面會阻塞客戶端。
Redis Server性能影響因素
影響Redis Server性能主要有硬件、數據分佈和配置有關。
硬件因素
Redis喜歡下面的硬件條件:
高帶寬,低延遲的網絡: Redis的性能中網絡帶寬和延遲通常是最大短板。因此,需要選擇高帶寬,低延遲的網絡。
大緩存快速 CPU: 而不是多核。這種場景下面,比較推薦 Intel CPU。AMD CPU 可能只有 Intel CPU 的一半性能(通過對 Nehalem EP/Westmere EP/Sandy 平臺的對比)。 當其他條件相當時候,CPU 就成了 redis-benchmark 的限制因素。
大對象(>10k)存儲時內存和帶寬顯得尤其重要。 但是更重要是優化大對象的存儲。
將Redis運行在物理機器上:Redis 在 VM 上會變慢。虛擬化對普通操作會有額外的消耗,Redis 對系統調用和網絡終端不會有太多的 overhead。建議把 Redis 運行在物理機器上。
大Value的影響
包大小影響Redis的相應速度。 以太網網數據包在 1500 bytes 以下時, 將多條命令包裝成 pipelining 可以大大提高效率。事實上,處理 10 bytes,100 bytes, 1000 bytes 的請求時候,吞吐量是差不多的,詳細可以見下圖。
不同數據包大小下的併發量
所以,當大value(>10k)存在時要及時優化掉。
參考文檔:
回覆“資料”,免費獲取 一份獨家嘔心整理的技術資料!