SET 命令的 EX 选项和 PX 选项

在使用键过期功能时,组合使用 SET 命令和 EXPIRE / PEXIRE 命令的做法非常常见,比如上面展示的带有自动移除特性的缓存程序就是这样做的。

因为 SET 命令和 EXPIRE / PEXPIRE 命令组合使用的情况是如此的常见,所以为了方便用户使用这两组命令,Redis 从 2.6.12 版本开始为 SET 命令提供 EX 选项和 PX 选项,用户可以通过使用这两个选项的其中一个来达到同时执行 SET 命令和 EXPIRE / PEXPIRE 命令的效果:

  1. SET key value [EX seconds] [PX milliseconds]

这也就是说,如果我们之前执行的是 SET 命令和 EXPIRE 命令:

  1. SET key value
  2. EXPIRE key seconds

那么现在只需要执行一条带有 EX 选项的 SET 命令就可以了:

  1. SET key value EX seconds

与此类似,如果我们之前执行的是 SET 命令和 PEXPIRE 命令:

  1. SET key value
  2. PEXPIRE key milliseconds

那么现在只需要执行一条带有 PX 选项的 SET 命令就可以了:

  1. SET key value PX milliseconds

组合命令的安全问题

使用带有 EX 选项或 PX 选项的 SET 命令除了可以减少命令的调用数量并提升程序的执行速度之外,更重要的是保证了操作的原子性,使得“为键设置值”和“为键设置生存时间”这两个操作可以一起执行。

比如说,前面在实现带有自动移除特性的缓存程序时,我们首先使用了 SET 命令设置缓存,然后又使用了 EXPIRE 命令为缓存设置生存时间,这相当于让程序依次地向 Redis 服务器发送以下两条命令:

  1. SET key value
  2.  
  3. EXPIRE key timeout

因为这两条命令是完全独立的,所以服务器在执行它们的时候,就可能会出现 SET 命令被执行了,但是 EXPIRE 命令却没有被执行的情况。比如说,如果 Redis 服务器在成功执行 SET 命令之后因为故障下线,导致 EXPIRE 命令没有被执行,那么 SET 命令设置的缓存就会一直存在,而不会因为过期而自动被移除。

与此相反,使用带有 EX 选项或 PX 选项的 SET 命令就没有这个问题:当服务器成功执行了一条带有 EX 选项或 PX 选项的 SET 命令时,键的值和生存时间都会同时被设置好,因此程序就不会出现只设置了值但是却没有设置生存时间的情况。

基于上述原因,我们把前面展示的缓存程序实现称之为“不安全”(unsafe)实现。为了修复这个问题,我们可以使用带有 EX 选项的 SET 命令来重写缓存程序,重写之后的程序正如代码清单 12-2 所示。


代码清单 12-2 重写之后的缓存程序:/expire/volatile_cache.py

  1. class VolatileCache:
  2.  
  3. def __init__(self, client):
  4. self.client = client
  5.  
  6. def set(self, key, value, timeout):
  7. """
  8. 把数据缓存到键 key 里面,并为其设置过期时间。
  9. 如果键 key 已经有值,那么使用新值去覆盖旧值。
  10. """
  11. self.client.set(key, value, ex=timeout)
  12.  
  13. def get(self, key):
  14. """
  15. 获取键 key 储存的缓存数据。
  16. 如果键不存在,又或者缓存已经过期,那么返回 None 。
  17. """
  18. return self.client.get(key)

重写之后的缓存程序实现是“安全的”:设置缓存和设置生存时间这两个操作要么就一起成功,要么就一起失败,“设置缓存成功了,但是设置生存时间却失败了”这样的情况将不会出现。后续的章节也会介绍如何通过 Redis 的事务功能来保证执行多条命令时的安全性。

其他信息

属性
复杂度O(1)
版本要求带有 EX 选项和 PX 选项的 SET 命令从 Redis 2.6.12 版本开始可用。