使用Redis做数据缓存
目的
本关目的:使用Redis
实现数据缓存。
相关知识
本文将将会你掌握:1
.将数据加入缓存队列,2
.缓存数据。
在我之前的文章中提到了实现了使用 Redis
做动态页面缓存,以此提高访问速度,但同时我们也提到了还有少部分动态页面是不可以对整个页面进行缓存的,例如商品页面,用户详情页面等。尽管这些页面不可以使用页面缓存,但我们仍可以对其中动态内容所需要的数据进行缓存,从而加快动态页面绘制时读取数据的速度,减少页面载入所需的时间。
使用 Redis
做数据缓存的做法是:
- 编写一个将数据加入缓存队列的函数
- 通过一个有序集合
cache:list
存储数据加入缓存的时间- 成员为数据
ID
(唯一标识)
- 成员为数据
- 通过一个有序集合
- 分值为当前时间(
time.time()
)- 通过一个有序集合
cache:delay
存储数据更新周期- 成员为数据
ID
(唯一标识)
- 成员为数据
- 通过一个有序集合
- 分值为更新周期,单位为秒
- 编写一个定时缓存数据的函数
- 将数据转换成
JSON
格式 - 然后将上述
JSON
存储到Redis
中 - 根据缓存更新周期定时更新
Redis
中的缓存键
- 将数据转换成
JSON
一种轻量级的数据交换格式。大多数编程语言都能高效地编码/解码
JSON
格式的数据。
将数据加入缓存队列
有序集合 cache:list
作为缓存队列,其需要依赖数据更新周期有序集合 cache:delay
,加入某数据的更新周期不存在,那么我们则需要删除掉该数据的缓存。在这里,我们采用一种更便捷的方式避免数据缓存和取消数据缓存,那就是将该数据的更新周期设置为小于等于 0
。
将数据加入缓存队列需要同时操作这两个有序集合:
def add_cache_list(data_id, delay):
conn.zadd('cache:delay', data_id, delay)
conn.zadd('cache:list', data_id, time.time())
缓存数据
我们将数据加入到缓存队列后,就将有序集合 cache:list
的分值看作下一次要更新的时间,所以我们可以根据分值对有序集合 cache:list
进行排序,并连同分值一起取出从小到大顺序的第一个成员(最可能需要更新的成员):
conn.zrange('cache:list', 0, 0, withscores=True)
其中 withscores=True
会告诉 Redis
在返回成员时一同返回成员的分值,返回一个由零或一个元组组成的列表,例如:[(member1, score1)]
。
接下来,我们再判断该成员的分值:
- 成员不存在/成员分值大于当前时间(还没有达到下一次更新的时间)
- 等待
100
毫秒 - 继续后续操作
- 等待
- 从有序集合
cache:delay
中取出该成员的更新周期- 若更新周期小于等于
0
:
- 若更新周期小于等于
- 从有序集合
cache:delay
中删除该成员 - 从有序集合
cache:list
中删除该成员 - 删除该成员的缓存键
cache:data:*
,其中*
是数据ID
(唯一标识)- 若更新周期大于
0
:- 将当前时间加上该成员的更新周期,重新存入有序集合
cache:list
中 - 从数据库中获取到该数据值(这里可以使用伪造数据替代,例如:
{'id':id, 'data':'fake data'}
) - 更新该成员的缓存键
cache:data:*
,值为:上述数据编码成JSON
格式json(dumps(data))
- 将当前时间加上该成员的更新周期,重新存入有序集合
- 若更新周期大于
将这些过程编写为 cache_row()
方法:
def cache_data():
# 从有序集合'cache:list'中获取排名第一的元素及其分数(score)
next = conn.zrange('cache:list', 0, 0, withscores=True)
# 获取当前时间
now = time.time()
# 如果没有下一个元素或者下一个元素的时间戳大于当前时间,则休眠0.1秒
if not next or next[0][1] > now:
time.sleep(0.1)
# 获取下一个元素的ID
data_id = next[0][0]
# 获取该元素在有序集合'cache:delay'中的分数,即延迟值
delay = conn.zscore('cache:delay', data_id)
# 如果延迟值小于等于0,则表示数据已过期
if delay <= 0:
# 从'cache:delay'集合中移除该数据的ID
conn.zrem('cache:delay', data_id)
# 从'cache:list'集合中移除该数据的ID
conn.zrem('cache:list', data_id)
# 删除键为'cache:data:' + data_id的相关数据
conn.delete('cache:data:' + data_id)
else:
# 创建一个包含虚假数据的字典对象
data = {'id': data_id, 'data': 'fake data'}
# 更新有序集合'cache:list'中该数据的时间戳,使其在延迟之后再次被处理
conn.zadd('cache:list', data_id, now + delay)
# 将虚假数据存储在键为'cache:data:' + data_id的Redis键中
conn.set('cache:data:' + data_id, json.dumps(data))