1 背景
需求:工作中需要导出线上redis数据,但需避免使用keys
命令全量扫描,导致瞬间响应卡顿,从而引发超时等问题
方法:最安全的方式是通过dump.rdb备份文件,在本地redis实例上恢复,然后执行shell脚本,使用scan
渐进扫描批量导出key-value。
2 详细步骤
2.1 本地docker启动redis
本地通过docker-compose创建redis实例,并挂载配置文件和数据目录
- docker-compose.yml
version: '3'
services:
redis:
image: redis
container_name: redis
restart: always
command: redis-server /etc/redis/redis.conf
ports:
- 46379:6379
environment:
TZ: Asia/Shanghai
LANG: en_US.UTF-8
volumes:
- ./mnt/conf/redis.conf:/etc/redis/redis.conf:rw
- ./mnt/data:/data:rw
- 在宿主机创建配置文件
指定dump恢复目录及文件,和redis实例密码
mkdir -p ./mnt/conf
vim ./mnt/conf/redis.conf
requirepass GSef7NOoIH5R
dbfilename dump.rdb
dir /data
- 将dump.rdb备份文件放在宿主机
./mnt/conf/data
下,启动redis
docker-compose up -d
- 通过日志查看备份恢复进度
docker logs -f redis
- 验证恢复情况
# 进入容器
docker exec -it redis bash
# 认证
auth GSef7NOoIH5R
# 查询key数量
dbsize
2.2 shell批量导出脚本
- 进入容器
docker exec -it redis bash
- 为便于后续操作,在容器安装vim(也可在宿主机挂载目录创建shell脚本)
# 换源并安装vim
sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list \
&& apt update -y \
&& apt-get install -y vim
# 解决vim中文乱码
echo -e "syntax on \nset termencoding=utf-8 \nset encoding=utf8 \nset fileencodings=utf8,ucs-bom,gbk,cp936,gb2312,gb18030" >> ~/.vimrc
- 创建shell脚本
使用的scan命令渐进遍历,相对于keys命令全量遍历速度慢些,但胜在安全,对redis的负载低。其中:
- 可通过
CNT
参数设定迭代元素的数量来以控制redis负载 - 获取value值时,通过
INTERVAL
调整redis-cli的执行间隔,来控制redis负载
vim redis_export.sh
#!/bin/bash
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=GSef7NOoIH5R
CNT=1000
KEY_NAME=vc_*
KEY_FILE=key_list.txt
VALUE_FILE=value_list.txt
RESULT_FILE=kv_result.txt
INTERVAL=0.01
redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD scan 0 match "$KEY_NAME" count $CNT 2>/dev/null> scan_tmp_result
new_cursor=`sed -n '1p' scan_tmp_result`
sed -n '2,$p' scan_tmp_result > $KEY_FILE
while [ $new_cursor -ne 0 ]
do
redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD scan $new_cursor match "$KEY_NAME" count $CNT 2>/dev/null> scan_tmp_result
new_cursor=`sed -n '1p' scan_tmp_result`
echo `cat $KEY_FILE |wc -l`
sed -n '2,$p' scan_tmp_result >> $KEY_FILE
done
TOTAL=`cat $KEY_FILE |wc -l`
echo $TOTAL
> $VALUE_FILE
i=0
for key in `cat $KEY_FILE`
do
i=$(($i+1))
if [[ ${i}%1000 -eq 0 || ${i} -eq $total ]]; then
echo "$i / $TOTAL"
fi
echo "GET $key" | redis-cli $INTERVAL -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD 2>/dev/null >> $VALUE_FILE
done
paste $KEY_FILE $VALUE_FILE > $RESULT_FILE
rm -f scan_tmp_result $VALUE_FILE $KEY_FILE
- 运行脚本
bash ./redis_export.sh
3 附录
记录下实践时的其它方法/功能:
- scan扫描不指定游标,相比
keys pattern
模式不会长时间阻塞redis。(可通过-i调整执行间隔控制负载)
redis-cli -a GSef7NOoIH5R --scan --pattern "vc_*">/tmp/redis.log
- keys全量扫描(慎用)
echo "KEYS vc_*" | redis-cli -a GSef7NOoIH5R >/tmp/redis.log