ES大数据量下的查询优化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES大数据量下的查询优化相关的知识,希望对你有一定的参考价值。

参考技术A

filesystem类似于我们在mysql上建立一层redis缓存;

es的搜索引擎严重依赖于底层的filesystem cache,如果给filesystem cache更多的内存,尽量让内存可以容纳所有的indx segment file索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。

两者差距非常大,走磁盘和走systenfile cache的读取的性能差距可以说是秒级和毫秒级的差距了;

要让es性能要好,最佳的情况下,就是我们的机器的内存,至少可以容纳你的数据量的一半

最佳的情况下,是仅仅在es中就存少量的数据,存储要用来搜索的那些索引,内存留给filesystem cache的,如果就100G,那么你就控制数据量在100gb以内,相当于是,你的数据几乎全部走内存来搜索,性能非常之高,一般可以在1秒以内

的少数几个字段就可以了,比如说,就写入es id name age三个字段就可以了,然后你可以把其他的字段数据存在mysql里面,我们一般是建议用 es + hbase 的一个架构。
hbase的特点是适用于海量数据的在线存储,就是对hbase可以写入海量数据,不要做复杂的搜索,就是做很简单的一些根据id或者范围进行查询的这么一个操作就可以了

如果确实内存不足,但是我们又存储了比较多的数据,比如只有30g给systemfile cache,但是存储了60g数据情况,这种情况可以做数据预热;

我们可以将一些高频访问的热点数据(比如微博知乎的热榜榜单数据,电商的热门商品(旗舰版手机,榜单商品信息)等等)提前预热,定期访问刷到我们es里;(比如定期访问一下当季苹果旗舰手机关键词,比如现在的iphone12)

对于那些你觉得比较热的,经常会有人访问的数据,最好做一个专门的缓存预热子系统,就是对热数据,每隔一段时间,提前访问一下,让数据进入filesystem cache里面去。这样下次别人访问的时候,一定性能会好一些。

我们可以将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在filesystem os cache里,别让冷数据给冲刷掉。

尽量做到设计document的时候就把需要数据结构都做好,这样搜索的数据写入的时候就完成。对于一些太复杂的操作,比如join,nested,parent-child搜索都要尽量避免,性能都很差的。

es的分页是较坑的 ,为啥呢?举个例子吧,假如你每页是10条数据,你现在要查询第100页,实际上是会把 每个shard上存储的前1000条数据都查到 一个协调节点上,如果你有个5个shard,那么就有5000条数据,接着 协调节点对这5000条数据进行一些合并、处理,再获取到最终第100页的10条数据。

因为他是分布式的,你要查第100页的10条数据,你是不可能说从5个shard,每个shard就查2条数据?最后到协调节点合并成10条数据?这样肯定不行,因为我们从单个结点上拿的数据几乎不可能正好是所需的数据。我们必须得从每个shard都查1000条数据过来,然后根据你的需求进行排序、筛选等等操作,最后再次分页,拿到里面第100页的数据。

你翻页的时候,翻的越深,每个shard返回的数据就越多,而且协调节点处理的时间越长。非常坑爹。所以用es做分页的时候,你会发现越翻到后面,就越是慢。

我们之前也是遇到过这个问题,用es作分页,前几页就几十毫秒,翻到10页之后,几十页的时候,基本上就要5~10秒才能查出来一页数据了

你系统不允许他翻那么深的页,或者产品同意翻的越深,性能就越差

如果是类似于微博中,下拉刷微博,刷出来一页一页的,可以用scroll api
scroll api1 scroll api2
scroll会一次性给你生成所有数据的一个快照,然后每次翻页就是通过游标移动 ,获取下一页下一页这样子,性能会比上面说的那种分页性能也高很多很多

scroll的原理实际上是保留一个数据快照,然后在一定时间内,你如果不断的滑动往后翻页的时候,类似于你现在在浏览微博,不断往下刷新翻页。那么就用scroll不断通过游标获取下一页数据,这个性能是很高的,比es实际翻页要好的多的多。

缺点:

大数据量下的分页查询优化技巧

上个月 负责公司人群包的查询 毕竟主产品上亿的注册量,分页查询 查到10W后 就会很慢,
上次 写了一个 sql 语句 导出一个中低活 人群包就整了 两个小时 可以见得数据之大 普通的sql 语句就肯定需要尽可能优化优化
最后是用 限定 id 做了查询优化,
看了几篇 博客 刚好现在可以 整理一下 数据库 分页查询的优化技巧
基本上全网 都是这么写的 我简化了一下保留 我想记住的内容
《大数据量下的分页查询优化》

一般分页查询

这个就是 大家在 初学SQL 语句的时候 都会学习的 limit 语句基础用法

SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset

LIMIT 子句可以被用于指定 SELECT 语句返回的记录数。需注意以下几点:

  1. 第一个参数指定第一个返回记录行的偏移量,注意从0开始
  2. 第二个参数指定返回记录行的最大数目
  3. 如果只给定一个参数:它表示返回最大的记录行数目
  4. 第二个参数为 -1 表示检索从某一个偏移量到记录集的结束所有的记录行
    初始记录行的偏移量是 0(而不是 1)

还是用最常用的 学生表 举例子吧

select * from students where class_id=8 order by id limit 10000,10;

这种分页查询方式会从数据库第一条记录开始扫描,所以越往后,查询速度越慢,而且查询的数据越多,也会拖慢总查询速度。

使用子查询优化

使用 select id 代替 select * 速度增加了3倍
这种方式假设数据表的id是连续递增的

select * from students where class_id=8limit 100000,1;

select id from students where class_id=8 limit 100000,1;

select * from students where class_id=8 and 
id>=(select id from students where class_id=8 limit 100000,1) 
limit 100;

select * from students where class_id=8 limit 100000,100;

使用 id 限定优化

这种方式假设数据表的id是连续递增的
限于 明确知道id 但是速度 会更大的优化

between… and.有局限性,不能增加过滤条件,否则很容易造成id不连续,这样得到的结果只会是正确结果的子集

写法一

select * from students where class_id=8
and id between 1000000 and 1000100 limit 100;

写法二

select * from students where class_id=8 and id >= 1000001 limit 100;

这种 in 查询的方式要注意:某些 mysql 版本不支持在 in 子句中使用 limit。

使用临时表优化

这个不太懂 目前也没用到过

这种方式已经不属于查询优化,这儿附带提一下。

对于使用 id 限定优化中的问题,需要 id 是连续递增的,
但是在一些场景下,比如使用历史表的时候,或者出现过数据缺失问题时,可以考虑使用临时存储的表来记录分页的id,
使用分页的id来进行 in 查询。
这样能够极大的提高传统的分页查询速度,尤其是数据量上千万的时候。

关于数据表的id说明

分库存储的大表 就需要 ID 生成器了

一般情况下,在数据库中建立表的时候,强制为每一张表添加 id 递增字段,这样方便查询。

如果像是订单库等数据量非常庞大,一般会进行分库分表。这个时候不建议使用数据库的 id 作为唯一标识,而应该使用分布式的高并发唯一 id 生成器来生成,并在数据表中使用另外的字段来存储这个唯一标识。

先通过范围查 id 再通过id 拿数据

使用先使用范围查询定位 id (或者索引),然后再使用索引进行定位数据,能够提高好几倍查询速度。即先 select id,然后再 select *。

《大数据量下的分页查询优化》https://segmentfault.com/a/1190000038856674

以上是关于ES大数据量下的查询优化的主要内容,如果未能解决你的问题,请参考以下文章

大数据量下的分页查询优化技巧

技术浅谈大数据量下数据库查询插入的优化

总结MySQL大数据量下如何进行优化

mysql大数据量下优化

彻底搞明白大数据量下Mysql的分页优化

java后台大数据量下的分批入库