需求:工作中需要导出线上redis数据,但需避免使用keys命令全量扫描,导致瞬间响应卡顿,从而引发超时等问题
方法:最安全的方式是通过dump.rdb备份文件,在本地redis实例上恢复,然后执行shell脚本,使用scan渐进扫描批量导出key-value。
本地通过docker-compose创建redis实例,并挂载配置文件和数据目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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实例密码
1 2 |
mkdir -p ./mnt/conf vim ./mnt/conf/redis.conf |
1 2 3 |
requirepass GSef7NOoIH5R dbfilename dump.rdb dir /data |
1 |
docker-compose up -d |
1 |
docker logs -f redis |
1 2 3 4 5 6 7 8 |
# 进入容器 docker exec -it redis bash
# 认证 auth GSef7NOoIH5R
# 查询key数量 dbsize |
1 |
docker exec -it redis bash |
1 2 3 4 5 6 7 |
# 换源并安装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 |
使用的scan命令渐进遍历,相对于keys命令全量遍历速度慢些,但胜在安全,对redis的负载低。其中:
1 |
vim redis_export.sh |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#!/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 |
1 |
bash ./redis_export.sh |
记录下实践时的其它方法/功能:
1 |
redis-cli -a GSef7NOoIH5R --scan --pattern "vc_*">/tmp/redis.log |
1 |
echo "KEYS vc_*" | redis-cli -a GSef7NOoIH5R >/tmp/redis.log |