zoukankan      html  css  js  c++  java
  • redis 模糊删除key

    redis-cli KEYS "pattern" | xargs redis-cli DEL 

    Redis keys命令支持模式匹配,但是del命令不支持模式匹配,有时候需要根据一定的模式来模糊删除key,这时只能结合shell命令来完成了。 具体命令是: 

    redis-cli KEYS "pattern" | xargs redis-cli DEL 
    其中pattern是keys命令支持的模式,这样就可以模糊删除key了。服务器上测试删除150万条数据的效率也是很高的。 

    问题是只能删除单机,集群模式下无法模糊删除;

    线上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
    host=$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 $host -p $port 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_keysG
    *************************** 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 ",'$3 ','DEL ','$',length(redis_key),' ',redis_key,' ') 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="33[42;30m"
    lv2="33[0m"
    end="33[0m"
    red="33[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一会,再接着执行,由于各个公司管理平台不同,脚本可能需要修改部分内容,可以根据思路实现自己的一套脚本

  • 相关阅读:
    最终版需求分析
    第九次会议
    第八次会议
    软工测试文档
    软工第七次会议记录
    第六次会议
    软件工程第二次和第三次会议
    Java环境搭建与概述
    [JavaWeb] Ajax&JQuery
    [JavaWeb] JSP
  • 原文地址:https://www.cnblogs.com/zhangshiwen/p/9650620.html
Copyright © 2011-2022 走看看