当前位置:首页 > 问答 > 正文

Redis缓存虽快但也有坑,聊聊那些让人头疼的缺陷和不足

说到Redis,搞技术的人几乎没人不知道,大家都把它当做法宝,因为它读写速度实在是太快了,能瞬间解决很多性能问题,但老话说的好,“是药三分毒”,Redis用起来爽,可要是只看到它的好处,不了解它的坑,那迟早得栽跟头,这些坑,有时候真能让人头疼好几天。

Redis缓存虽快但也有坑,聊聊那些让人头疼的缺陷和不足

第一个大坑,就是它“记性不好”——数据易失性。 这是Redis最核心的特性,但也最容易让人疏忽,Redis默认是把所有数据都放在内存里的,这样才快,内存一断电,数据就全没了,虽然Redis提供了持久化机制,比如RDB快照和AOF日志,但这俩都不是省油的灯,RDB是隔一段时间拍个照保存,万一在两次拍照之间服务器宕机,那段时间的数据就丢了,AOF是记录 every write operation,数据更安全,但日志文件会越来越大,重启恢复数据的时候能慢到你怀疑人生,你可能会想,那我两个都开不就行了?没错,可以,但这又会牺牲一部分性能,毕竟磁盘IO跟不上内存的速度,你永远要清醒地认识到,Redis里的数据不一定是“铁打的营盘”,它更像一个高速的临时工,不能把它当成最终的数据保险柜,很多新手会把重要的、绝对不能丢的数据,比如订单的支付状态,只放在Redis里,这是非常危险的。

第二个头疼的点,是它“脑子简单”——功能单一。 Redis虽然支持好几种数据结构,像字符串、列表、哈希等等,但它本质上还是个键值存储,你没法用它去做像关系型数据库(比如MySQL)那样复杂的多表关联查询,你想查“所有购买了某款商品且来自北京的女性用户”,这种需求在MySQL里一句SQL就能搞定,但在Redis里,你得自己设计好几套键,可能要取好几次数据,然后在应用程序里自己拼凑逻辑,这就非常考验程序员的数据结构设计能力,设计得不好,非但没提高效率,反而把代码搞得又臭又长,Redis通常是作为数据库的“缓存”或“加速器”来用的,它擅长的是存储一些结构简单、需要快速访问的热点数据,想让它包打天下,那是不现实的。

Redis缓存虽快但也有坑,聊聊那些让人头疼的缺陷和不足

第三个问题,是它“不善交际”——处理复杂事务能力弱。 Redis确实有事务功能,但它的“事务”和数据库的事务完全是两码事,数据库的事务有ACID特性,能保证一堆操作要么全成功,要么全失败,Redis的事务呢,更像一个“批处理”:它把一堆命令打包,然后按顺序执行,但在执行过程中,如果某个命令出错了,它不会回滚已经执行成功的命令,这就很尴尬了,你无法保证严格的原子性,在分布式环境下,当多个客户端同时修改同一个键时,虽然可以用WATCH命令实现一种乐观锁,但用起来比较麻烦,而且在高并发场景下,很容易导致事务执行失败,需要重试,增加了程序的复杂度。

第四个让人纠结的地方,是它的“身材管理”——内存容量限制。 既然数据都放在内存里,那机器的内存有多大,Redis能用的空间就有多大,现在内存虽然便宜了,但和海量的硬盘比起来,还是显得很有限,如果你的业务数据量非常大,比如有几个T,那全塞进Redis就不太现实了,成本太高,虽然Redis有集群模式可以把数据分片到多台机器上,但这又引入了新的复杂度,比如运维成本变高,跨分片操作变得困难等,当内存快用完时,Redis会根据你设定的策略淘汰一些数据,万一不小心把重要的热点数据给淘汰了,又会引起缓存击穿,直接压垮后端的数据库。

第五个是“成长的烦恼”——扩展时的挑战。 当你的业务量增长,一台Redis实例顶不住了,你就得考虑搭建Redis集群,但集群的扩容和缩容可不是点一下按钮就完事的,尤其是在扩容时,需要进行数据迁移,这个过程如果处理不好,很容易导致服务抖动,甚至数据不一致,在集群模式下,有些命令是不支持的,比如那些需要同时操作多个键的命令,因为这几个键很可能不在同一个分片上,这就在一定程度上限制了你的使用方式,需要提前规划好数据的设计。

还有一个容易被忽略的坑:缓存一致性问题。 这是指Redis里的数据和你背后真实数据库(比如MySQL)里的数据不一致了,当你修改了数据库的数据后,你需要同时去更新或者删除Redis里对应的缓存,这个“说起来简单,做起来难,你是在更新数据库之前删缓存,还是之后删?如果删除缓存失败了呢?在高并发场景下,非常容易出现一种情况:线程A把数据库更新了,但还没来得及删缓存,线程B就来读数据,读到的还是旧的缓存数据,为了解决这个问题,业界提出了各种复杂的方案,延时双删”、“订阅数据库binlog”等,每一个方案都挺折腾人的。

所以你看,Redis就像一把无比锋利的双刃剑,它用速度征服了我们,但也因为这些固有的缺陷,要求我们在使用时必须格外小心,不能无脑地用,得想清楚:数据能不能丢?数据结构合不合适?会不会有并发冲突?内存够不够?将来怎么扩展?缓存和数据库怎么保持一致?把这些坑都提前想到了,并做好预案,才能真正让Redis成为你得心应手的利器,而不是半夜把你叫起来处理故障的“坑王”。(内容参考了多位技术博主如why技术、程序员囧辉、石杉的架构笔记等对Redis缺点的讨论,并结合了常见的项目实践经验。)

Redis缓存虽快但也有坑,聊聊那些让人头疼的缺陷和不足