Winse Blog

走走停停都是风景, 熙熙攘攘都向最好, 忙忙碌碌都为明朝, 何畏之.

Redis批量操作

紧接上一篇优化的文章,在生产进行shard分库/分片操作后,部分大量zsort键值对拆散到多个redis实例。原来统计总量的命令现在需要汇总后才行。

今天就来说说Redis里面的批量操作,批量操作其实就是循环,把大部分的工作让机器做了。逻辑比较复杂的用比较实现,功能简单的用脚本即可。

  • 单机用lua脚本
1
2
3
# redis-cli

eval "local aks=redis.call('keys', '*'); local res=0; for i,r in ipairs(aks) do res=res+redis.call('hlen', r) end; return res" 0

通过keys获取所有的键(都是hash类型),然后计算出匹配的hash包括的键值对总数。

同一个实例的小数据量的统计,lua脚本优势还是比较明显的:redis自带的还能编程。

  • shell脚本

看官网的文档:the Redis command line interface

1
2
3
4
5
6
7
8
9
10
11
12
13
## shell

cat commands | redis-cli --raw

## redis-cli

127.0.0.1:6379> CONFIG RESETSTAT
OK
...
127.0.0.1:6379> info stats
# Stats
total_connections_received:2
...

注意:不要用循环然后调用redis-cli COMMAND,这种方式会产生很多的TCP连接,如果要执行的命令太多,可能导致TCP端口不够用。

还有 redis-cli --statredis-cli --scanredis-cli monitor 这些命令挺有意思的。

  • 直接tcp连接管道操作
1
2
3
4
5
6
7
8
9
10
11
## shell

sed 's/$/^M/' commands > test.redis.cmd
cat test.redis.cmd | nc localhost 6379

## redis-cli

127.0.0.1:6379> info stats
# Stats
total_connections_received:1
...

通过tcp连接操作,一批次的操作就一个连接。但是,操作的时刻需要主要,linux的换行符是 \n,而redis需要的是 \r\n;还有通过tcp连接返回的结果是redis协议原始数据,没有经过处理,需要稍微看看协议规范Redis Protocol specification

  • pipelining

redis自带的管道功能,性能提升相当明显(官网数据)。

原来看到过觉得高大上,准备试试。但是返回结果却只有一个成功数而已!!

1
2
3
4
$ echo 119.84.100.68 | xargs -I{} echo "1:ips:2016-08-16:{}" | while read cmd ; do echo -e "*2\r\n\$5\r\nzcard\r\n\$${#cmd}\r\n$cmd\r\n" ; done  | redis-cli -p 26379 --pipe
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 1

注意:redis-cli带的pipe就比较坑:最后只返回操作成功失败的统计数量,只适合用来做更新操作Redis Mass Insertion!!

要用pipeline来实现查询的话,用java/scala等语言来弄吧。

最后帖一下用shell脚本:

1
2
3
4
5
6
7
8
9
10
11
12
day=${day:-`date "+%Y-%m-%d"`}
key="1:ips:$day"

for h in hadoop-master{1..4} ; do 
  exists=$(redis-cli -h $h -p 6372 exists $key)
  if [ $exists -gt 0 ] ; then
    redis-cli -h $h -p 6372 zrange $key 0 -1 > activeresourceip$day.data
    for h in hadoop-master{1..4} ; do
      cat activeresourceip$day.data | while read ip ; do echo -e "zcard $key:$ip\r" ; done | redis-cli -h $h -p 6372
    done
  fi
done | awk '{s+=$1} END {print s}' 

–END

Comments