面试题 & 真实经历

面试题:在数据量很大的情况下,怎么实现深度分页?

大家在面试时,或者准备面试中可能会遇到上述的问题,大多的回答基本上是分库分表建索引,这是一种很标准的正确回答,但现实总是很骨感,所以面试官一般会追问你一句,现在工期不足,人员不足,该怎么实现深度分页?

这个时候没有实际经验的同学基本麻爪,So,请听我娓娓道来。

惨痛的教训

首先必须明确一点:深度分页可以做,但是深度随机跳页绝对需要禁止。

上一张图:

你们猜,我点一下第142360页,服务会不会爆炸?

MySQLMongoDB数据库还好,本身就是专业的数据库,处理的不好,最多就是慢,但如果涉及到ES,性质就不一样了,我们不得不利用 SearchAfter Api,去循环获取数据,这就牵扯到内存占用的问题,如果当时代码写的不优雅,直接就可能导致内存溢出。

为什么不能允许随机深度跳页

从技术的角度浅显的聊一聊为什么不能允许随机深度跳页,或者说为什么不建议深度分页

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

原分页SQL:

# 第一页
SELECT * FROM `year_score` where `year` = 2017 ORDER BY id limit 0, 20; # 第N页
SELECT * FROM `year_score` where `year` = 2017 ORDER BY id limit (N - 1) * 20, 20;

通过上下文关系,改写为:

# XXXX 代表已知的数据
SELECT * FROM `year_score` where `year` = 2017 and id > XXXX ORDER BY id limit 20;

没内鬼,来点干货!SQL优化和诊断 一文中提到过,LIMIT会在满足条件下停止查询,因此该方案的扫描总量会急剧减少,效率提升Max!

ES

方案和MySQL相同,此时我们就可以随用所欲的使用 FROM-TO Api,而且不用考虑最大限制的问题。

MongoDB

方案基本类似,基本代码如下:

相关性能测试:

如果非要深度随机跳页

如果你没有杠过产品经理,又该怎么办呢,没关系,还有一丝丝的机会。

SQL优化 一文中还提到过MySQL深度分页的处理技巧,代码如下:

# 反例(耗时129.570s)
select * from task_result LIMIT 20000000, 10; # 正例(耗时5.114s)
SELECT a.* FROM task_result a, (select id from task_result LIMIT 20000000, 10) b where a.id = b.id; # 说明
# task_result表为生产环境的一个表,总数据量为3400万,id为主键,偏移量达到2000万

该方案的核心逻辑即基于聚簇索引,在不通过回表的情况下,快速拿到指定偏移量数据的主键ID,然后利用聚簇索引进行回表查询,此时总量仅为10条,效率很高。

因此我们在处理MySQLESMongoDB时,也可以采用一样的办法:

  1. 限制获取的字段,只通过筛选条件,深度分页获取主键ID
  2. 通过主键ID定向查询需要的数据

瑕疵:当偏移量非常大时,耗时较长,如文中的 5s

最后

参考文章:MongoDB中文社区

感谢 @程大设计师 为我倾情设计的二维码

如果觉得对你有用的话,不要忘记点个赞啊~

上亿数据怎么玩深度分页?兼容MySQL + ES + MongoDB的更多相关文章

  1. 大数据学习[16]--使用scroll实现Elasticsearch数据遍历和深度分页[转]

    题目:使用scroll实现Elasticsearch数据遍历和深度分页 作者:星爷 出处: http://lxWei.github.io/posts/%E4%BD%BF%E7%94%A8scroll% ...

  2. 上千万或上亿数据(有反复),统计当中出现次数最多的N个数据. C++实现

    上千万或上亿的数据,如今的机器的内存应该能存下.所以考虑採用hash_map/搜索二叉树/红黑树等来进行统计次数. 然后就是取出前N个出现次数最多的数据了,能够用第2题提到的堆机制完毕. #inclu ...

  3. R语言操作mysql上亿数据量(ff包ffbase包和ETLUtils包)

    平时都是几百万的数据量,这段时间公司中了个大标,有上亿的数据量. 现在情况是数据已经在数据库里面了,需要用R分析,但是完全加载不进来内存. 面对现在这种情况,R提供了ff, ffbase , ETLU ...

  4. 30G 上亿数据的超大文件,如何快速导入生产环境?

    Hello,大家好,我是楼下小黑哥~ 如果给你一个包含一亿行数据的超大文件,让你在一周之内将数据转化导入生产数据库,你会如何操作? 上面的问题其实是小黑哥前段时间接到一个真实的业务需求,将一个老系统历 ...

  5. 搭建企业级实时数据融合平台难吗?Tapdata + ES + MongoDB 就能搞定

      摘要:如何打造一套企业级的实时数据融合平台?Tapdata 已经找到了最佳实践,下文将以 Tapdata 的零售行业客户为例,与您分享:基于 ES 和 MongoDB 来快速构建一套企业级的实时数 ...

  6. 网易java高级开发课程 面对上亿数据量,网易用啥技术?

  7. mysql千万级测试1亿数据的分页分析测试

    本文为本人最近利用几个小时才分析总结出的原创文章,希望大家转载,但是要注明出处 http://blog.sina.com.cn/s/blog_438308750100im0e.html 有什么问题可以 ...

  8. [翻译] C# 8.0 新特性 Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南) 【由浅至深】redis 实现发布订阅的几种方式 .NET Core开发者的福音之玩转Redis的又一傻瓜式神器推荐

    [翻译] C# 8.0 新特性 2018-11-13 17:04 by Rwing, 1179 阅读, 24 评论, 收藏, 编辑 原文: Building C# 8.0[译注:原文主标题如此,但内容 ...

  9. 生产环境zabbix3.2上亿的表数据通过表分区的方式进行历史数据清理

    生产环境zabbix3.2上亿的表数据通过表分区的方式进行历史数据清理 zabbix服务器经常报警io过载,在报警的时候发现是数据库在删除历史数据时耗时较长 数据库积攒了大量的历史数据信息,主要集中在 ...

随机推荐

  1. 深度解密 Go 语言之 sync.map

    工作中,经常会碰到并发读写 map 而造成 panic 的情况,为什么在并发读写的时候,会 panic 呢?因为在并发读写的情况下,map 里的数据会被写乱,之后就是 Garbage in, garb ...

  2. Android学习笔记长按事件的处理

    常见的长按事件 代码示例: @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedIns ...

  3. VS Code WebApi系列——3、发布

    上两篇已经实现了WebApi及基于jwt的Token设置,那么功能做完了,该发布WebApi了.为什么要对发布进行一下说明呢,因为是基于vscode和.netcore的发布,所以可能会遇到莫名奇妙的问 ...

  4. 移动 Ubuntu16.04 桌面左侧的启动器到屏幕底部_设置方法

    通过命令行,对 Launcher 的位置进行一下调整. 按下 Ctrl + Alt + t 键盘组合键,调出终端,在终端中输入以下命令: gsettings set com.canonical.Uni ...

  5. 基于web网站项目的性能测试结果分析

    业务背景: 最近公司研发了一款对并发要求比较高的web项目,需要对其压力测试,模拟线上可能存在的问题,这个过程中遇到一些很多问题,这里重新梳理一下思路,希望能给遇到同样问题的小伙伴提供一个参考. 工具 ...

  6. Quaternion:通过API对Quaternion(四元数)类中的方法属性初步学习总结(二)

    1.RotateTowards方法 RotateTowards(From.rotation,To.rotation,fspeed) 个人理解:使From的rotation以floatspeed为速度, ...

  7. Spring mvc 面试

    Spring工作原理及其作用 1.springmvc请所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责负责对请求进行真正的处理工作. 2.DispatcherSer ...

  8. java scoket Blocking 阻塞IO socket通信三

    在NIO同步非阻塞的场景中和原来同步阻塞最大的却别就是引入了上面的Buffer对象,现在我们来学校上面的BUffer对象 我们来看看程序的代码: package bhz.nio.test; impor ...

  9. PHPstudy 2018 集成环境项目配置虚拟域名访问

    1.首先启动PHPstudy2018,并停止它 2.点击“其他选项菜单->站点域名管理” 3.设置站点域名.项目目录,点击“新增”,再点击“保存并生成配置文件” 4.生产配置文件之后会重启,然后 ...

  10. Alink漫谈(八) : 二分类评估 AUC、K-S、PRC、Precision、Recall、LiftChart 如何实现

    Alink漫谈(八) : 二分类评估 AUC.K-S.PRC.Precision.Recall.LiftChart 如何实现 目录 Alink漫谈(八) : 二分类评估 AUC.K-S.PRC.Pre ...