《Redis实战》笔记——第二章

本章讲述Redis在Web方面的应用场景,将数据库的数据处理任务转交给Redis完成。

涉及的新命令:

    zremrangebyrank (移除一定范围内成员)

    setex(设置值同时设置有效时间)

    zcard(返回长度)

登录和cookie缓存

首先是管理登录会话,使用令牌cookie,使用一个散列(Hash)来存储cookie令牌和已登录用户。

检查用户是否登录,可以使用给定的token去查找,返回用户ID,代码如下:

def check_token(conn, token):
    return conn.hget('login:', token)   #对应redis命令:hget login 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
LIMIT = 10000000

def clean_sessions(conn):
    while not QUIT:
        size = conn.zcard('recent:')                    #zcard recent 获取长度
        if size <= LIMIT:                               
            time.sleep(1)                               
            continue

        end_index = min(size - LIMIT, 100)              
        tokens = conn.zrange('recent:', 0, end_index-1)  #zrange recent 0 99 获取token

        session_keys = []                               
        for sess in sessions:
            session_keys.append('viewed:' + sess)
            session_keys.append('cart:' + sess)     

        conn.delete(*session_keys)                      #del cart viewed
        conn.hdel('login:', *tokens)                    #hdel login token1 token2
        conn.zrem('recent:', *tokens)                   #zrem recent token1 token2

使用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):
    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)          #setex pagekey 300 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):
    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)                   #获取延迟值 zscore delay rowid
        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()))   #更新商品

 

网页分析

在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):
    while not QUIT:
        conn.zremrangebyrank('viewed:', 20000, -1)      #zremrangebyrank viewed 20000 -1
        conn.zinterstore('viewed:', {'viewed:': .5})    #zinterstore viewed 1 viewed weight 0.5
        time.sleep(300)        

 

判断页面是否需要缓存

 

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的商品,进行缓存
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值