序言
上一篇文章《Redis為什么這么快》介紹了Redis性能評估工具姻乓,以及Redis高性能的原因猪瞬。詳細請見: 這篇我們將從業(yè)務的視角啸胧,講解下影響Redis性能的因素以及如何提升Redis使用的性能邑闲。
從用戶到Redis請求過程分析
以最常用場景緩存為例戳寸,流量從用戶到Redis Server的過程如下所示:
image
用戶訪問后端服務器兜畸,調用對應的Controller
Controller命中緩存記錄努释,通過Jedis客戶端調用Reids從緩存獲取記錄。 如果使用的Jedis連接池獲取Jedis對象咬摇,從Jedis連接池獲取一個Jedis連接實例伐蒂。
Jedis使用Redis序列化協議(RESP)將命令編碼,放到Redis Server輸入緩沖區(qū)中肛鹏。
Redis Server從輸入緩沖區(qū)獲取命令并執(zhí)行逸邦。
執(zhí)行結束后將執(zhí)行結果放入到輸出緩沖區(qū)。
Jedis客戶端從輸出緩沖區(qū)獲取執(zhí)行結果并返回給Controller在扰。
Controller執(zhí)行完業(yè)務邏輯相應用戶的請求缕减。
從上面時序圖可以看出,用戶請求通過Redis client經由網路到達Redis Server芒珠。
因此在考慮使用Redis性能的時候要從客戶端和服務端兩個角度考慮桥狡。 對于業(yè)務方來說, 合理使用Redis特性比Redis服務器的優(yōu)化可操作性更強皱卓,也更容易獲得好的效果裹芝。
下面將從業(yè)務優(yōu)化和服務器優(yōu)化兩個方面介紹Redis的優(yōu)化。
業(yè)務優(yōu)化
查詢本地redis的延遲通常低于1毫秒娜汁,而查詢同一個數據中心的redis的延遲通常低于5毫秒嫂易。也就是說,網絡傳輸的損耗為實際操作用時的5倍掐禁。
因此怜械,從客戶端角度颅和,如何減少網絡耗時至關重要。
使用連接池減少建立連接和銷毀連接的時間開銷
Jedis是Java語言使用最多的Redis客戶端缕允。 Jedis支持直連和連接池的兩種方式融虽。
直連的方式:
# 1. 生成一個Jedis對象,這個對象負責和指定Redis實例進行通信 Jedis jedis = new Jedis("127.0.0.1", 6379); # 2. jedis執(zhí)行set操作 jedis.set("hello", "world"); # 3. jedis執(zhí)行get操作 value="world" String value = jedis.get("hello");
所謂直連是指Jedis每次都會新建TCP 連接灼芭,使用后再斷開連接。 我們都知道新建TCP連接經過3次握手般又,釋放TCP連接經過4次揮手彼绷,新建和回收是非常耗時操作。對于頻繁訪問Redis的場景顯然不是高效的使用方式茴迁。
Jedis也提供了連接池的方式寄悯。節(jié)選自:《Redis開發(fā)和運維》
// 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. 執(zhí)行操作 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執(zhí)行命令,注意此時命令并未真正執(zhí)行 for (String key : keys) { pipeline.del(key); } // 3)執(zhí)行命令 pipeline.sync(); }
將del命令封裝到pipeline中茬底,可以調用pipeline.del(String key)沪悲,此時不會真正的 執(zhí)行命令。
使用pipeline.sync()完成此次pipeline對象的調用阱表。
除了pipeline.sync()殿如,還可以使用pipeline.syncAndReturnAll()將 pipeline的命令進行返回贡珊。
pipeline提升性能的原因
pipeline提升性能的一個原因是減少了命令總的RTT時間(往返時延), 另外一方面減少 總的系統(tǒng)調用的次數。
RTT(Round-Trip Time): 往返時延涉馁。在計算機網絡中它是一個重要的性能指標门岔,表示從發(fā)送端發(fā)送數據開始,到發(fā)送端收到來自接收端的確認(接收端收到數據后便立即發(fā)送確認)烤送,總共經歷的時延寒随。往返延時(RTT)由三個部分決定:即鏈路的傳播時間、末端系統(tǒng)的處理時間以及路由器的緩存中的排隊和處理時間帮坚。其中妻往,前面兩個部分的值作為一個TCP連接相對固定,路由器的緩存中的排隊和處理時間會隨著整個網絡擁塞程度的變化而變化试和。所以RTT的變化在一定程度上反映了網絡擁塞程度的變化讯泣。簡單來說就是發(fā)送方從發(fā)送數據開始,到收到來自接受方的確認信息所經歷的時間阅悍。
pipline和lua腳本的不同
Redis原生支持Lua語言好渠,并且提供了通過客戶端執(zhí)行l(wèi)ua腳本的命令。
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返回執(zhí)行結果肴茄。
更多的Lua腳本晌畅,會在其他文章中介紹。
可以關注微信公眾號:非典型理科男寡痰,查看全部文章列表閱讀Lua腳本相關的文章抗楔。
pipeline和Lua比較:
(1) 返回結果不同: pipeline會把命令執(zhí)行結果都返回出來, lua腳本只有一個返回結果拦坠。
(2) 使用場景不同: lua腳本可以提供復雜邏輯運算并且提供了緩存腳本的功能连躏,提升像原生命令一樣的性能體驗。 因此lua腳本可以用在處理邏輯復雜贞滨,不需要返回或者只返回操作結果的場景入热。 pipeline用在合并命令減少執(zhí)行開銷和redis server壓力的場景下。
在使用pipeline時有幾個注意事項:
(1) pipeline執(zhí)行命令雖然沒有明確的執(zhí)行命令數量的限制晓铆,但是建議限制執(zhí)行命令數量勺良。 執(zhí)行命令數量過多一方面占用網絡帶寬,另一方面會阻塞客戶端骄噪。
Redis Server性能影響因素
影響Redis Server性能主要有硬件尚困、數據分布和配置有關。
硬件因素
Redis喜歡下面的硬件條件:
高帶寬链蕊,低延遲的網絡: Redis的性能中網絡帶寬和延遲通常是最大短板事甜。因此谬泌,需要選擇高帶寬,低延遲的網絡逻谦。
大緩存快速 CPU: 而不是多核掌实。這種場景下面,比較推薦 Intel CPU邦马。AMD CPU 可能只有 Intel CPU 的一半性能(通過對 Nehalem EP/Westmere EP/Sandy 平臺的對比)贱鼻。 當其他條件相當時候,CPU 就成了 redis-benchmark 的限制因素滋将。
大對象(>10k)存儲時內存和帶寬顯得尤其重要忱嘹。 但是更重要是優(yōu)化大對象的存儲。
將Redis運行在物理機器上:Redis 在 VM 上會變慢耕渴。虛擬化對普通操作會有額外的消耗,Redis 對系統(tǒng)調用和網絡終端不會有太多的 overhead齿兔。建議把 Redis 運行在物理機器上橱脸。
大Value的影響
包大小影響Redis的相應速度。 以太網網數據包在 1500 bytes 以下時分苇, 將多條命令包裝成 pipelining 可以大大提高效率添诉。事實上,處理 10 bytes医寿,100 bytes栏赴, 1000 bytes 的請求時候,吞吐量是差不多的靖秩,詳細可以見下圖须眷。
不同數據包大小下的并發(fā)量
所以,當大value(>10k)存在時要及時優(yōu)化掉沟突。
參考文檔: