Redis性能調優,影響Redis性能的因素

頭像

👁 關注微信公衆號:非典型理科男 回覆:redis獲取redis三本經典著做html

序言

上一篇文章《Redis爲何這麼快》介紹了Redis性能評估工具,以及Redis高性能的緣由。詳細請見: 這篇咱們將從業務的視角,講解下影響Redis性能的因素以及如何提高Redis使用的性能。redis

從用戶到Redis請求過程分析

以最經常使用場景緩存爲例,流量從用戶到Redis Server的過程以下所示:後端

  1. 用戶訪問後端服務器,調用對應的Controller
  2. Controller命中緩存記錄,經過Jedis客戶端調用Reids從緩存獲取記錄。 若是使用的Jedis鏈接池獲取Jedis對象,從Jedis鏈接池獲取一個Jedis鏈接實例。
  3. Jedis使用Redis序列化協議(RESP)將命令編碼,放到Redis Server輸入緩衝區中。
  4. Redis Server從輸入緩衝區獲取命令並執行。
  5. 執行結束後將執行結果放入到輸出緩衝區。
  6. Jedis客戶端從輸出緩衝區獲取執行結果並返回給Controller。
  7. Controller執行完業務邏輯相應用戶的請求。

從上面時序圖能夠看出,用戶請求經過Redis client經由網路到達Redis Server。緩存

所以在考慮使用Redis性能的時候要從客戶端和服務端兩個角度考慮。 對於業務方來講, 合理使用Redis特性比Redis服務器的優化可操做性更強,也更容易得到好的效果。bash

下面將從業務優化和服務器優化兩個方面介紹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開發和運維》

節選自:《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 -q
SET: 552028.75 requests per second
GET: 707463.75 requests per second
LPUSH: 767459.75 requests per second
LPOP: 770119.38 requests per second
Intel(R) Xeon(R) CPU E5520 @ 2.27GHz (without pipelining)
複製代碼

無pipeline的狀況:

$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q
SET: 122556.53 requests per second
GET: 123601.76 requests per second
LPUSH: 136752.14 requests per second
LPOP: 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喜歡下面的硬件條件:

  1. 高帶寬,低延遲的網絡: Redis的性能中網絡帶寬和延遲一般是最大短板。所以,須要選擇高帶寬,低延遲的網絡。
  2. 大緩存快速 CPU: 而不是多核。這種場景下面,比較推薦 Intel CPU。AMD CPU 可能只有 Intel CPU 的一半性能(經過對 Nehalem EP/Westmere EP/Sandy 平臺的對比)。 當其餘條件至關時候,CPU 就成了 redis-benchmark 的限制因素。
  3. 大對象(>10k)存儲時內存和帶寬顯得尤爲重要。 可是更重要是優化大對象的存儲。
  4. 將Redis運行在物理機器上:Redis 在 VM 上會變慢。虛擬化對普通操做會有額外的消耗,Redis 對系統調用和網絡終端不會有太多的 overhead。建議把 Redis 運行在物理機器上。

大Value的影響

包大小影響Redis的相應速度。 以太網網數據包在 1500 bytes 如下時, 將多條命令包裝成 pipelining 能夠大大提升效率。事實上,處理 10 bytes,100 bytes, 1000 bytes 的請求時候,吞吐量是差很少的,詳細能夠見下圖。

不一樣數據包大小下的併發量

因此,當大value(>10k)存在時要及時優化掉。

參考文檔:

  1. Redis Benchmark
  2. Redis 命令合集

頭像
👁 關注微信公衆號:非典型理科男 回覆:redis獲取redis三本經典著做

本文由博客羣發一文多發等運營工具平臺 OpenWrite 發佈