前言
这几年,新型非易失存储(Non-Volatile Memory,NVM)器件发展得非常快。NVM 器件具有容量大、性能快、能持久化保存数据的特性,这些刚好就是 Redis 追求的目标。同时,NVM 器件像 DRAM 一样,可以让软件以字节粒度进行寻址访问,所以,在实际应用中,NVM 可以作为内存来使用,我们称为 NVM 内存。
NVM
内存特性
Redis 是基于 DRAM 内存的键值数据库,而跟传统的 DRAM 内存相比,NVM 有三个显著的特点。
- 持久化保存数据:数据保存在 NVM 内存上后,即使发生了宕机或是掉电,数据仍然存在 NVM 内存上。但如果数据是保存在 DRAM 上,那么,掉电后数据就会丢失。
- 访问速度接近 DRAM 的速度:读延迟大约是 200~300ns,而写延迟大约是 100ns。在读写带宽方面,单根 NVM 内存条的写带宽大约是 1~2GB/s,而读带宽约是 5~6GB/s。当软件系统把数据保存在 NVM 内存上时,系统仍然可以快速地存取数据。
- 内存容量大:NVM 器件的密度大,单个 NVM 的存储单元可以保存更多数据。例如,单根 NVM 内存条就能达到 128GB 的容量,最大可以达到 512GB,而单根 DRAM 内存条通常是 16GB 或 32GB。所以,我们可以很轻松地用 NVM 内存构建 TB 级别的内存。
使用模式
业界已经有了实际的 NVM 内存产品,就是 Intel 在 2019 年 4 月份时推出的 Optane AEP 内存条(简称 AEP 内存)。我们在应用 AEP 内存时,需要注意的是,AEP 内存给软件提供了两种使用模式,分别对应着使用了 NVM 的容量大和持久化保存数据两个特性。
Memory模式
这种模式下把 NVM 内存作为大容量内存来使用,只使用 NVM 容量大和性能高的特性,没有持久化数据的功能。
在 Memory 模式下,服务器上仍然需要配置 DRAM 内存,但是,DRAM 内存是被 CPU 用作 AEP 内存的缓存,DRAM 的空间对应用软件不可见。换句话说,软件系统能使用到的内存空间,就是 AEP 内存条的空间容量。
App Direct模式
这种模式启用了 NVM 持久化数据的功能。在这种模式下,应用软件把数据写到 AEP 内存上时,数据就直接持久化保存下来了。所以,使用了 App Direct 模式的 AEP 内存,也叫做持久化内存(Persistent Memory,PM)。
基于 NVM 内存的实践
当 AEP 内存使用 Memory 模式时,应用软件就可以利用它的大容量特性来保存大量数据,Redis 也就可以给上层业务应用提供大容量的实例了。而且,在 Memory 模式下,Redis 可以像在 DRAM 内存上运行一样,直接在 AEP 内存上运行,不用修改代码。
不过,有个地方需要注意下:在 Memory 模式下,AEP 内存的访问延迟会比 DRAM 高一点。我刚刚提到过,NVM 的读延迟大约是 200~300ns,而写延迟大约是 100ns。所以,在 Memory 模式下运行 Redis 实例,实例读性能会有所降低,我们就需要在保存大量数据和读性能较慢两者之间做个取舍。
那么,当我们使用 App Direct 模式,把 AEP 内存用作 PM (Persistent memory, PM) 时,Redis 又该如何利用 PM 快速持久化数据的特性呢?这就和 Redis 的数据可靠性保证需求和现有机制有关了,我们来具体分析下。
为了保证数据可靠性,Redis 设计了 RDB 和 AOF 两种机制,把数据持久化保存到硬盘上。
但是,无论是 RDB 还是 AOF,都需要把数据或命令操作以文件的形式写到硬盘上。对于 RDB 来说,虽然 Redis 实例可以通过子进程生成 RDB 文件,但是,实例主线程 fork 子进程时,仍然会阻塞主线程。而且,RDB 文件的生成需要经过文件系统,文件本身会有一定的操作开销。
对于 AOF 日志来说,虽然 Redis 提供了 always、everysec 和 no 三个选项,其中,always 选项以 fsync 的方式落盘保存数据,虽然保证了数据的可靠性,但是面临性能损失的风险。everysec 选项避免了每个操作都要实时落盘,改为后台每秒定期落盘。在这种情况下,Redis 的写性能得到了改善,但是,应用会面临秒级数据丢失的风险。
此外,当我们使用 RDB 文件或 AOF 文件对 Redis 进行恢复时,需要把 RDB 文件加载到内存中,或者是回放 AOF 中的日志操作。这个恢复过程的效率受到 RDB 文件大小和 AOF 文件中的日志操作多少的影响。
所以,不要让单个 Redis 实例过大,否则会导致 RDB 文件过大。在主从集群应用中,过大的 RDB 文件就会导致低效的主从同步。现在 Redis 在涉及持久化操作时的问题:
- RDB 文件创建时的 fork 操作会阻塞主线程
- AOF 文件记录日志时,需要在数据可靠性和写性能之间取得平衡
- 使用 RDB 或 AOF 恢复数据时,恢复效率受 RDB 和 AOF 大小的限制
但是,如果我们使用持久化内存(Memory),就可以充分利用 PM 快速持久化的特点,来避免 RDB 和 AOF 的操作。因为 PM 支持内存访问,而 Redis 的操作都是内存操作,那么,我们就可以把 Redis 直接运行在 PM 上。同时,数据本身就可以在 PM 上持久化保存了,我们就不再需要额外的 RDB 或 AOF 日志机制来保证数据可靠性了。
实现 PM 支持 Redis 的持久化操作
当服务器中部署了 PM 后,我们可以在操作系统的 /dev 目录下看到一个 PM 设备,如下所示:
/dev/pmem0
然后,我们需要使用 ext4-dax 文件系统来格式化这个设备:
mkfs.ext4 /dev/pmem0
接着,我们把这个格式化好的设备,挂载到服务器上的一个目录下:
mount -o dax /dev/pmem0 /mnt/pmem0
此时,我们就可以在这个目录下创建文件了。创建好了以后,再把这些文件通过内存映射(mmap)的方式映射到 Redis 的进程空间。这样一来,我们就可以把 Redis 接收到的数据直接保存到映射的内存空间上了,而这块内存空间是由 PM 提供的。所以,数据写入这块空间时,就可以直接被持久化保存了。
因为 PM 的读写速度比 DRAM 慢,所以,如果使用 PM 来运行 Redis,需要评估下 PM 提供的访问延迟和访问带宽,是否能满足业务层的需求。
总结
NVM 内存是近年来存储设备领域中一个非常大的变化,它既能持久化保存数据,还能像内存一样快速访问,这必然会给当前基于 DRAM 和硬盘的系统软件优化带来新的机遇。
参考
摘自 极客时间 - 蒋德钧老师的《Redis 核心技术与实战》 <- 极其推荐大家阅读~
《Redis 核心技术与实战》学习笔记 Day2