本章讲述Redis在Web方面的应用场景,将数据库的数据处理任务转交给Redis完成。
涉及的新命令:
zremrangebyrank (移除一定范围内成员)
setex(设置值同时设置有效时间)
zcard(返回长度)
登录和cookie缓存
首先是管理登录会话,使用令牌cookie,使用一个散列(Hash)来存储cookie令牌和已登录用户。
检查用户是否登录,可以使用给定的token去查找,返回用户ID,代码如下:
def check_token(conn, token): |
二是管理用户浏览信息,这里使用了一个update_token函数,个人理解如果是单纯登录,则item参数不存在,更新token,若item存在,则记录用户浏览的时间和item。
使用Zremrangebyrank函数,移除旧记录,保留最新的一部分,代码如下:
def update_token(conn, token, user, item=None): timestamp = time.time() #返回浮点数,如111.111 conn.hset('login:', token, user) #hset login token user conn.zadd('recent:', token, timestamp) #zadd recent 111.111 token if item: conn.zadd('viewed:' + token, item, timestamp) #zadd viewedtoken 111.111 itemA conn.zremrangebyrank('viewed:' + token, 0, -26) #zremrangebyrank viewedtoken 0 -26 |
三是会话清理,为防止内存过多,需要清理一些旧的会话。在上面代码的第4行,保证了如果用户在进行操作,时间戳将会不断更新。所以清除1000万之后的会话也就有了保证。清理会话的同时,浏览记录等信息也将被清除。代码如下:
QUIT = False def clean_sessions(conn): end_index = min(size - LIMIT, 100) session_keys = [] conn.delete(*session_keys) #del cart viewed |
使用Redis实现购物车
使用散列(Hash)存储每个用户的购物车,进行增删
def add_to_cart(conn, session, item, count): #session为上一步存储的token if count <= 0: conn.hrem('cart:' + session, item) #此处书中代码有误,应为hdel,即hdel carttoken1 f1 else: conn.hset('cart:' + session, item, count) #hset carttoken1 f1 1 |
网页缓存
网页中许多内容不需要动态生成,对于可以缓存的请求,函数首先从缓存中获取,若不存在,生成内容并放入redis中,并设置一个有效时间。
def cache_request(conn, request, callback): page_key = 'cache:' + hash_request(request) if not content: return content |
数据行缓存
假设网站进行促销活动,需要从数据库取出特价商品以及剩余数量,则需要对数据行进行缓存。
使用String缓存一个数据行,列名为其键,内容为其值,使用json格式。
使用两个有序集合记录何时进行更新,第一个有序集合,成员为行ID,值为时间戳,记录何时将其ID缓存到redis中。第二个有序集合成员为行ID,值为一个数字,每隔多久更新一次数据行,若想取消商品,将延迟值删除或者设置小于等于0即可。
redis中没有嵌套结构,如有需要可以通过键名来模拟嵌套。
def schedule_row_cache(conn, row_id, delay): conn.zadd('delay:', row_id, delay) #数据行的延迟值 zadd delay 5 rowid conn.zadd('schedule:', row_id, time.time()) #数据行的调度 zadd sche 11.11 rowid |
缓存时先取出第一个进行判断是否存在或已经到达时间,然后根据延迟时间判断,延迟时间小于等于0的移除。
若大于,表示商品还需要显示,设置下一次更新时间,并进行更新。
def cache_rows(conn): row_id = next[0][0] row = Inventory.get(row_id) |
网页分析
在update_token()函数中增加一行,记录浏览次数
def update_token(conn, token, user, item=None): timestamp = time.time() conn.hset('login:', token, user) conn.zadd('recent:', token, timestamp) if item: conn.zadd('viewed:' + token, item, timestamp) conn.zremrangebyrank('viewed:' + token, 0, -26) conn.zincrby('viewed:', item, -1) |
为了让新流行的商品也有机会进入排行,进行一定的修剪,此处是将排名20000之后的商品删除,将剩余的商品的权值降低一半。
def rescale_viewed(conn): |
判断页面是否需要缓存
def can_cache(conn, request): item_id = extract_item_id(request) #判断是否为商品页,能否取出ID if not item_id or is_dynamic(request): return False rank = conn.zrank('viewed:', item_id) #获取rank return rank is not None and rank < 10000 #true为前10000的商品,进行缓存 |