涉及到的key:
1. login,hash结构,存储用户token与用户ID之间的映射。
2. recent_tokens,存储最近登陆用户token,zset结构
member: token,score: 登陆时间戳
3. viewed_token,存储token对应用户的浏览商品集合,zset结构,
member: 商品ID,score: 浏览时间戳
4. cart_token, 存储token对应用户的购物车,hash结构,key:商品ID,value: 商品数量
5. schedule, zset结构, member为数据行的行ID,score是一个时间戳,对应何时将指定的数据行缓存到Redis里面
6. delay,zset结构, member为数据行的行ID,score记录了指定数据行的缓存需要每隔多少秒更新一次。
7. viewed, zset结构, member:商品ID, score: 商品浏览次数,用负数表示,浏览次数最高的商品,其索引值为0
# python3 # -*- coding: utf-8 -*- import redis import time import json def check_token(conn, token): return conn.hget('login', token) def update_token(conn, token, user, item=None): timestamp = time.time() conn.hset('login', token, user) conn.zadd('recent_tokens', token, timestamp) # 用户正在浏览的是一个商品页面 if item: conn.zadd('viewed_' + token, item, timestamp) # 只保留用户最近浏览过的25个商品 conn.zremrangebyrank('viewed_' + token, 0, -26) # 采用负数表示页面浏览次数,浏览次数越高的页面,其索引值越小 conn.zincrby('viewed', item, -1) QUIT = False LIMIT = 10000000 # 应该用守护进程来执行这个函数或者做成定时任务 #清除内容包括: recent_tokens,login,用户对应的浏览记录、购物车 def clean_full_session(conn): while not QUIT: size = conn.zcard('recent_tokens') if size <= LIMIT: time.sleep(1) continue end_index = min(size - LIMIT, 100) sessions = conn.zrange('recent_tokens', 0, end_index - 1) session_keys = [] for sess in sessions: session_keys.append('viewed_' + sess) session_keys.append('cart_' + sess) conn.delete(*session_keys) conn.hdel('login', *sessions) conn.zrem('recent_tokens', *sessions) # 添加商品到购物车 def add_to_cart(conn, session, item, count): if count <= 0: conn.hrem('cart_' + session, item) else: conn.hset('cart_' + session, item, count) def can_cache(conn,request): item_id = extract_item_id(request) if not item_id or is_dynamic(request): return False rank = conn.zrank('viewed', item_id) return rank is not None and rank < 1000 def cache_request(conn, request, callback): if not can_cache(conn,request): return callback(request) page_key = 'cache_' + hash_request(request) content = conn.get(page_key) if not content: content = callback(request) conn.setex(page_key, content, 300) return content def schedule_row_cache(conn, row_id, delay): conn.zadd('delay', row_id, delay) conn.zadd('schedule', row_id, time.time()) # 守护进程方式运行或做成定时任务 def cache_rows(conn): while not QUIT: next = conn.zrange('schedule', 0, 0, withscores=True) now = time.time() if not next or next[0][1] > now: time.sleep(.05) continue row_id = next[0][0] delay = conn.zscore('delay', row_id) if delay <= 0: conn.zrem('delay', row_id) conn.zrem('schedule', row_id) conn.delete('inv_' + row_id) continue row = Inventory.get(row_id) conn.zadd('schedule', row_id, now + delay) conn.set('inv_' + row_id, json.dumps(row.to_dict())) def rescale_viewed(conn): while not QUIT: # 保留浏览次数最低的20000个商品 conn.zremrangebyrank('viewed', 0, -20001) conn.zinterstore('viewed', {'viewed': .5}) time.sleep(300) r = redis.Redis(host='redis_serverip', port=6379, password='redis_passwd', db=0)
参考资料:
《Redis实战》