最近在作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;