在前面的课程中,我们陆续介绍了 Redis 的六种数据结构,它们分别是字符串、列表、散列、集合、有序集合和 HyperLogLog。
除此之外,我们还介绍了如何使用数据库命令来操作 Redis 的数据库本身:比如使用 DEL 命令删除数据库中的某个键,使用 SCAN 命令渐进地遍历整个数据库,使用 DBSIZE 返回数据库包含的键值对数量,等等。
在这些数据库功能的基础上,Redis 还提供了一些附加功能,它 们分别是:键过期功能、发布与订阅功能、流水线功能、事务功能以及 Lua 脚本功能。
这些附加功能要么就是用来解决特定 问题的,要么就是用来增强数据库功能、使得数据库设置操作能够更好地进行的,在接下来的几节课程中,我们将陆续介绍这些附加功能。
让 Redis 在指定的时间自动删除特定的键。
Redis 提供了两个命令来设置键的生存时间(TTL,time to live),它们分别是:
如果给定的键不存在,那么 EXPIRE 和 PEXPIRE 将返回 0 ,表示设置失败;如果命令返回 1 ,那么表示设置成功。
当一个键被设置了生存时间之后,它的生存时间将会随着时间的流逝而减少:时间过去一毫秒,键的生存时间就减少一毫秒;时间过去一秒钟,键的生存时间就减少一秒钟;以此类推。
当一个键的生存时间被减少至低于 0 时,Redis 就会自动将这个键删除掉。
redis> SET msg "hello world"
OK
redis> EXPIRE msg 5 # 设置生存时间
(integer) 1
redis> EXISTS msg # 仍然存在
(integer) 1
redis> EXISTS msg # 已被自动删除
(integer) 0
redis> PEXPIRE number 5500
(integer) 1
redis> EXISTS number
(integer) 1
redis> EXISTS number
(integer) 0
注 1 :一秒钟 = 1000 毫秒
注 2 :Redis 默认每 100 毫秒检查一次键是否过期,所以上表也是以每 100 毫秒为单位列出的。
Redis 提供了两个命令用于设置键的过期时间(expire time):
如果给定的键不存在,那么 EXPIREAT 和 PEXPIREAT 将返回 0 ,表示设置失败;如果命令返回 1 ,那么表示设置成功。
对于被设置了过期时间的键来说,当键的过期时间小于当前时间的时候,Redis 就会自动地删除该键。
redis> SET msg "time to go"
OK
redis> EXPIREAT msg 1000000005
(integer) 1
redis> EXISTS msg # 1000000005 之前
(integer) 1
redis> EXISTS msg # 1000000005 之后
(integer) 0
redis> SET song "Last Night, Good Night"
OK
redis> PEXPIREAT song 1000000005500
(integer) 1
redis> EXISTS song # 1000000005500 之前
(integer) 1
redis> EXISTS song # 1000000005500 之后
(integer) 0
设置生存时间和设置过期时间都可以让 Redis 自动删除指定的键,它们的区别在于设置“键什么时候要被删除”的方式不同:
• EXPIRE 和 PEXIRE 的作用是让键在 N 秒钟或者 N 毫秒之后被删除。
• 而 EXPIREAT 和 PEXPIREAT 的作用则是让键在指定的 UNIX 时间到达之后被删除。
带有生存时间的键就像是一个倒计时器,它会倒数 5、4、3、2、1、0,然后被删掉。
而带有过期时间的键则像是一个定时器,它会在指定的时间来临之后被删掉。
在为一个键设置过期时间或者生存时间之后,用户可以使用 TTL 命令或者 PTTL 命令查看这个键的剩余生存时间,以此来获知键还有多久才会被 Redis 删除:
redis> SET msg "hello"
OK
redis> EXPIRE msg 10086
(integer) 1
redis> TTL msg
(integer) 10083
redis> PTTL msg
(integer) 10079336
redis> SET number 10086
OK
redis> EXPIREAT number 1408498480
(integer) 1
redis> TTL number # 距离 14...80 的秒数
(integer) 15
redis> PTTL number # 距离 14...80 的毫秒数
(integer) 11651
注意,Redis 并没有提供查看键的过期时间的命令,所以对于一个设置了过期时间的键来说,我们只能使用TTL 和 PTTL 来查看它的剩余生存时间。
PERSIST key
移除为键 key 设置的过期时间或生存时间,使得它不会被 Redis 自动删除。
移除成功时命令返回 1 ;如果命令没有设置过期时间或生存时间,那么命令返回 0 。 复杂度为 O(1) 。
redis> SET msg "hello"
OK
redis> EXPIRE msg 30
(integer) 1
redis> TTL msg
(integer) 28
redis> PERSIST msg
(integer) 1
redis> TTL msg
(integer) -1
redis> SET number 10086
OK
redis> EXPIREAT number 1408499100
(integer) 1
redis> TTL number
(integer) 25
redis> PERSIST number
(integer) 1
redis> TTL number
(integer) -1
自动更新缓存、自动刷新排行榜、定时删除的用户 Session。
示例:自动更新的缓存
以下是《字符串》一节介绍过的缓存方法,但这个方法并不实用,因为程序需要通过手动删除旧的缓存来进行缓存更新,否则程序就会一直使用同一个缓存:
@app.route(“/”)
def index():
cached_content = GET ‘index’
if cached_content:
return cached_content
else:
content = fetch_and_create_index()
SET ‘index’ content
return content
以下是使用键过期功能实现的,一个每五分钟就会自动更新一次的缓存实现:
@app.route(“/”)
def index():
cached_content = GET ‘index’
if cached_content:
return cached_content
else:
content = fetch_and_create_index()
SET ‘index’ content
EXPIRE ‘index’ 5*60
return content
因为
SET key value
EXPIRE key seconds
这个模式经常出现,所以 Redis 在 2.0 版本新增了 SETEX 命令:
SETEX key seconds value
这个命令相当于原子地执行 SET 命令和 EXPIRE 命令。
在 Redis 2.6.0 ,Redis 开始支持毫秒精度的过期时间和生存时间,并增加了相应的 PEXPIRE、PEXPIREAT、PTTL 三个命令,以及 PSETEX 命令:
PSETEX key milliseconds value
执行这个命令相当于原子地执行以下两个命令:
SET key value
PEXPIRE key milliseconds
在 Redis 2.6.12 版本,Redis 又对 SET 命令进行了修改,让它支持可选的 EX 参数和 PX 参数:
SET key value [EX seconds] [PX milliseconds]
执行 SET key value EX seconds 相当于执行 SETEX key seconds value ; 而执行 SET key value PX milliseconds 相当于执行 PSETEX key milliseconds value 。
尽管目前 SETEX 和 PSETEX 都能正常使用,但我个人 还是建议尽量使用 SET 而不是 SETEX 或者PSETEX ,因为使用 SET 来设置生存时间更简洁一些,而比较旧的 SETEX 和 PSETEX 在将来可能会被废弃。
# coding: utf-8
class Cache:
def __init__(self, client):
self.client = client
def put(self, name, content):
self.client.set(name, content)
def get(self, name):
return self.client.get(name)
# 使用 SETEX 实现
def expire_put(self, name, content, ttl):
self.client.setex(name, content, ttl)
# 使用 SET + EXPIRE 实现
def expire_put2(self, name, content, ttl):
self.client.set(name, content)
self.client.expire(name, ttl)
# 使用 PSETEX 实现
def expire_put3(self, name, content, ttl):
"""
Python 客户端尚未支持 PSETEX 命令,
如果支持的话,我们可以通过将 ttl*1000 来调用 PSETEX
从而实现与 SETEX 相同的效果:
PSETEX name ttl*1000 content
"""
raise
# 使用 SET ... EX seconds 实现
def expire_put4(self, name, content, ttl):
"""
Python 客户端尚未支持 SET 命令的 EX 参数,
如果支持的话,我们可以用以下方法来实现与 SETEX 相同的效果:
SET name content EX ttl
"""
raise
# 使用 SET ... PX milliseconds 实现
def expire_put5(self, name, content, ttl):
"""
Python 客户端尚未支持 SET 命令的 PX 参数,
如果支持的话,我们可以用以下方法来实现与 SETEX 相同的效果:
SET name content PX ttl*1000
"""
raise
在介绍有序集合的时候,我们给出了日排行版的实现方法(rank_list.py),通过给日排行榜设置生存时间,我们可以让 Redis 在每个新的一天开始时,自动删除旧的排行榜。实现方法是,在每次添加一个 项到排行榜之后,执行以下检查:
# 如果排行榜还没有设置生存时间
# 那么将它的生存时间设置为今天剩余的秒数
if TTL(ranklist) == -1:
EXPIRE ranklist today_remain_seconds()
通过这个设置操作,程序可以让 Redis 自动删除每天的旧排行榜。
# encoding: utf-8
from datetime import datetime, timedelta
def today_remaind_seconds():
now = datetime.now()
tomorrow = now + timedelta(days=1) - timedelta(hours=now.hour, minutes=now.minute,seconds=now.second)
return (tomorrow-now).seconds
class DayRankList:
def __init__(self, key, client):
self.key = key
self.client = client
def incr(self, item, increment=1):
# 注意在 redis-py 客户端中
# zincrby() 方法是先给定元素后给定分值
# 这和 ZINCRBY 命令规定的顺序正好相反
self.client.zincrby(self.key, item, increment)
# 注意 Python 客户端在键没有生存时间/过期时间时返回 None 而不是 -1
if self.client.ttl(self.key) is None:
self.client.expire(self.key, today_remaind_seconds())
def get_top(self, n, show_score=False):
return self.client.zrevrange(self.key, 0, n-1, withscores=show_score)
用户 Session (会话)是一种验证用户身份的手段,通常会分 别储存在服务器端和客户端,一般由用户ID(uid)和 Session ID (sid)组成。
当用户成功登录网站之后,网页浏览器会把网站返回的 Session 储存起来,之后每次浏览器查看网站的某个页面时,浏览器都会把 Session 里面储存的信息连同网页请求一并发送给网站,使得网站可以检查这个客户端是否已经登录,以及是否为指定的用户,等等。
Session 一般每过一周或者一个月就会自 动过期,强制用户再次登录,以防用户遗忘密码,或者Session ID 遭人破解。
# 设置客户端,设置 Session 的生存时间为一个月(默认值)
session = Session(client)
# 为 ID 为 10086 的用户创建 Session ID
sid = session.assign(10086)
# 检查 Session ID 是否正确,将返回 True ,表示传入的 Session ID 正确
session.valid(10086, sid)
# 再次检查,这次将返回 False ,表示 Session ID 不正确
session.valid(10086, “WrongSessionID”)
使用键过期功能可以让 Redis 自动在指定的时间删除特定的键。 让 Redis 自动删除键的方法有两种,一种是 为键设置生存时间,另一种是为键设置过期时间:
• 生存时间就像一个倒计时器,它会随着时间的流逝而不断减少,当生存 时间被减少至低于 0 时,键就会被删除;
• 过期时间就像一个定时器,当给定的时间到达之后,键就会被删除。