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一会,再接着执行,由于各个公司管理平台不同,脚本可能需要修改部分内容,可以根据思路实现自己的一套脚本

  • 相关阅读:
    UVA 1025 A Spy in the Metro DP水题
    ZOJ 3814 Sawtooth Puzzle BFS
    ZOJ 3816 Generalized Palindromic Number
    UVA 10859 Placing Lampposts 树形DP
    UVA 11825 Hackers' Crackdown 状压DP
    POJ 2887 Big String 线段树 离线处理
    POJ 1635 Subway tree systems Hash法判断有根树是否同构
    BZOJ 3110 k大数查询 & 树套树
    sdoi 2009 & 状态压缩
    来自于2016.2.24的flag
  • 原文地址:https://www.cnblogs.com/zhangshiwen/p/9650620.html
Copyright © 2011-2022 走看看