redis cluster超大批量刪除keys
阿新 • • 發佈:2018-12-25
【需求】
線上redis cluster需要刪除幾百萬的keys,要刪除keys的字首是usertags_uid_*
【解決方案】
第一種方式:
首先通過scan在三個主節點(假設叢集是三主三從)掃描出匹配字首的keys
redis-cli -c -h $host1 -p $port1 --scan --pattern "usertags_uid_*" > /tmp/node1.log
redis-cli -c -h $host2 -p $port2 --scan --pattern "usertags_uid_*" > /tmp/node2.log
redis-cli -c -h $host3 -p $port3 --scan --pattern "usertags_uid_*" > /tmp/node3.log
然後寫個簡單指令碼進行刪除,比如
#!/bin/bash
ip=$1
port=$2
file=$3
if [ $# -ne 3 ];then
echo "Usage: $0 ip port file"
exit
fi
cat $file|while read line
do
redis-cli -c -h $host1 -p $port1 del $line #叢集裡任意一個節點即可
done
最後呼叫指令碼刪除
sh del_redis_keys.sh $host1 $port1 node1.log
第二種方式:
使用redis的pipeline進行刪除
首先通過scan掃描出匹配字首的keys
redis-cli -c -h $host1 -p $port1 --scan --pattern "usertags_uid_*" > /tmp/node1.log
redis-cli -c -h $host2 -p $port2 --scan --pattern "usertags_uid_*" > /tmp/node2.log
redis-cli -c -h $host3 -p $port3 --scan --pattern "usertags_uid_*" > /tmp/node3.log
然後把這些keys匯入mysql中
最後利用redis協議刪除
第一種方式效率很低,一秒只能刪除大概200到300個keys
第二種方式效率很高,可以達到redis的極限
兩種刪除方式我們線上都在用,為了方便使用都寫成了指令碼,第一種方式適合keys少的情況,第二種方式適合keys多的情況
【具體實現】
1、建立表
mysql> show create table del_redis_keys\G
*************************** 1. row ***************************
Table: del_redis_keys
Create Table: CREATE TABLE `del_redis_keys` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`keyname` varchar(60) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_kn` (`keyname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
2、編輯SQL檔案
cat del_redis_keys.sql
select concat("*2\r\n",'$3\r\n','DEL\r\n','$',length(redis_key),'\r\n',redis_key,'\r') from (select concat('',keyname) as redis_key from del_redis_keys) as t;
3、指令碼
#!/bin/bash
source /etc/profile
rc_member=$1
key_pattern_name=$2
time=`date "+%m%d%H%M"`
manage_db_user="user"
manage_db_pass="password"
manage_db_host="10.10.64.100"
manage_db_port="3306"
manage_config_db="DB_M"
manage_config_tb="tb_redis_node"
base_dir=$(cd `dirname $0`; pwd)
del_redis_keys_sql="${base_dir}/del_redis_keys.sql"
scan_keys_dir="${base_dir}/logs/group_${rc_member}_${time}"
error_log="/tmp/del_redis_keys.log"
error_log2="/tmp/del_redis_keys.log2"
redis_cli="/server/redis_cluster/src/redis-cli"
mysql_server="/server/mysql/bin/mysql"
lv1="\033[42;30m"
lv2="\033[0m"
end="\033[0m"
red="\033[45;37m"
if [ $# -ne 2 ];then
echo "Usage: $0 rc_member key_pattern"
exit
fi
if ! [ -f "$del_redis_keys_sql" ];then
echo -e "${red}${del_redis_keys_sql}檔案不存在,請檢查${end}"
exit
fi
if ! [ -f "$redis_cli" ];then
echo -e "${red}${redis_cli}不存在,請檢查${end}"
exit
fi
if ! [ -f "$mysql_server" ];then
echo -e "${red}${mysql_server}不存在,請檢查${end}"
exit
fi
if ! [ -d "$scan_keys_dir" ];then
mkdir -p $scan_keys_dir
fi
echo "keys所在目錄: $scan_keys_dir"
sum=0
check_key=0
redis_node_list=`$mysql_server -u $manage_db_user -p$manage_db_pass -h $manage_db_host -P $manage_db_port $manage_config_db -N -e "select concat_ws('|',hostip,port) from $manage_config_tb where type=1 and status=1 and rc_member=$rc_member;"` #線上將status修改為1
for redis_info in $redis_node_list
do
host=`echo $redis_info | awk -F"|" '{print $1}'`
port=`echo $redis_info | awk -F"|" '{print $2}'`
befer_del_cnt=`$redis_cli -c -h $host -p $port info Keyspace|grep -v Keyspace|awk -F, '{print $1}'|awk -F: '{print $2}'|awk -F= '{print $2}'`
#redis匯出要刪除的keys
$redis_cli -c -h $host -p $port --scan --pattern "*${key_pattern_name}*" > $scan_keys_dir/${host}_${port}.log
key_num=`wc -l $scan_keys_dir/${host}_${port}.log|awk '{print $1}'`
sum=$[$key_num+$sum]
echo -e "${lv1}$host:$port${lv2}"
#清空del_redis_keys表
$mysql_server -u $manage_db_user -p$manage_db_pass -h $manage_db_host -P $manage_db_port $manage_config_db -N -e "truncate table del_redis_keys;"
#匯入要刪除的keys
$mysql_server -u $manage_db_user -p$manage_db_pass -h $manage_db_host -P $manage_db_port $manage_config_db --local-infile=1 --show-warnings -N -e "load data local infile '$scan_keys_dir/${host}_${port}.log' into table del_redis_keys (keyname);" > $error_log
#判斷匯出的keys和匯入的keys數目是否相等
mysql_keys_num=`$mysql_server -u $manage_db_user -p$manage_db_pass -h $manage_db_host -P $manage_db_port $manage_config_db -N -e "select count(1) from del_redis_keys;"`
if [ $key_num -ne $mysql_keys_num ];then
echo -e "${red}匯入資料庫的keys數目不正確${end}"
exit
fi
#檢驗匯入時是否有warning,如果有退出並提示
if [ -s "$error_log" ];then
warning_cnt=`grep -o Warning $error_log|wc -l|awk '{print $1}'`
if [ $warning_cnt -ge 1 ];then
echo -e "${red}匯入資料報錯,請看$error_log${end}"
exit
fi
fi
#檢查keys是否正確,只在第一個節點檢查,後面節點不再重複檢查
if [ $check_key -eq 0 ];then
echo "請檢查資料庫裡要刪除的keys是否正確"
echo "$mysql_server -u $manage_db_user -p$manage_db_pass -h $manage_db_host -P $manage_db_port $manage_config_db -e 'select * from del_redis_keys limit 20;'"
#echo "select * from del_redis_keys limit 50;"
while true; do
read -p "檢查完keys之後選擇繼續或退出(yes|no): " yn
case $yn in
yes ) break;;
no ) exit;;
* ) echo "Please input yes or no";;
esac
done
while true; do
read -p "確認繼續嗎(yes|no): " yn
case $yn in
yes ) break;;
no ) exit;;
* ) echo "Please input yes or no";;
esac
done
fi
check_key=$[check_key+1]
#刪除keys
$mysql_server --raw --skip-column-names --default-character-set utf8 -u $manage_db_user -p$manage_db_pass -h $manage_db_host -P $manage_db_port $manage_config_db -A < ${del_redis_keys_sql} | $redis_cli -h $host -p $port --pipe >> $error_log2 2>&1
#列印
after_del_cnt=`$redis_cli -c -h $host -p $port info Keyspace|grep -v Keyspace|awk -F, '{print $1}'|awk -F: '{print $2}'|awk -F= '{print $2}'`
echo "刪除之前keys數目是${befer_del_cnt}"
echo "刪除之後keys數目是${after_del_cnt}"
echo "刪除keys數目是${key_num}"
done
echo "刪除的keys總數是$sum"
可以根據實際需求,在腳本里新增sleep,刪除一部分sleep一會,再接著執行,由於各個公司管理平臺不同,指令碼可能需要修改部分內容,可以根據思路實現自己的一套指令碼
參考文章
http://blog.csdn.net/mengxianhua/article/details/51076165
線上redis cluster需要刪除幾百萬的keys,要刪除keys的字首是usertags_uid_*
【解決方案】
第一種方式:
首先通過scan在三個主節點(假設叢集是三主三從)掃描出匹配字首的keys
redis-cli -c -h $host1 -p $port1 --scan --pattern "usertags_uid_*" > /tmp/node1.log
redis-cli -c -h $host2 -p $port2 --scan --pattern "usertags_uid_*" > /tmp/node2.log
redis-cli -c -h $host3 -p $port3 --scan --pattern "usertags_uid_*" > /tmp/node3.log
然後寫個簡單指令碼進行刪除,比如
#!/bin/bash
ip=$1
port=$2
file=$3
if [ $# -ne 3 ];then
echo "Usage: $0 ip port file"
exit
fi
cat $file|while read line
do
redis-cli -c -h $host1 -p $port1 del $line #叢集裡任意一個節點即可
done
最後呼叫指令碼刪除
sh del_redis_keys.sh $host1 $port1 node1.log
第二種方式:
使用redis的pipeline進行刪除
首先通過scan掃描出匹配字首的keys
redis-cli -c -h $host1 -p $port1 --scan --pattern "usertags_uid_*" > /tmp/node1.log
redis-cli -c -h $host2 -p $port2 --scan --pattern "usertags_uid_*" > /tmp/node2.log
redis-cli -c -h $host3 -p $port3 --scan --pattern "usertags_uid_*" > /tmp/node3.log
然後把這些keys匯入mysql中
最後利用redis協議刪除
第一種方式效率很低,一秒只能刪除大概200到300個keys
第二種方式效率很高,可以達到redis的極限
兩種刪除方式我們線上都在用,為了方便使用都寫成了指令碼,第一種方式適合keys少的情況,第二種方式適合keys多的情況
【具體實現】
1、建立表
mysql> show create table del_redis_keys\G
*************************** 1. row ***************************
Table: del_redis_keys
Create Table: CREATE TABLE `del_redis_keys` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`keyname` varchar(60) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_kn` (`keyname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
2、編輯SQL檔案
cat del_redis_keys.sql
select concat("*2\r\n",'$3\r\n','DEL\r\n','$',length(redis_key),'\r\n',redis_key,'\r') from (select concat('',keyname) as redis_key from del_redis_keys) as t;
3、指令碼
#!/bin/bash
source /etc/profile
rc_member=$1
key_pattern_name=$2
time=`date "+%m%d%H%M"`
manage_db_user="user"
manage_db_pass="password"
manage_db_host="10.10.64.100"
manage_db_port="3306"
manage_config_db="DB_M"
manage_config_tb="tb_redis_node"
base_dir=$(cd `dirname $0`; pwd)
del_redis_keys_sql="${base_dir}/del_redis_keys.sql"
scan_keys_dir="${base_dir}/logs/group_${rc_member}_${time}"
error_log="/tmp/del_redis_keys.log"
error_log2="/tmp/del_redis_keys.log2"
redis_cli="/server/redis_cluster/src/redis-cli"
mysql_server="/server/mysql/bin/mysql"
lv1="\033[42;30m"
lv2="\033[0m"
end="\033[0m"
red="\033[45;37m"
if [ $# -ne 2 ];then
echo "Usage: $0 rc_member key_pattern"
exit
fi
if ! [ -f "$del_redis_keys_sql" ];then
echo -e "${red}${del_redis_keys_sql}檔案不存在,請檢查${end}"
exit
fi
if ! [ -f "$redis_cli" ];then
echo -e "${red}${redis_cli}不存在,請檢查${end}"
exit
fi
if ! [ -f "$mysql_server" ];then
echo -e "${red}${mysql_server}不存在,請檢查${end}"
exit
fi
if ! [ -d "$scan_keys_dir" ];then
mkdir -p $scan_keys_dir
fi
echo "keys所在目錄: $scan_keys_dir"
sum=0
check_key=0
redis_node_list=`$mysql_server -u $manage_db_user -p$manage_db_pass -h $manage_db_host -P $manage_db_port $manage_config_db -N -e "select concat_ws('|',hostip,port) from $manage_config_tb where type=1 and status=1 and rc_member=$rc_member;"` #線上將status修改為1
for redis_info in $redis_node_list
do
host=`echo $redis_info | awk -F"|" '{print $1}'`
port=`echo $redis_info | awk -F"|" '{print $2}'`
befer_del_cnt=`$redis_cli -c -h $host -p $port info Keyspace|grep -v Keyspace|awk -F, '{print $1}'|awk -F: '{print $2}'|awk -F= '{print $2}'`
#redis匯出要刪除的keys
$redis_cli -c -h $host -p $port --scan --pattern "*${key_pattern_name}*" > $scan_keys_dir/${host}_${port}.log
key_num=`wc -l $scan_keys_dir/${host}_${port}.log|awk '{print $1}'`
sum=$[$key_num+$sum]
echo -e "${lv1}$host:$port${lv2}"
#清空del_redis_keys表
$mysql_server -u $manage_db_user -p$manage_db_pass -h $manage_db_host -P $manage_db_port $manage_config_db -N -e "truncate table del_redis_keys;"
#匯入要刪除的keys
$mysql_server -u $manage_db_user -p$manage_db_pass -h $manage_db_host -P $manage_db_port $manage_config_db --local-infile=1 --show-warnings -N -e "load data local infile '$scan_keys_dir/${host}_${port}.log' into table del_redis_keys (keyname);" > $error_log
#判斷匯出的keys和匯入的keys數目是否相等
mysql_keys_num=`$mysql_server -u $manage_db_user -p$manage_db_pass -h $manage_db_host -P $manage_db_port $manage_config_db -N -e "select count(1) from del_redis_keys;"`
if [ $key_num -ne $mysql_keys_num ];then
echo -e "${red}匯入資料庫的keys數目不正確${end}"
exit
fi
#檢驗匯入時是否有warning,如果有退出並提示
if [ -s "$error_log" ];then
warning_cnt=`grep -o Warning $error_log|wc -l|awk '{print $1}'`
if [ $warning_cnt -ge 1 ];then
echo -e "${red}匯入資料報錯,請看$error_log${end}"
exit
fi
fi
#檢查keys是否正確,只在第一個節點檢查,後面節點不再重複檢查
if [ $check_key -eq 0 ];then
echo "請檢查資料庫裡要刪除的keys是否正確"
echo "$mysql_server -u $manage_db_user -p$manage_db_pass -h $manage_db_host -P $manage_db_port $manage_config_db -e 'select * from del_redis_keys limit 20;'"
#echo "select * from del_redis_keys limit 50;"
while true; do
read -p "檢查完keys之後選擇繼續或退出(yes|no): " yn
case $yn in
yes ) break;;
no ) exit;;
* ) echo "Please input yes or no";;
esac
done
while true; do
read -p "確認繼續嗎(yes|no): " yn
case $yn in
yes ) break;;
no ) exit;;
* ) echo "Please input yes or no";;
esac
done
fi
check_key=$[check_key+1]
#刪除keys
$mysql_server --raw --skip-column-names --default-character-set utf8 -u $manage_db_user -p$manage_db_pass -h $manage_db_host -P $manage_db_port $manage_config_db -A < ${del_redis_keys_sql} | $redis_cli -h $host -p $port --pipe >> $error_log2 2>&1
#列印
after_del_cnt=`$redis_cli -c -h $host -p $port info Keyspace|grep -v Keyspace|awk -F, '{print $1}'|awk -F: '{print $2}'|awk -F= '{print $2}'`
echo "刪除之前keys數目是${befer_del_cnt}"
echo "刪除之後keys數目是${after_del_cnt}"
echo "刪除keys數目是${key_num}"
done
echo "刪除的keys總數是$sum"
可以根據實際需求,在腳本里新增sleep,刪除一部分sleep一會,再接著執行,由於各個公司管理平臺不同,指令碼可能需要修改部分內容,可以根據思路實現自己的一套指令碼
參考文章
http://blog.csdn.net/mengxianhua/article/details/51076165