上亿数据怎么玩深度分页?兼容MySQL + ES + MongoDB

 面试题 & 真实经历

面试题:在数据量很大的情况下 , 怎么实现深度分页?
大家在面试时 , 或者准备面试中可能会遇到上述的问题 , 大多的回答基本上是分库分表建索引 , 这是一种很标准的正确回答 , 但现实总是很骨感 , 所以面试官一般会追问你一句 , 现在工期不足 , 人员不足 , 该怎么实现深度分页?
这个时候没有实际经验的同学基本麻爪 , So , 请听我娓娓道来 。
惨痛的教训首先必须明确一点:深度分页可以做 , 但是深度随机跳页绝对需要禁止 。
上一张图:
上亿数据怎么玩深度分页?兼容MySQL + ES + MongoDB

文章插图
 
你们猜 , 我点一下第142360页 , 服务会不会爆炸?
像MySQL , MongoDB数据库还好 , 本身就是专业的数据库 , 处理的不好 , 最多就是慢 , 但如果涉及到ES , 性质就不一样了 , 我们不得不利用 SearchAfter Api , 去循环获取数据 , 这就牵扯到内存占用的问题 , 如果当时代码写的不优雅 , 直接就可能导致内存溢出 。
为什么不能允许随机深度跳页从技术的角度浅显的聊一聊为什么不能允许随机深度跳页 , 或者说为什么不建议深度分页
上亿数据怎么玩深度分页?兼容MySQL + ES + MongoDB

文章插图
 
MySQL分页的基本原理:
SELECT * FROM test ORDER BY id DESC LIMIT 10000, 20;LIMIT 10000 , 20的意思扫描满足条件的10020行 , 扔掉前面的10000行 , 返回最后的20行 。如果是LIMIT 1000000 , 100 , 需要扫描1000100 行 , 在一个高并发的应用里 , 每次查询需要扫描超过100W行 , 不炸才怪 。
MongoDB分页的基本原理:
db.t_data.find().limit(5).skip(5);同样的 , 随着页码的增大 , skip 跳过的条目也会随之变大 , 而这个操作是通过 cursor 的迭代器来实现的 , 对于cpu的消耗会非常明显 , 当页码非常大时且频繁时 , 必然爆炸 。
ElasticSearch从业务的角度来说 , ElasticSearch不是典型的数据库 , 它是一个搜索引擎 , 如果在筛选条件下没有搜索出想要的数据 , 继续深度分页也不会找到想要的数据 , 退一步讲 , 假如我们把ES作为数据库来使用进行查询 , 在进行分页的时候一定会遇到max_result_window的限制 , 看到没 , 官方都告诉你最大偏移量限制是一万 。
查询流程:
  1. 如查询第501页 , 每页10条 , 客户端发送请求到某节点
  2. 此节点将数据广播到各个分片 , 各分片各自查询前 5010 条数据
  3. 查询结果返回至该节点 , 然后对数据进行整合 , 取出前 5010 条数据
  4. 返回给客户端
由此可以看出为什么要限制偏移量 , 另外 , 如果使用 Search After 这种滚动式API进行深度跳页查询 , 也是一样需要每次滚动几千条 , 可能一共需要滚动上百万 , 千万条数据 , 就为了最后的20条数据 , 效率可想而知 。
再次和产品对线俗话说的好 , 技术解决不了的问题 , 就由业务来解决!
在实习的时候信了产品的邪 , 必须实现深度分页 + 跳页 , 如今必须拨乱反正 , 业务上必须有如下更改:
  • 尽可能的增加默认的筛选条件 , 如:时间周期 , 目的是为了减少数据量的展示
  • 修改跳页的展现方式 , 改为滚动显示 , 或小范围跳页
滚动显示参考图:
上亿数据怎么玩深度分页?兼容MySQL + ES + MongoDB

文章插图
 
小规模跳页参考图:
上亿数据怎么玩深度分页?兼容MySQL + ES + MongoDB

文章插图
 
通用解决方案短时间内快速解决的方案主要是以下几点: