reference:https://my.oschina.net/limingluzhu/blog/484506
http://weipengfei.blog.51cto.com/1511707/1215042
由于需求的更改,之前做的一个项目需要对redis中存储的数据格式进行修改。为防止新包发布后,老数据会导致新数据无法插入。所以,必须在发布前删除掉所有的老数据。当前redis是一个公用的集群,里面涉及好几个业务。那么问题来了,如何删除大量的老数据(目前库中的key总数为1200w),而又不影响其他业务的使用。
常见批量删除redis数据的方法:
- 如果待删除数据的key是已知的,可以使用redis-cli的del命令
/usr/local/redis/bin/redis-cli del key
或者也可以使用其他高级语言对应的redis包或库。如java下的jedis,python下的redis库java: jdeis.del(key) python: redis.delete(key)
- 如果待删除数据的key是未知的,只知道满足特定模式的key。 这种情况下,就需要使用redis的
keys
命令找出满足特定模式的key
可以使用linux的找到满足前缀是video的所有key /usr/local/redis/bin/redis-cli keys video_*
xargs
来完成批量删除/usr/local/redis/bin/redis-cli keys video* | xargs /usr/local/redis/bin/redis-cli del
- 如果待删除的数据是库中所有的数据,可以使用
flushdb
清除整个库/usr/local/redis/bin/redis-cli flushdb
几种方法的说明
- 第一种方法需要明确知道特定的key
- 使用
keys
命令,当库中数据量过大,keys
命令会阻塞redis的其他所有请求。无疑,这种方式对公用redis集群是不可取的。当然,具体还得考虑业务的需要。实在不行,也可以把删除脚本放到业务访问量比较小的时间点上执行。 - 使用
flushdb
这种方式,会对整个库中的数据进行清理。
我的解决方法
线上redis集群使用的是matser-slave的结构。所以可以把阻塞请求的keys命令放到slave节点上执行,找出所有满足特定前缀的key。然后使用shell脚本或高级语言在master节点上删除数据。
#获取前缀是video,album,actor所有的key,并把这些key追加导出到文件/data/keys.txt中
#!/bin/bash
keys=('video' 'album' 'actor');
host='localhost';
port='6378';
for key in ${keys[@]};
do
cmd="/usr/local/redis/bin/redis-cli -h ${host} -p ${port} keys gal.video.${key}* >> /data/keys.txt";
echo ${cmd};
eval ${cmd};
done;
# 根据前面生成的key,删除数据
#!/bin/bash
host='localhost';
port='6378';
file="/data/keys.txt";
i=0;
cat ${file} | while read key;
do
let i=i+1;
cmd="/usr/local/redis/bin/redis-cli -h ${host} -p ${port} del ${key}";
echo "line:"${i}",cmd:"${cmd};
eval ${cmd};
done;
脚本2由于是逐条发送del命令,执行效率相当的低。测试中大概是1小时删除120w条数据。1200w条需要删除10小时!!! 考虑到每次发送请求的耗时,想到可以使用redis的pipeline来实现批量提交。
__author__ = 'litao'
from redis import Redis
host="127.0.0.1"
port=6379
db=0
r =Redis(host,port,db)
pl=r.pipeline()
per_pipe_size=10000
count=0
file = open("/data/keys.txt")
print "start del all keys in "+file.name
while 1:
lines = file.readlines(10000)
if not lines:
break
for key in lines:
key=key.strip('\n')
pl.delete(key)
count=count+1
if(count==per_pipe_size):
count=0
pl.execute()
pl.execute()
file.close()
print 'finish del all keys'
改进后的脚本2在线上执行时间仅需要2min左右!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#!/usr/bin/python2
import
redis
import
time
def
without_pipeline():
r
=
redis.Redis()
for
i
in
range
(
10000
):
r.ping()
return
def
with_pipeline():
r
=
redis.Redis()
pipeline
=
r.pipeline()
for
i
in
range
(
10000
):
pipeline.ping()
pipeline.execute()
return
def
bench(desc):
start
=
time.clock()
desc()
stop
=
time.clock()
diff
=
stop
-
start
print
"%s has token %s"
%
(desc.func_name,
str
(diff))
if
__name__
=
=
'__main__'
:
bench(without_pipeline)
bench(with_pipeline)
|