最近在作S線的業務中,須要計算用戶的排名以及不少雜項數據。因爲數據量過多,爲了保證系統響應速度和負載能力,因此在Redis中產生了緩存(基於天天)。php
Redis的pipeline(管道)功能在命令行中沒有,但redis是支持pipeline的,並且在各個語言版的client中都有相應的實現。 因爲網絡開銷延遲,就算redis server端有很強的處理能力,也會因爲收到的client消息少,而形成吞吐量小。當client 使用pipelining 發送命令時,redis server必須將部分請求放到隊列中(使用內存),執行完畢後一次性發送結果;若是發送的命令不少的話,建議對返回的結果加標籤,固然這也會增長使用的內存。html
Pipeline在某些場景下很是有用,好比有多個command須要被「及時的」提交,並且他們對相應結果沒有互相依賴,並且對結果響應也無需當即得到,那麼pipeline就能夠充當這種「批處理」的工具;並且在必定程度上,能夠較大的提高性能,性能提高的緣由主要是TCP連接中較少了「交互往返」的時間。redis
$redis = new Redis(); $redis->connect('host','port'); $redis->pipeline(); //do any more $redis->exec(); $redis-close();
Redis內置了對LUA腳本的支持,而且在計算過程當中保證了腳本中執行的原子性。所以在開發過程當中對Redis對Lua的支持進行了學習。從 Redis 2.6.0 版本開始,經過內置的 Lua 解釋器,可使用EVAL命令對 Lua 腳本進行求值。如下將Redis對LUA的支持進行總結。緩存
官網文檔上有這樣一段話(官方文檔):網絡
A Redis script is transactional by definition, so everything you can do with a Redis transaction, you can also do with a script, and usually the script will be both simpler and faster.dom
由此能夠看出,官方仍是支持你們儘可能使用lua script來代替transaction的。工具
關於更多設計細節,能夠參考Redis 設計與實現。性能
$redis = new Redis(); $redis->connect('host','port'); $script = " local result ={} for i = 1,#(KEYS) do result[i]= redis.call('get',KEYS[i]) //do any more end return result "; $result = $redis->eval($script, $keys, count($keys));
$host = ''; $port = ''; $keys = ['test1','test2','test3','test4','test5','tese6','test7']; $redis = new Redis(); $redis->connect($host, $port); foreach($keys as $key) { $random = rand(1,100); $value = ''; for ($i=0; $i < $random; $i++) { $value .= $key; } $redis->set($key, $value); } $redis->close(); //定義的lua腳本 $script = "local result ={} for i = 1,#(KEYS) do result[i]= redis.call('get',KEYS[i]) end return result"; for($i =0 ; $i < 100; $i ++ ) { $start = microtime(true); $redis = new Redis(); $redis->connect($host, $port); foreach($keys as $key) { $redis->get($key); } $redis->close(); $foreachTime[$i] = ( microtime(true) - $start)*1000; file_put_contents('redis_time.txt','foreach:'.$foreachTime[$i], 8); file_put_contents('redis_time.txt', "\n", 8); $start = microtime(true); $redis = new Redis(); $redis->connect($host, $port); $redis->pipeline(); foreach($keys as $key) { $redis->get($key); } $redis->exec(); $redis->close(); $pipeTime[$i] = ( microtime(true) - $start)*1000; file_put_contents('redis_time.txt','pipeline:'.$pipeTime[$i], 8); file_put_contents('redis_time.txt', "\n", 8); $start = microtime(true); $redis = new Redis(); $redis->connect($host, $port); $redis->eval($script, $keys, count($keys)); $redis->close(); $evalTime[$i] = ( microtime(true) - $start)*1000; file_put_contents('redis_time.txt','eval:'.$evalTime[$i], 8); file_put_contents('redis_time.txt', "\n", 8); echo $foreachTime[$i]; echo PHP_EOL; echo $pipeTime[$i]; echo PHP_EOL; echo $evalTime[$i]; echo PHP_EOL; } file_put_contents('redis_time.txt', "\n", 8); file_put_contents('redis_time.txt', "\n", 8); file_put_contents('redis_time.txt', "\n", 8); file_put_contents('redis_time.txt','foreach avg: '.( array_sum($foreachTime)/100), 8); file_put_contents('redis_time.txt', "\n", 8); file_put_contents('redis_time.txt','pipe avg: '.( array_sum($pipeTime)/100), 8); file_put_contents('redis_time.txt', "\n", 8); file_put_contents('redis_time.txt','eval avg: '.( array_sum($evalTime)/100), 8); file_put_contents('redis_time.txt', "\n", 8);
測試結果(ms)學習
foreach avg: 191.03681325912 pipe avg: 53.837163448334 eval avg: 54.453134536743
從結果來看,lua和pipeline的性能提高差很少,可是相對而言,lua更加靈活(可寫簡單業務)。 一樣,因爲redis lua和 redis pipeline的耗時性,因爲redis原子性的要求,致使同一時間只能執行一個命令,所以,單個pipeline/lua不建議太大,致使系統被佔用,從而引發其餘服務沒法正常進行。測試
構建數據
$host = ''; $port = ''; $redis = new Redis(); $redis->connect($host, $port); $rankTypes = [1,3,4,5,6,9,14]; for($i = 0; $i< 1000; $i++){ $rand = array_rand($rankTypes); $key = 'user:test:'.$i; $redis->hset($key,'rank_type',$rankTypes[$rand]); $redis->hset($key,'rank',$i); $redis->hset($key,'score', $i); $redis->hset($key,'blog_read_score', $i); $redis->hset($key,'blog_read_rank', $i); $redis->hset($key,'interact_score', $i); $redis->hset($key,'interact_rank', $i); $redis->hset($key,'close_score',$i); $redis->hset($key,'close_rank',$i); $redis->hset($key,'mention_score',$i); $redis->hset($key,'mention_rank',$i); $key = 'rank_key:'.$rankTypes[$rand]; $redis->lpush($key, $i); }
普通查詢
$key_format = 'rank_key:%d'; $redis = new Redis(); $redis->connect($host,$port); $user = []; $start = microtime(true); foreach ($rankTypes as $type) { $key = sprintf($key_format, $type); $top50 = $redis->lrange($key, 0, 50); foreach ($top50 as $uid) { $key = 'user:test:'.$uid; $user[$type][$uid] = $redis->hGetAll($key); } } $redis->close(); echo ( microtime(true) - $start)*1000; //耗時2S~3S+ echo PHP_EOL;
pipeline查詢(須要兩條,由於需求限定須要上次的查詢結果)
$key_format = 'rank_key:%d'; $start = microtime(true); $redis = new Redis(); $redis->connect($host,$port); $redis->pipeline(); foreach ($rankTypes as $type) { $key = sprintf($key_format, $type); $redis->lrange($key, 0, 49); } $top50 = $redis->exec(); $redis->pipeline(); foreach ($rankTypes as $key=>$type) { foreach ($top50[$key] as $uid) { $key = 'user:test:'.$uid; $redis->hGetAll($key); } } $users = $redis->exec(); $redis->close(); $result = []; $i = 0; foreach ($users as $key=>$user) { if($key != 0 && $key%50 == 0) { $i++; continue; } $result[$rankTypes[$i]][] = $user; # code... } echo ( microtime(true) - $start)*1000; //耗時100ms~400ms echo PHP_EOL;
lua腳本查詢
$script = "local users = {} for i = 1,#(KEYS) do local key = 'rank_key:'..KEYS[i] users[i] = redis.call('lrange',key,0,49) end local result ={} for j = 1,#(KEYS) do local user_table = {} for k=1,50,1 do local user_key = 'user:test:'..users[j][k] user_table[k] = redis.call('hgetall', user_key) end result[j] = user_table end return result"; $start = microtime(true); $redis = new Redis(); $redis->connect($host,$port); $result = $redis->eval($script, $rankTypes, count($rankTypes)); $redis->close(); echo ( microtime(true) - $start)*1000; //耗時100ms~200ms echo PHP_EOL;