一、背景

    在搭建了项目之后,由于需要通过触发动作,并删除redis中多个key

二、思路

     在查询了jedis并没有类似的删除方法之后,事情就变得清晰起来。完成上述任务,分为两个步骤:

第一,找到要删除的key;
第二,删除掉他们。

三、 解决方法

     从找到要删除的key来说,有两种方法,一种是通过jedis的keys方法来获得;另外一种是通过scan方法获得。

1. keys

     使用keys方法,寻找包含指定参数的key,其中“*”是匹配符,要是想找前缀就在其后加*,要是找后缀就在传入参数后面加*,而要是图省事方便前后加*即可

1

2

3

4

5

6

7

8

9

10

public Set<String> queryKeys(String key) {

        Jedis jedis = jedisPool.getResource();

        StringBuilder paramKey = new StringBuilder("*").append(key).append("*");

        Set<String> keys = jedis.keys(paramKey.toString());

        return keys;

    }

2. scan

使用scan方法,寻找包含指定参数的key,其中 
第一,需要构建ScanParams(redis.clients.jedis.ScanParams);
第二,主要是填充两个参数第一个是match,标明需要匹配的字符串,第二个是count标明要扫描出多少个来,我这里写的是1000,因为目前库中keys远小于1000,相当于全找出来;
第三,jedis中的部分scan方法是@Deprecated的不建议使用。

   在获得结果集之后,转换出来即可

1

2

3

4

5

6

7

8

9

10

11

12

13

public List<String> queryKeys(String key) {

        Jedis jedis = jedisPool.getResource();

        // 存入键值对

        ScanParams scanParams = new ScanParams();

        StringBuilder paramKey = new StringBuilder("*").append(key).append("*");

        scanParams.match(paramKey.toString());

        scanParams.count(1000);

        ScanResult<String> sr = jedis.scan("0", scanParams);

        List<String> a = sr.getResult();

        return a;

    }

因为,大家都知道keys的方法会阻塞单线程的redis。
在keys少的时候,无所谓,但是在keys多的时候,这个就会是很大的隐患。我们采用redis 就是为了其查询速度快。所以,决定采取后者scan的方法实现。

 1 public void delStrings(String key) {
 2 
 3         try {
 4             Jedis jedis = jedisPool.getResource();
 5             // 存入键值对
 6             ScanParams scanParams = new ScanParams();
 7             StringBuilder paramKey = new StringBuilder("*").append(key).append("*");
 8             scanParams.match(paramKey.toString());
 9             scanParams.count(1000);
10             ScanResult<String> sr = jedis.scan("0", scanParams);
11             List<String> a = sr.getResult();
12             for (String delkey : a) { //循环一个一个的删除
13                 jedis.del(delkey);
14             }
15             jedis.close();
16         } catch (Exception e) {
17             // TODO: handle exception
18         }
19     }

四、MAVEN的依赖

1

2

3

4

5

       <dependency>

    <groupId>redis.clients</groupId>

    <artifactId>jedis</artifactId>

    <version>2.9.0</version>

</dependency>

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
jedis方式,到此结束

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

五、扩展——springboot中redisTemplate中使用lua脚本

RedisTemplate 已经封装了关于 set/hash/sorted set 的scan

redisTemplate.opsForHash().scan();
redisTemplate.opsForSet().scan();
redisTemplate.opsForZSet().scan();

下面介绍使用scan配合lua脚本进行删除。

① lua 脚本

local function scan(key)
    local cursor = 0
    local keynum = 0

    repeat
        local res = redis.call("scan", cursor, "match", key,'COUNT',ARGV[1])

        if (res ~= nil and #res >= 0) then
            redis.replicate_commands()
            cursor = tonumber(res[1])
            local ks = res[2]
            keynum = #ks
            for i=1,keynum,1 do
                local k = tostring(ks[i])
                redis.call("del", k)
            end
        end
    until (cursor <= 0)

    return keynum
end

local a = #KEYS
local b = 1
local total = 0
while (b <= a)
do
    total = total + scan(KEYS[b])
    b = b + 1
end

return total

1、key [key ...]: 表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。

2、arg [arg ...]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。

② springboot 中使用RedisTemplate执行脚本,测试通过

String del = "local function scan(key)\n" +
                "    local cursor = 0\n" +
                "    local keynum = 0\n" +
                "\n" +
                "    repeat\n" +
                "        local res = redis.call(\"scan\", cursor, \"match\", key,'COUNT',ARGV[1])\n" +
                "\n" +
                "        if (res ~= nil and #res >= 0) then\n" +
                "            redis.replicate_commands()\n" +
                "            cursor = tonumber(res[1])\n" +
                "            local ks = res[2]\n" +
                "            keynum = #ks\n" +
                "            for i=1,keynum,1 do\n" +
                "                local k = tostring(ks[i])\n" +
                "                redis.call(\"del\", k)\n" +
                "            end\n" +
                "        end\n" +
                "    until (cursor <= 0)\n" +
                "\n" +
                "    return keynum\n" +
                "end\n" +
                "\n" +
                "local a = #KEYS\n" +
                "local b = 1\n" +
                "local total = 0\n" +
                "while (b <= a)\n" +
                "do\n" +
                "    total = total + scan(KEYS[b])\n" +
                "    b = b + 1\n" +
                "end\n" +
                "\n" +
                "return total";
        System.out.println(del);
        //KEY[1]  为要删除的前缀  举例为 Test.* 代表以Test.开头的所有Key
        //ARGV[1] 单次遍历的数量 注意 不是返回的数量,举例 服务器总共Key有10万  设置为20000则需要循环5次才能全部遍历完

        // 指定 lua 脚本,并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(del, Long.class);
        // 参数一:redisScript,参数二:key列表,参数三:arg(可多个)
        Long result = (Long) redisTemplate.execute(redisScript, Collections.singletonList("myServiceCache*"),1000);
        System.out.println(result);

附加:

redis 中执行 使用eval命令执行lua脚本

1、redis-cli命令 连接redis
2、使用eval命令

127.0.0.1:6379> eval "local function scan(key)    local cursor = 0    local keynum = 0    repeat        local res = redis.call("scan", cursor, "match", key,'COUNT',ARGV[1])        if (res ~= nil and #res >= 0) then            redis.replicate_commands()            cursor = tonumber(res[1])            local ks = res[2]            keynum = #ks            for i=1,keynum,1 do                local k = tostring(ks[i])                redis.call("del", k)            end        end    until (cursor <= 0)    return keynum end  local a = #KEYS local b = 1 local total = 0 while (b <= a) do    total = total + scan(KEYS[b])    b = b + 1 end return total" 1 loadU* , 1000
Invalid argument(s)
127.0.0.1:6379> 

3、但是出现报错,带解决。有了解的大神,还望不吝赐教。


链接:http://events.jianshu.io/p/5a95a8209e5b

转载自:

在spring boot上基于maven使用redis——批量匹配并删除 (二) - 幻xiang - 博客园

Redis:使用scan配合lua脚本进行删除key - 简书

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐