Redis7.0 核心特性简介
——杨艳杰(烟圈)
阿里云数据库产品事业部 产品经理
一、Redis大版本历史核心特性回顾
Redis自 2009 年诞生以来,已经走过了 13 年。在这漫长的 13 年中,Redis 从小小的开源项目逐步演变成为当今最受欢迎的内存数据库之一,被用于多种场景,帮助解决很多问题。
Redis始终保持着 1-2 年发布大版本的迭代速度,而每个历史大版本都有一些重要的核心特性出现。比如 Redis 3.0 中的 cluster 解决了 Redis 的单机瓶颈,将其从主从架构变成了分布式集群架构;4.0 版本的 lazy-free 彻底解决了大KEY 删除阻塞的运维痛点, modules 则将 Redis 的功能进行了进一步拓展,使其能够实现更多能力,比如 RedisSearch 带来了全文搜索能力; 5.0 版本的 Stream 使 Redis 真正意义上成为了轻量级的消息队列; 6.0 版本的多 IO 和 SSL 提高了 Redis 的性能和安全性。
所有核心特性都在保持 Redis 稳定性的前提下,围绕着性能、扩展性、安全场景和拓展四个方面不断增强。
二、Redis 7.0 核心特性简介
而在 2022 年 4 月正式发布的 Redis 7.0 是目前 Redis 历史版本中变化最大的版本。首先,它有超过 50 个以上新增命令;其次,它有大量核心特性的新增和改进。
Redis 7.0 新增的核心特性不仅能够解决一些之前难以解决的使用问题,同时也将 Redis 的使用场景进行了进一步拓展。
1.Redis 7.0 核心特性-Redis Functions
Redis 2.6 引入了 Lua 脚本。使用 Lua 脚本有非常多好处,比如可以在 Redis 中实现多条命令的原子性操作,能够轻松安全地实现分布式锁功能。但 Lua 脚本持久化在过去的版本中始终属于模糊不清的概念,它不会被 RDD 或 AOF 持久化到磁盘,也不会随着主从由主库同步至从库,一旦发生进程重启或主从 HA, Lua 脚本会丢失。官方也建议用户应当在本地保存一份 Lua 脚本,以保证其安全性。
因此,Lua 脚本一直是 Redis 运维中令人头疼的问题之一。
Redis 7.0 中 Functions 的出现很好地对 Lua 脚本进行了补充。它允许用户向 Redis 加载自定义的函数库:一方面用户自定义的函数名可以有更为清晰的语义,另一方面,Functions 加载的函数库会被主从复制和持久化存储。
Functions 彻底解决了过去 Lua 脚本在持久化上含糊不清的问题,安全性也得到了大幅度提高,通过使用 Functions 即可完全避免 Lua 脚本丢失的问题了,极大减轻了运维压力。
社区计划在后续版本中让 Functions 支持更多语言,比如 javascript 、Python等,相信 Redis Functions 在不久的将来将会彻底替代 Lua 脚本。
2. Redis 7.0 核心特性-Client-eviction
运维 Redis 的时候一定会经历内存未被数据用尽,但 client 却发生了无法写入或数据被强制逐出的问题。
Redis 内存占用主要分为三个部分: data 是用户数据的内存占用, metadata 是元数据的内存占用,client buffer 是连接内存占用。在之前的版本中,可以通过 maxmemory 对 Redis 的内存上限进行控制,但数据占用内存和连接占用内存并不会被 maxmemory 区分。虽然 Redis 提供了参数,比如 clientoutput、buffer limit ,允许对每个连接的内存占用进行限制,但它并不能解决总连接内存的占用。
所以一旦 Redis 连接较多,再加上每个连接的内存占用都比较大的时候, Redis 总连接内存占用可能会达到 maxmemory 的上限,出现内存未被数据用尽却无法写入数据的情况,进而导致丢数据。为了解决上述问题,通常需要给 Redis 分配更多内存来避免连接内存太大而影响业务或数据。
Redis 7.0 新增了 client-invocation 参数,能够从全局的角度对 Redis 连接总内存占用进行控制。举个例子,如果连接总内存占用超过配置上限, Redis 会优先清理内存占用较大的连接,在一定程度上实现了对内存占用和数据内存占用的隔离控制,能够更好地管理 Redis 内存,节约内存使用成本,无须再预留过多内存。
3. Redis 7.0 核心特性-Multi-part AOF
AOF 触发 Rewrite 的时候,Redis 会先将全量数据做内存快照,然后落盘。落盘的漫长过程中会产生增量数据,此时 Redis 会开辟一块新的内存空间来记录这些增量数据,这就带来了额外的内存开销。在极端情况下,AOF Rewrite 过程中的额外内存占用会与 Redis 的数据内存几乎相等,极易发生 OOM ,因此也被迫需要在操作系统中预留更多内存来避免 Redis OOM 的发生,这也是 Redis 内存资源浪费的重要原因。
此外,在 AOF Rewrite 的最后,Redis 会将全量数据与增量数据做一次合并操作,导致一份数据带来两次磁盘 IO。 同时在 AOF 的合并过程中,主进程和子进程之间的数据交互也会占用 CPU 资源。
所以在 AOF Rewrite 过程中,内存、IO、CPU 都会被占用,而这些都是额外的负担,非常影响业务。因此,通常需要将 AOF 自动 Rewrite 改在业务低峰期,通过脚本触发,甚至关闭 AOF。
经历了多次迭代和开发,我们终于通过 Multi-part AOF 彻底解决了上述问题。
Multi-part AOF 是对 AOF 的彻底改造。它将 AOF 分为三个部分,分别是 base AOF、incr AOF 和 History AOF 。其中 base AOF 用来记录 AOF Rewrite 时刻的全量内存数据,incr AOF 用来记录 rewrite 过程中所有增量数据。incr AOF 的出现,使 Redis 不需要再开辟新的内存空间来记录增量数据。而多文件设计的理念也使得新版 AOF 无需做数据合并,因为它的全量和增量被放在不同文件中,天然隔离。在 AOF React 的最后,此前的历史 AOF 文件都会成为 history AOF 被直接删除,因而也不存在合并。
在 Redis 7.0 中, AOF Rewrite 不再是运维中的洪水猛兽,对业务影响降至非常低。
另一个重要改进是 AOF 的增量数据带上了时间戳。在此之前,如果误操作造成数据损坏需要对数据进行恢复,通常需要先用最近一次的 RDB 全量备份做基础数据,然后用 AOF 文件做增量数据恢复。但由于 AOF 中的数据并没有时间戳,因此需要进行繁杂的人工分析。在没有时间信息的情况下,人工找到异常位置难度极大且容易出错。
而新版 AOF 中时间戳的加入可以大幅度减轻人工分析的复杂度。但是 AOF 的恢复仍然存在一些难题:
第一,如果没有及时发现问题,AOF 可能发生 Rewrite ,增量数会全部丢失,无法恢复增量数据。
第二, AOF 不支持按 KEY 恢复数据。如果想在极大的 AOF 文件中恢复个别 KEY,扫描大量无关数据会严重拉长恢复时间。
第三,虽然可以通过关闭 AOF Rewrite 来解决问题一,但也很可能因为 Redis 的大量写入造成 AOF 文件占用巨量磁盘空间。另外,本地备份还存在磁盘损坏,造成数据丢失的风险。
基于以上痛点,阿里云 Tair (即阿里云 Redis 企业版)实现了数据闪回功能。首先,它去掉了 AOF Rewrite ,确保增量数据能够永久保留。同时,它支持将增量数据流式备份至异地存储,解决了本地磁盘不够用以及本地磁盘损坏导致数据丢失的问题。此外,它能够支持 KEY 级恢复,通过过滤能力使数据恢复速度有了大幅提高,真正意义上实现了数据的闪回。
除此之外,阿里云 Tair 还有大量其他企业级能力,无论在性能、稳定性还是功能方面均强于 Redis 社区版。阿里云 Tair 内核团队会逐步将这些企业级能力下放至 Redis 社区版,实现对社区的回馈。
4. Redis 7.0 核心特性-ACL v2
Redis 6.0 大版本中引入了 ACL v1,虽然能够实现一定程度的权限控制,但实用性并不强,比如无法支持粒度至 KEY 的权限访问控制,所有 KEY 的权限必须一致。而在 Redis 7.0 中, ACL v2 正式支持粒度至 KEY 的权限访问控制,可以轻松实现账户对不同 KEY 有不同的权限访问控制。
基于 ACL v2,过去 Redis 常被诟病的业务权限难以维护管理的问题也将得到彻底解决。
阿里云 Redis 7.0 的新版权限管理功能也在规划中,它被称为 RBAC ——基于角色的访问权限控制。在这套系统中,所有资源都能被控制并且成为角色,包括实例、DB、KEY、命令甚至读写分离、路由策略等。此套系统能够带给用户更好更便捷的权限控制管理体验,帮助用户轻松实现不同业务之间的权限隔离与统一管理。