因为历史原因,在某个同步菜单操作的方法中先清除缓存,然后在初始化缓存。本来很正常的逻辑,但是这个清除是db查询获取所有的菜单 然后循环一条条删除 然后在db查询有效的菜单操作 在循环一条条插进去 经统计这个菜单操作大概有个7千个 执行 耗时过久 大概50s -60s 不等
因为一些体验问题 也自然而然 想到优化
第一种 使用并行 插入或者删除
使用到stream的parallelStream 来并行执行 由于redis本身的单线程执行限制 时间来到了 10-15秒左右 体验效果还不是很好
第二种 使用pipeline 来批量执行命令
由于并行执行 提升的效果有限,我们换个思路来解决问题,减少与redis的交互 将命令批量执行 这样就会大大减少执行耗时 时间来到了 1- 2秒这个优化效果还是比较理想的 但是也发现了新的问题
虽然执行效果很快 但是在初始化缓存的时候 发现并没有成功初始化缓存
先看下 示例代码
这代码看着 好像也没啥问题 批量执行50个key的set 以及expire 操作
最后获取pipeline所有命令的执行结果
期间以为和使用pipelined的set方法 String 入参有关 于是更换为支持byte的方法 未果
后续还以为使用用法不对,经查询多方资料后 发现用法没问题
省略其他的尝试步骤。。。。。
最后将把expire 的设置注释掉 果然可以了
最后问题定位到 是因为 pipelined1.expire(key,-1) 命令执行导致数据无法存入redis
这个命令看似很正常 想法是设置一个 -1 来表示这个缓存无过期时间 但实际上 好像并没有生效
查看源码后 并无没有什么特殊操作
由于未使用 long类型的时间 ,默认调用时间类为 int类型的方法 最后实际上调用的还是 long类型的时间方法
再往下就直接设置命令了
于是找到redis client 执行了命令 发现也很快失效
于是猜测 -1 这个过期时间会被设置 可能失效时间很短 有可能是 1毫秒 或者1 毫秒
带着问题 去找了下官方文档 看到这样一句描述
好像只写到了 会将过期时间戳存储为 绝对值 至于传入的时间 为负数 该如何处理并未说明
那就再来看下源码的逻辑
过期命令的实现类在 https://github.com/redis/redis/blob/unstable/src/expire.c
看了代码后 思路也清晰了, 这个设置时间过期的逻辑 我们简单梳理下
这个当执行过期时间命令时,我们会传入 key 以及 过期时间(单位秒 或者 毫秒值) 以及 flag 参数 例如 nx xx 等等
核心的逻辑
- 判断时间参数
- 计算过期时间 = 当前时间戳 + 传入的过期时间参数 (单位秒/毫秒)
- 执行 flag参数 逻辑
- 执行 checkAlreadyExpired 判断时间是否已经过期 只有在 只有在非加载数据和非从实例的情况下,当 when 小于等于当前时间戳时,checkAlreadyExpired 函数才会返回 true 就会走到删除key的逻辑 并返回
- 没过期则进行设置新的过期时间 并返回
回到我们的执行操作中,我们执行expire 命令传入的时间参数为-1, 那过期时间就设置为当前时间戳 - 1000 。最后又因为设置的过期时间满足过期条件 (when 小于等于当前时间戳 非加载数据和非从实例),所以我们key 立刻会被删除 。这就导致了虽然我们方法执行完成,但是缓存却没有。
当需要设置一个没有过期时间的key的话 无需要调用expire方法 因为默认没有设置过期时间的话 就是永久不失效
参考官方文档: 官方文档地址: https://redis.io/docs/latest/commands/expire/
good day !!!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ri-ji/67096.html