文章目录加载中

Redis 过期时间

# 命令

获取过期时间:TTL keyName。key 不存在时,返回-2;key 永久有效,返回-1。

设置过期时间(单位为秒):EXPIRE keyName seconds

更精确地设置过期时间(单位为毫秒):PEXPIRE keyName millSeconds

取消过期时间,设置为永久:PERSIST keyName

# 应用:频率控制

场景:为了减轻服务器的压力,需要限制每个用户(以 IP 计)一段时间的最大访问量。与时间有关的操作很容易想到 EXPIRE 命令

# 简单做法:借助事务+过期时间

每次访问的时候,纪录值+1.

$isKeyExists = EXISTS rate.limiting:$IP
if $isKeyExists is 1
  $times = INCR rate.limiting:$IP
  if $times > 100
    print 访问频率超过了限制,请稍后再试。
    exit
  else
    # 必须使用事务。否则如果程序出错,或者物理意外,不执行expire命令,那么纪录值永久存在
    # 此时只能访问100次
    MULTI
    INCR rate.limiting:$IP
    EXPIRE $keyName, 60
    EXEC

# 推荐做法:借助列表

上面代码仍然有个问题:如果一个用户在一分钟的第一秒访问了一次博客,在同一分钟的最后一秒访问了 9 次,又在下一分钟的第一秒访问了 10 次,这样的访问是可以通过现在的访问频率限制的,但实际上该用户在 2 秒内访问了 19 次博客,这与每个用户每分钟只能访问 10 次的限制差距较大

借助列表,可以使控制变得平滑:

# 使用列表 rate.limiting:$IP 纪录最近10次访问时间
$listLength = LLEN rate.limiting:$IP
# 当有新的访问进来,如果10次访问没有打满
if $listLength < 10
  LPUSH rate.limiting:$IP, now()
else
  $time = LINDEX rate.limiting:$IP, -1
  # 最早的时间在60s之前,那么就拦截
  if now() - $time < 60
    print 访问频率超过了限制,请稍后再试。
  # 否则,移除最早的时间,并且将现在访问时间加入list最右侧
  else
    LPUSH rate.limiting:$IP, now()
    LTRIM rate.limiting:$IP, 0, 9

如果次数到达 10000 次呢?
随着次数增加,list 开销变大,内存开销变大。此时有 2 种解决方法:
1、使用漏桶算法
2、将时间缩小。例如现在是控制 60 秒内 100 次访问,上面就不是比较 now()和 $time 差值小于 60,而是小于 6。依次类推。

# 应用:缓存

可以实现 LRU。LRU 需要缓存过期时间,以及控制台最大存储上限:

修改配置文件的 maxmemory 参数,限制 Redis 最大可用内存大小(单位是字节),当超出了这个限制时 Redis 会依据 maxmemory-policy 参数指定的策略来删除不需要的键直到 Redis 占用的内存小于指定内存
本文来自心谭博客:xin-tan.com,经常更新web和算法的文章笔记,前往github查看目录归纳:github.com/dongyuanxin/blog