微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

颠覆你的认知——Redis会遇到的15个「坑」,你踩过几个?

这篇文章,我想和你聊一聊在使用 Redis 时,可能会踩到的「坑」。

如果你在使用 Redis 时,也遇到过以下这些「诡异」的场景,那很大概率是踩到「坑」了:

  • 明明一个 key 设置了过期时间,怎么变成不过期了?

  • 使用 O(1) 复杂度的 SETBIT 命令,Redis 竟然被 OOM 了?

  • 执行 RANDOMKEY 随机拿出一个 key,竟然也会阻塞 Redis

  • 同样的命令,为什么主库查不到数据,从库却可以查到?

  • 从库内存为什么比主库用得还多?

  • 写入到 Redis 的数据,为什么莫名其妙丢了?

究竟是什么原因,导致的这些问题呢?

这篇文章,我就来和你盘点一下,使用 Redis 时可能会踩到「坑」,以及如何去规避。

我把这些问题划分成了三大部分:

  1. 常见命令有哪些坑?

  2. 数据持久化有哪些坑?

  3. 主从库同步有哪些坑?

导致这些问题的原因,很有可能会「颠覆」你的认知,如果你准备好了,那就跟着我的思路开始吧!

这篇文章干货很多,希望你可以耐心读完。

常见命令有哪些坑?

首先,我们来看一下,平时在使用 Redis 时,有哪些常见的命令会遇到「意料之外」的结果。

1) 过期时间意外丢失?

你在使用 Redis 时,肯定经常使用 SET 命令,它非常简单。

SET 除了可以设置 key-value 之外,还可以设置 key 的过期时间,就像下面这样:

127.0.0.1:6379> SET testkey val1 EX 60  
OK  
127.0.0.1:6379> TTL testkey  
(integer) 59  

此时如果你想修改 key 的值,但只是单纯地使用 SET 命令,而没有加上「过期时间」的参数,那这个 key 的过期时间将会被「擦除」。

127.0.0.1:6379> SET testkey val2  
OK  
127.0.0.1:6379> TTL testkey  // key永远不过期了!  
(integer) -1  

看到了么?testkey 变成永远不过期了!

如果你刚刚开始使用 Redis,相信你肯定也踩过这个坑。

导致这个问题的原因在于:SET 命令如果不设置过期时间,那么 Redis自动「擦除」这个 key 的过期时间。

如果你发现 Redis 的内存持续增长,而且很多 key 原来设置了过期时间,后来发现过期时间丢失了,很有可能是因为这个原因导致的。

这时你的 Redis 中就会存在大量不过期的 key,消耗过多的内存资源。

所以,你在使用 SET 命令时,如果刚开始就设置了过期时间,那么之后修改这个 key,也务必要加上过期时间的参数,避免过期时间丢失问题。

2) DEL 竟然也会阻塞 Redis

删除一个 key,你肯定会用 DEL 命令,不知道你没有思考过它的时间复杂度是多少?

O(1)?其实不一定。

如果你有认真阅读 Redis 的官方文档,就会发现:删除一个 key 的耗时,与这个 key 的类型有关。

Redis 官方文档在介绍 DEL 命令时,是这样描述的:

  • key 是 String 类型,DEL 时间复杂度是 O(1)

  • key 是 List/Hash/Set/ZSet 类型,DEL 时间复杂度是 O(M),M 为元素数量

也就是说,如果你要删除的是一个非 String 类型的 key,这个 key 的元素越多,那么在执行 DEL 时耗时就越久!

为什么会这样?

原因在于,删除这种 key 时,Redis 需要依次释放每个元素的内存,元素越多,这个过程就会越耗时。

而这么长的操作耗时,势必会阻塞整个 Redis 实例,影响 Redis性能

所以,当你在删除 List/Hash/Set/ZSet 类型的 key 时,一定要格外注意,不能无脑执行 DEL,而是应该用以下方式删除

  1. 查询元素数量:执行 LLEN/HLEN/SCARD/ZCARD 命令

  2. 判断元素数量:如果元素数量较少,可直接执行 DEL 删除,否则分批删除

  3. 分批删除:执行 LRANGE/HSCAN/SSCAN/ZSCAN + LPOP/RPOP/HDEL/SREM/ZREM 删除

了解了 DEL 对于 List/Hash/Set/ZSet 类型数据的影响,我们再来分析下,删除一个 String 类型的 key 会不会有这种问题?

如果你觉得自己学习效率低,缺乏正确的指导,可以加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧!
[Java架构群]
群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的JAVA交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

啊?前面不是提到,Redis 官方文档的描述,删除 String 类型的 key,时间复杂度是 O(1) 么?这不会导致 Redis 阻塞吧?

其实这也不一定!

你思考一下,如果这个 key 占用的内存非常大呢?

例如,这个 key 存储了 500MB 的数据(很明显,它是一个 bigkey),那在执行 DEL 时,耗时依旧会变长!

这是因为,Redis 释放这么大的内存给操作系统,也是需要时间的,所以操作耗时也会变长。

所以,对于 String 类型来说,你最好也不要存储过大的数据,否则在删除它时,也会有性能问题。

此时,你可能会想:Redis 4.0 不是推出了 lazy-free 机制么?打开这个机制,释放内存的操作会放到后台线程中执行,那是不是就不会阻塞主线程了?

这个问题非常好。

真的会是这样吗?

这里我先告诉你结论:即使 Redis 打开了 lazy-free,在删除一个 String 类型的 bigkey 时,它仍旧是在主线程中处理,而不是放到后台线程中执行。所以,依旧有阻塞 Redis 的风险!

这是为什么?

这里先卖一个关子,感兴趣的同学可以先自行查阅 lazy-free 相关资料寻找答案。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐