最早发表于范浩波科学院Redis管道(pipelining)用于将多个不相关的命令打包批量执行,从而减少多个命令单独执行带来的网络交互时间。在一些批量操作数据的场景下,使用管道可以显着提升Redis的读写性能。原理演示Redispipeline的本质是将命令打包并批量执行,将多个网络交互减少为一个。使用管道和不使用管道时的交互过程如下:我们通过nc命令来直观体验Redis管道的使用过程:#安装nc命令$yuminstallnc#nc打包多个命令$(printf"PING\r\nPING\r\nPING\r\n")|nclocalhost6379#Response+PONG+PONG+PONG所以只要通过管道打包命令,Redis就可以批量返回命令的执行结果。流水线应用首先构造示例需要的Hash用户数据:$keyPrex='user:hash:u:';for($i=1;$i<=10000;$i++){$redis->hMset($keyPrex.$i,['name'=>name(),//name()函数生成一个随机名字'age'=>rand(21,30),'sex'=>rand(0,1),'is_new'=>rand(0,1)]);}然后查看导入Redis的数据:127.0.0.1:6379>keysuser:hash:u:*9997)"user:hash:u:3013"9998)"user:hash:u:8971"9999)"user:hash:u:4761"10000)"user:hash:u:1828"127.0.0.1:6379>HGETALLuser:hash:u:18281)"name"2)"ggrg"3)"age"4)"23"5)"sex"6)"0"7)"is_new"8)"1"需要通过a中的一系列筛选逻辑后获取种子用户uid某些社交活动,然后用这些uids去Hash得到用户信息。你会如何处理这种情况?一般情况下,当数据量较小时,我们会直接使用HGETALL命令遍历获取用户数据。$start=nowTime();foreach(range(1,1000)as$id){$user[]=$redis->hgetAll($keyPrex.$id);}echo'Time:',nowTime()-$开始,'ms',PHP_EOL;时间:39ms执行时间:39ms使用管道因为用户数据是通过uid批量获取的,每条命令没有依赖关系,所以可以使用Redis管道来优化查询。$start=nowTime();$redis->multi(Redis::PIPELINE);foreach(range(1,1000)as$id){//返回资源id相同的socket资源,不执行命令$redis->hgetAll($keyPrex.$id);}$user=$redis->exec();echo'Time:',nowTime()-$start,'ms',PHP_EOL;时间:6ms使用流水线后,执行时间显着减少为:6ms。使用tcpdump抓包命令如下:10:45:03.029049IPlocalhost.58176>localhost.6379:Flags[P.],seq2255478840:2255479211,ack3144685411,win342,options[nop,nop,TSval1764047ecr17640474],长度371E..../@.@.o......@...o.8.p.c...V......,.*2$7HGETALL$13user:hash:u:1*2$7HGETALL$13user:hash:u:2*2$7......适用场景批量操作(查询和写入)数据时,尽量避免多次与Redis网络交互。这时候可以通过使用管道来实现,也可以通过在Redis中嵌入Lua脚本来实现。需要注意的是,管道只适用于没有因果关系的多命令操作,否则需要Lua脚本来实现批量操作;在实际应用中,Redis往往无法部署在单机上。如果你想在集群中使用管道,你可以Deployasamaster-multiple-slave架构。此时所有节点的数据是一致的,可以随机选择节点使用pipeline;总之,在批量获取数据的时候,虽然使用Redis的pipeline性能会有明显的提升,但是使用pipeline的时候,Redis会缓存之前的命令结果,最后输出到终端,所以packedcommands不能太多,否则内存占用会很严重。
