项目背景
- 需求:运营后台的分页功能(基于时间线的 审核 和 管理页面)
- 项目场景:类似 朋友圈 的动态发布,会有大量朋友圈(图文/视频)发布,数量会达 几十W~百万级。
- 技术框架:Django+Vue做前后端分离。
方案
方案1
- 简介:第一次查询时,取出所有数据(符合查询条件的数据),然后进行分页 并放入本地缓存,得到一个缓存Key(Page Key),返回给前端。前端需要查询分页结果时,直接使用该Page Key和页码 从内存缓存中 取出。为保证 同一个后台账户可以在多个页面同时进行分页,Page Key根据时间戳生成,利用LRU机制,保证内存占用的上限。
- 优点:思维直观,查看分页结果快。
- 缺点:
- 第一次查询大量数据,而且 生成缓存很大,特别是单词查询范围为 几十万条时,这次缓存可能达十几MB。
- 因为进程间不能共享内存,导致 Django项目无法利用多进程进行并发,只能多线程。
Redis改进方案
- 利用MemCache/Redis等第三方缓存服务,使 多个运行Django的进程可以使用 缓存。
- 缺点:
- Redis的String类型最大只能存500MB的字符串,换算为本项目 即为200W条朋友圈。
- 体积很大的数据需要从Redis取出,io慢。
- 为保证 同一个后台账户同时使用分页,Redis需要单独配置LRU,或 利用 过期时间,会导致无效缓存占用大量空间。(可通过访问时生成 特定的cookie或session,而不是 时间戳区分Page Key,覆盖上次分页缓存,节约空间)
粘性会话改进方案
- 粘性访问:服务器会把某个用户的请求,每次交给集群中的同一个节点。
- Gunicorn无该功能,uWSGI有。即可使用 多进程 + 内存缓存 分页结果。
方案2
- 简介:直接根据页码,利用SQL的
LIMIT
与OFFSET
语句取对应页码的数据,模拟分页效果。 - 难点:
- 此项目中不断会有大量新增的朋友圈,会把 第一页的数据 寄到第二页,因此发生混乱。可以朋友圈的自增id,每次分页时记录当前的最大id,前端回传改id时,即可避免新数据的干扰,保证分页的准确。
- 在操作会改变数据属性时,操作后直接刷新当前页,避免分页结果混乱。
- 优点:每次只取少量数据,接口响应快。
- 缺点:每次请求直接到MySQL,无法满足高并发和大访问量的接口,但后台无此情况。
思考
- 面向客户端的需要高并发的接口,直接瀑布式分页即可,每次请求带id,拿该id之后的一批即可。