redis调用lua批量删除key及其相关数据

本文介绍了一种利用Lua脚本在Redis中批量删除特定数据的方法,通过优化数据结构和脚本执行流程,实现了高效的大数据量清理,适用于高并发场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

redis调用脚本批量删除key及其关联数据

情况描述

  项目中需要批量删除redis指定数据,线网数据量很大,考虑到性能,决定用lua脚本实现此功能。
  功能描述:根据输入的lnsName进行批量删除相关数据

redis数据结构

keyvalue(哈希表)
ses_会话IDfiledlns_nameusernamecallingidipent_realmstart_time
valueLNS名称用户名主叫号码IP企业域名会话开始时间
keyvalue(String)
ex_会话ID1(固定)
keyvalue(String)
ipses_会话ID
keyvalue(使用start_time作为分数的SortedSet)
sessionsses_会话ID1,ses_会话ID2,ses_会话ID3…
keyvalue(使用start_time作为分数的SortedSet)
lns_LNS名称ses_会话ID1,ses_会话ID2,ses_会话ID3…
keyvalue(使用start_time作为分数的SortedSet)
ent_企业名称ses_会话ID1,ses_会话ID2,ses_会话ID3…
keyvalue(使用start_time作为分数的SortedSet)
un_用户名ses_会话ID1,ses_会话ID2,ses_会话ID3…
keyvalue(使用start_time作为分数的SortedSet)
cid_主叫号码ses_会话ID1,ses_会话ID2,ses_会话ID3…

lua脚本

--csdn代码块高亮显示没有lua格式,凑活看吧
--选库
redis.call("select",0)

--游标的id
local cursor = 0
--查找删除的key的数量
local keyNum = 0
-- --当前时间
local currentTime = ARGV[1]
-- --过期时间
local expire = ARGV[2]
--删除key所对应的sessionId的数量
local totalNum = 0
--字符串切分函数
local function splitStr(str, delimeter)
    local find, sub, insert = string.find, string.sub, table.insert
    local res = {}
    local start, start_pos, end_pos = 1, 1, 1
    while true do
        start_pos, end_pos = find(str, delimeter, start, true)
        if not start_pos then
            break
        end
        insert(res, sub(str, start, start_pos - 1))
        start = end_pos + 1
    end
    insert(res, sub(str,start))
    return res
end
repeat
    --使用scan搜索,cursor=0的时候标识一个新的迭代期,服务器返回0的时候表示迭代已经结束
    local res = redis.call("scan",cursor,"MATCH",KEYS[1],"COUNT",10000)
    if(res ~= nil and #res>=0) then
        cursor = tonumber(res[1])
        local ks = res[2]
        if(ks ~= nil and #ks>0) then
            redis.replicate_commands()
            --循环删除当前迭代器迭代出的数据
            for i=1,#ks,1 do
                local key = tostring(ks[i])
                --根据查询出来的key,获取value(sessionid)的数量
                local valNum = redis.call("ZCARD",key)
                local allVal = {}
                --根据key分批获取value,每次获取1000for j=1,valNum,1000 do
                    local val = redis.call("ZRANGE",key,j-1,j+999)
                    for k=1,#val,1 do
                        allVal[j+k-1] = val[k]
                    end
                end
                --根据获取的sessionid进行关联删除
                for i=1,valNum,1 do
                    --删除key为ip_*
                    local ip =redis.call("HGET",tostring(allVal[i]),"ip")
                    redis.call("UNLINK","ip_"..ip)
                    --移除sessions
                    redis.call("ZREM","sessions",tostring(allVal[i]))
                    --移除key为ent_*对应的数据
                    local entRealm = redis.call("HGET",tostring(allVal[i]),"ent_realm")
                    redis.call("ZREM","ent_"..entRealm,tostring(allVal[i]))
                    -- 移除key为un_*对应的数据
                    local username = redis.call("HGET",tostring(allVal[i]),"username")
                    redis.call("ZREM","un_"..username,tostring(allVal[i]))
                    --移除key为cid_*对应的数据
                    local callingId = redis.call("HGET",tostring(allVal[i]),"callingid")
                    redis.call("ZREM","cid_"..callingId,tostring(allVal[i]))
                    --删除过期时间的key
                    --获取sessionid(不带ses_开头的形式)
                    local sessionid = splitStr(tostring(allVal[i]),'_')
                    redis.call("UNLINK","ex_"..sessionid[2])
                    --删除key"ses_"
                    redis.call("UNLINK",tostring(allVal[i]))
                    -- --记录下线数量
                    redis.call("INCR","offline_"..tostring(currentTime))
                    -- --设置过期时间
                    redis.call("EXPIRE","offline_"..tostring(currentTime),expire)
                end

                --使用UNLINK删除,区别于del的是这个是异步执行的
                --这条指令要版本大于4.0.0 小于4.0.0就使用del
                redis.call("UNLINK",key)
                --统计删除的key的数量
                keyNum = keyNum + #ks
                --统计删除的sessionId数量
                totalNum =totalNum+valNum
            end
        end
    end
--当服务器返回0的时候,跳出循环
until( cursor <= 0 )

return totalNum

  脚本中共接收3个参数,依次是要删除的key当前时间(转换成分钟,例如202003171614)过期时间(秒)

执行脚本

  如果直接在redis服务器执行,执行命令如下:

./redis/bin/redis-cli -h 172.17.0.225 -p 6380  --eval test.lua "lns_LNS100" , 202003171019 190

  其中,./redis/bin/redis-cli -h 172.17.0.225 -p 6380为redis连接方式,test.lua为脚本路径,后面的则为对应参数。

Java中执行

  由于项目是Java项目,同时使用了StringRedisTemplate,下面是Java中的实现方法,后期调用即可。

 public   Object runLuaScript(String scriptFile, List<String> keys, String currentTime, String expireTime)
    {
        Resource resource = new ClassPathResource(scriptFile);
        ResourceScriptSource scriptSource = new ResourceScriptSource(resource);
        DefaultRedisScript script = new DefaultRedisScript();
        script.setScriptSource(scriptSource);
        script.setResultType(Long.class);
        return stringRedisTemplate.execute(script, keys,currentTime,expireTime);
    }

  亲测100w数据时删除时间为1分钟左右,在现网环境下,大批量删除基本满足条件,当然后续会用redis主从和哨兵机制。总体来说用脚本实现效率是非常高效的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值