概要
本篇从介绍搜索分页为起点,简单阐述分页式数据搜索与原有集中式数据搜索思维方式的差异,就分页问题对deep paging问题的现象进行分析,最后介绍分页式系统top N的案例。
搜索分页语法
Elasticsearch中search语法有from和size两个参数用来实现分页的效果:
- size:显示应该返回的结果数量,默认是10。
- from:显示查询数据的偏移量,即应该跳过的初始结果数量,默认是0。
from和size这两个参数的含义和MySql使用limit关键字分页的参数含义是一样的。
举几个示例,查询第1-3页的请求:
GET /music/children/_search?size=10
GET /music/children/_search?size=10&from=10
GET /music/children/_search?size=10&from=20
分布式数据与集中式数据的差异
集中式数据存储方式,从最早的单体应用模式,到早期的SOA服务模式,那时存储大多数都是采用集中式数据存储,数据落地到mysql等关系型数据中,有支持读写分离,部署了多台数据库实例实现主-从结构的本质上也还是集中式存储。
在单数据库或主从数据库中,执行分页查询,统计排序等思路相对清晰,毕竟数据都完完整整地放在一起,直接挑一台实例搞就是了,可能就是容量有上限,出结果慢一些而已。
关系型数据库使用分布式数据存储的经典方案是分库分表,同一张表的数据,用一定的路由逻辑,拆分在不同的数据库实例里存储,此时做数据统计,就不能只关注一个实例了。
分布式数据存储方式,搜索思路就开始有了细微的改变,比如说Elasticsearch,索引的数据是拆分存储在各个shard里的,每个shard可能散布在ES集群的各个node上,这种情况下,做查询,统计分析等操作,虽然ES已经封装好了技术细节,我们仍然需要明白这是一个分布式储存的查询方案。
个人认为,分布式数据与集中式数据的处理差异,虽然在关系型数据库或ES方面,已经有成熟的框架对其进行封装,但使用者还是需要从思维上去理解分布式带来的改变,这样才能得到正确的结果。
deep paging问题
deep paging简单来说叫深度分页,就是搜索得特别深,显示第好几百页的数据。为什么说deep paging是有问题的?
我们假定索引内有20000条数据,存储在5个shard里,发送一个有条件和指定排序字段的查询请求,如果我要取第1页的数据,那么每个shard都取10条数据,汇总到Coordinate Node里,共50条,Coordinate Node对这50条数据再进行排序,滤掉后面的40条数据,只取最前面的
10条,返回给客户端。
如果是第1000页呢?
按老套路每个shard取10000-10010条,汇总到Coordinate Node里,还是50条,最后返回给客户端?
这么做就错啦,分布式数据不是这么查的,第1000页,在每个shard中不是取第10000-10010条,而是取前面10010,5个shard共取50050条给Coordinate Node,Coordinate Node汇总数据完成排序等操作后再取第10000-10010条,返回客户端10条数据。
费了这么大劲收集到50050条数据,实际给客户端的就10条,丢掉50040条数据,好费内存。
如果第10000页呢?这结果不忍直视
如果一个索引的分片数越多,需要汇总的数据就会成倍增长 ,可以看到分布式系统对结果排序分布的成本随深度呈指数上升,最重要的两个影响维度是分页深度和shard数量。这种重量级的查询,极有可能拖垮整个Elasticsearch集群,所以说搜索引擎对任何查询都不要返回超过1000个结果。
引申top N问题模型
deep paging的问题,通过优化搜索关键词,控制分页深度,问题能得到一定的改善,那top N问题如如何解决呢?
聚合查询中,经常能遇到查询最XX的10条记录这种分析需求,这种就是top N问题模型。
完美解决的场景
我们先举个熟悉的案例:统计播放量最高的10首的英文儿歌。
document数据结构:
{
"_index": "music",
"_type": "children",
"_id": "2",
"_version": 6,
"found": true,
"_source": {
"name": "wake me, shark me",
"content": "don't let me sleep too late, gonna get up brightly early in the morning",
"language": "english",
"length": "55",
"likes": 0
}
}
这个需求ES处理起来得心应手,有下面几个原因:
- 一个document只会存在于一个shard中
- 每个document数据里中有播放数量的统计值
有这上面几点的保证,查询时ES就可以放心大胆地在每个shard取播放数最高的前10条数据,Coordinate Node汇总的数据也就50条,此时性能非常高。
不宜直接查询的场景
上一小节依赖document的预先设计和shard存储数据的特性,避免了全索引扫描,性能特别高,假设系统中针对每天的播放点击,都有一个播放日志记录,记录着歌曲ID,点击人,点击时间,收听时长,时长百分比(与完整歌曲的百分比,有听到一半就退出不听了的,这个值就是50%),该document的数据示例:
{
"_index": "playlog-20191121",
"_type": "music",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"music_id": 1,
"listener": "tony wang",
"listen_date": "2019-11-21 15:35:00",
"music_length": 52,
"isten_percentage": 0.95
}
}
假设歌曲总量200万条,每天的播放日志1亿条,日志索引每天建立一个,primary shard数量为10,命名格式playlog-yyyyMMdd,需求是搜索当天的播放排行榜,取排名前10的记录。
如果直接统计,就只能硬扛了,基本过程如下:
- 每个shard根据music_id做分组统计,理论上单shard数量量最多200万条。
- Coordinate Node收集10个shard的数据,进行合并,数据处理量上限2000万条,最后合并成200万条。
- 从这200万条数据取前面10条,返回给客户端。
这个过程绝对是重量级,如果每次都实时统计的话,ES集群的压力可想而知。
改进方案
播放功能增加数据更新逻辑
预先增加按日期统计的索引数据结构,每次有用户点击播放时,额外发送一条更新消息将其数据更新,查询时直接从统计的索引里出结果,避免每次查询。定时任务统计数据
数据统计的需求,可以用定时任务进行计算,将计算结果存储起来,通过降低实时性,来避免全索引扫描计算的压力。
简单对比:
- 相同点:都是以空间换时间的做法,避免全索引扫描。
- 不同点:前者通过更改业务实现逻辑,增加数据级联更新,有一定的业务耦合性;后者将实时计算变定时任务,灵活性较高,与业务耦合性低,但实时性差。
补充一点
良好的数据结构设计可以很大程度地降低ES查询压力,提高实时查询的性能,但有一点需要接受:考虑得再周全的设计,也难适应千变万化的需求;需求变更是无法避免的,没有一劳永逸的方案。
小结
本篇从分页查询入手,阐述了deep paging的问题原因,并顺带将自己对分布式系统与集中式系统处理的思维差异做了简单描述,最后引申了top N场景的问题,上面提到的改进方案,只是针对比较简单的场景,实际生产要面临的情况肯定更复杂,比如采用分布式计算组件storm来解决top N 问题的,这里当做是抛砖引玉,欢迎各位分享自己的看法。
专注Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区
Elasticsearch系列---搜索分页和deep paging问题的更多相关文章
-
ES 25 - Elasticsearch的分页查询及其深分页问题 (deep paging)
目录 1 分页查询方法 2 分页查询的deep paging问题 1 分页查询方法 在GET请求中拼接from和size参数 // 查询10条数据, 默认从第0条数据开始 GET book_shop/ ...
-
36.分页及deep paging
主要知识点 1.es分页 2.deep paging 一.es分页语法 size,from 这两个关键字 GET /_search?size=10 指定每页10条数据 GET /_search ...
-
Elasticsearch系列---搜索执行过程及scroll游标查询
概要 本篇主要介绍一下分布式环境中搜索的两阶段执行过程. 两阶段搜索过程 回顾我们之前的CRUD操作,因为只对单个文档进行处理,文档的唯一性很容易确定,并且很容易知道是此文档在哪个node,哪个sha ...
-
ElasticSearch(十五) _search api 分页搜索及deep paging性能问题
1.分页搜索 语法: size,from GET /_search?size=10 GET /_search?size=10&from=0 GET /_search?size=10&f ...
-
游标 深度分页 deep paging
Solr Deep Paging(solr 深分页) - ickes的专栏 - CSDN博客 https://blog.csdn.net/xl_ickes/article/details/427725 ...
-
SAP UI 搜索分页技术
搜索分页技术往往和另一个术语Lazy Loading(懒加载)联系起来.今天由Jerry首先介绍S/4HANA,CRM Fiori和S4CRM应用里的UI搜索分页的实现原理.后半部分由SAP成都研究院 ...
-
S/4HANA和CRM Fiori应用的搜索分页实现
在我的博客Paging Implementation in S/4HANA for Customer Management 我介绍了S/4HANA for Customer Management里采用 ...
-
ElasticSearch入门-搜索(java api)
ElasticSearch入门-搜索(java api) package com.qlyd.searchhelper; import java.util.Map; import net.sf.json ...
-
Elasticsearch分布式搜索和数据分析引擎-ElasticStack(上)v7.14.0
Elasticsearch概述 **本人博客网站 **IT小神 www.itxiaoshen.com Elasticsearch官网地址 https://www.elastic.co/cn/elast ...
随机推荐
-
Linux- Nginx简单的负载均衡(一)
这里先进行简单的nginx负载,安装nginx这里就不多说了,我们情景假设在已经安装好了nginx上: 1)查询nginx中的upstrea负载均衡模块 默认是有安装的.进入nginx源码目录中 . ...
-
aircrack-ng test
Aircrack-ng工具包有很多工具,我用到的工具主要有以下几个: airmon-ng 处理网卡工作模式 airodump-ng 抓包 aircrack-ng 破解 aireplay-ng 发包,干 ...
-
C#设计模式(17)——观察者模式(Observer Pattern)
一.引言 在现实生活中,处处可见观察者模式,例如,微信中的订阅号,订阅博客和QQ微博中关注好友,这些都属于观察者模式的应用.在这一章将分享我对观察者模式的理解,废话不多说了,直接进入今天的主题. 二. ...
-
ASP.NET HTTP模拟提交通用类 GET POST
用法: WebRequestSugar ws = new WebRequestSugar(); //可选参数 //ws.SetAccept //ws.SetContentType //ws.SetC ...
-
【Java编程进阶-1】enum枚举的使用
枚举主要用于枚举常量,下面举个简单的应用. 比如一个公司有如下几个部门: 研发部: 销售部: 财务部: (其他部门暂时不列举) 部门的某些信息相对固定,此时可以考虑使用枚举来说明: 枚举类 Depts ...
-
树-伸展树(Splay Tree)
伸展树概念 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由Daniel Sleator和Robert Tarjan创造. (01) 伸展树属于二 ...
-
第四篇:web之前端之jquery
前端之jquery 前端之jquery 本节内容 jquery简介 选择器和筛选器 操作元素 示例 1. jquery简介 1 jquery是什么 jQuery由美国人John Resig创建,至 ...
-
Spring Security(19)——对Acl的支持
目录 1.1 准备工作 1.2 表功能介绍 1.2.1 表acl_sid 1.2.2 表acl_class 1.2.3 表acl_obj ...
-
ecshop收货地址货到付款修改
用户选择某些地址时,支付方式里则可以依据此地址来对货到付款选项进行显示或隐藏.目前仅与顺丰合作,以顺丰提供的数据为准. 使用到的数据库分别如下: ecs_region//地方数据,PRIMARY KE ...
-
Spring Security 入门(3-11)Spring Security 的使用-自定义登录验证和回调地址
配置文件 security-ns.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmln ...