dot 快速的未来正在您的城市举办的活动中到来。

加入我们在 Redis 发布会

Redis 4.0 的鲜为人知的功能,可加速您的应用程序

Redis 4.0 为 Redis 生态系统带来了一个惊人的功能:模块。模块 是 Redis 的一个重大转变——突然之间,它成为一个开放的定制数据类型和全速计算的景观,直接位于 Redis 内部。但虽然此次发布的大部分宣传都集中在模块上,但新版本还引入了一个非常重要的命令,它本身就是游戏规则的改变者:UNLINK

要确定您是否可以使用 UNLINK 命令,请从 redis-cli 运行 INFO。响应将告诉您有关服务器的所有信息。在第一部分(#Server)中,应该有一行称为redis_version.如果此值大于 4.0,则可以开始使用 UNLINK 命令。所有版本的Redis 企业版 5.0+ 和所有新的 Redis 企业版云订阅都应该能够使用 UNLINK 命令。并非所有 Redis 提供商都保持最新,因此在更改任何代码之前最好检查版本。

让我们回顾一下 Redis 的一个关键架构特征:单线程。Redis 在很大程度上是一个单线程应用程序。它一次只做一件事,并且它以超快的速度完成这些事。多线程很复杂,会引入锁和其他问题,这些问题反直觉地会减慢应用程序的速度。虽然 Redis(到 4.0 为止)在多线程中执行了少量操作,但它通常会在开始另一个命令之前完成一个命令。

删除一个键(使用 DEL)通常是一个您可能不会过多考虑的命令。高速写入和读取值得吹嘘,但在许多情况下,删除数据同样重要。与 Redis 中的大多数其他命令一样,DEL 命令在单个线程中运行。如果您有一个值只有几 KB 的键,这不是什么大问题——它可能只需要不到毫秒的时间。当您的键的值是一个 MB 时会发生什么?100 MB?500 MB?哈希、有序集合、列表或集合通常是通过随着时间的推移添加项目来构建的,这可能会导致一个多 GB 的键。当您使用 DEL 删除这些大键之一时会发生什么?由于 Redis 是单线程的,因此您的整个服务器会被占用……嗯,一段时间。更糟糕的是,这些键中保存的数据可能是通过数千次或数百万次微小的请求构建的,因此应用程序或操作员可能并不真正了解删除数据需要多长时间。

理智告诉我们不要在 一个包含一百万个成员的有序集合上运行这样的命令:

> ZRANGE some-zset 0 -1

但是,DEL 在some-zset 上将花费类似的时间——没有传输开销,但确实存在内存释放问题,这些问题确实会累加起来,而且在此期间,您的 CPU 会被占用。在 UNLINK 之前,您可能不得不诉诸非原子方法,即结合使用 SCAN 执行少量删除操作来避免这种内存释放噩梦。无论哪种方式,这都不是一件愉快的事!

您可能已经猜到了,UNLINK 是来拯救我们的!UNLINK 在语法上与 DEL 相同,但提供了一个更理想的解决方案。首先,它从整个键空间中删除该键。然后,它在另一个线程中开始回收内存。从多线程的角度来看,这是一个安全的操作,因为它(在主线程中)从键空间中删除了该项目,因此从任何 Redis 命令中都无法访问它。

如果您有很大的值,则速度提升非常显著——UNLINK 是一个 O(1) 操作(每个键;在主线程中),与键中保存的值的大小无关。而一个大的值可能需要几百毫秒或更长时间才能用 DEL 删除,UNLINK 将在不到毫秒的时间内完成(包括网络往返行程)。当然,您的服务器仍然需要在另一个线程中花费周期来重新分配值内存(其中工作量为 O(N),其中 N 是已删除值的分配次数),但您的主线程性能不太可能受到另一个线程中正在进行的操作的严重影响。

因此,您是否应该将代码中的所有 DEL 都替换为 UNLINK?可能是的。有一些小的边缘情况,其中 DEL 正是您想要的。以下是两个我能想到的

  • 在 MULTI/EXEC 或管道中,当添加和删除大值时,DEL 是理想的。在这种情况下,UNLINK 不会立即释放空间,在运行到内存限制的流量繁忙的情况下,您可能会遇到麻烦。
  • 当能够在不驱逐的情况下写入比快速响应更重要时。

在没有极端内存限制的全新环境中,很难想象您不希望使用 UNLINK 的情况。UNLINK 将提供更一致的行为和整体更好的性能,并且这是一个非常小的代码更改(或者如果您可以重命名客户端中的命令,则没有更改)。如果 UNLINK 适合您的应用程序,请将您的 DEL 更改为 UNLINK,并查看改进效果。