CherryPy+Redis Web应用分页:选SQL Limit还是Redis Scan?
针对Redis+CherryPy分页的解决方案
嘿,先直接给你划重点:SQL Limit完全不适用,因为Redis是键值数据库,根本不支持SQL语法!你得用Redis的SCAN命令来实现分页,而且绝对不能用你现在写的con.keys()——数据量大的时候这个命令会直接阻塞Redis服务,生产环境里踩过这个坑的人都懂有多酸爽😅
为什么选Redis SCAN而不是KEYS?
KEYS *会一次性遍历Redis所有键,数据量几万以上时,Redis会被卡住好几秒,其他请求全堵着;SCAN是渐进式遍历,每次只返回一小批键,不会阻塞服务,还能通过游标(cursor)控制遍历进度,完美适配分页需求。
怎么用SCAN实现每页10条的分页?
SCAN的基本用法是SCAN cursor COUNT 数量,游标从0开始,每次调用会返回新的游标和一批键,当游标回到0时表示遍历完成。不过要注意:COUNT只是Redis的提示值,不是精确返回数量,所以我们需要做一点处理来确保每页能拿到10条数据。
下面是修改后的CherryPy控制器代码,我帮你实现了分页逻辑:
import redis import json import cherrypy from controllers.base import BaseController con = redis.Redis(host='localhost', port=6379, db=3) class HomeController(BaseController): @cherrypy.expose def index(self, page=1): page = int(page) per_page = 10 total_collected = 0 cursor = 0 current_page_keys = [] # 循环扫描直到凑够当前页的10条数据,或者遍历完所有键 while total_collected < per_page * page: cursor, keys = con.scan(cursor=cursor, count=20) # COUNT设为20提高命中率 # 去重(SCAN可能返回重复键) unique_keys = list(set(keys)) # 计算当前页需要的键范围 start_idx = per_page * (page - 1) end_idx = start_idx + per_page # 把键加入临时列表 current_page_keys.extend(unique_keys) total_collected = len(current_page_keys) # 如果游标回到0,说明遍历完了,直接跳出 if cursor == 0: break # 截取当前页的10条键 current_page_keys = current_page_keys[start_idx:end_idx] # 批量获取值,比循环get效率高很多 values = con.mget(current_page_keys) # 处理数据,适配不同格式的值 outdata = [] for key, val in zip(current_page_keys, values): if val: try: data = json.loads(val) outdata.append({"key": key.decode('utf-8'), "value": data}) except json.JSONDecodeError: outdata.append({"key": key.decode('utf-8'), "value": val.decode('utf-8')}) # 这里返回渲染后的页面(示例用模板引擎渲染,可根据你的框架调整) return self.render_template("index.html", data=outdata, current_page=page)
几个关键注意点
- 重复键处理:SCAN可能会返回重复的键,所以用
list(set(keys))去重(如果你的键是字符串格式,这个方法简单有效); - COUNT参数:设为比
per_page大一点(比如20),避免每次返回的键太少,需要多次扫描; - 数据一致性:如果Redis中的键频繁增删,分页结果可能会出现重复或遗漏,这是SCAN的特性,没办法完全避免;如果对一致性要求极高,可能需要额外维护一个有序集合来存储键,但会增加复杂度;
- 批量获取值:用
mget代替循环get,能大幅提升性能,尤其是数据量大的时候。
内容的提问来源于stack exchange,提问作者Magnotta
相关产品推荐
相关产品推荐

