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实际翻页要好的多的多。
缺点:
1 ES和solr都是作为全文搜索引擎出现的。都是基于Lucene的搜索服务器。
2 ES不是可靠的存储系统,不是数据库,它有丢数据的风险。
3 ES不是实时系统,数据写入成功只是trans log成功(类似于MySQL的bin log),写入成功后立刻查询查不到是正常的。因为数据此刻可能还在内存里而不是进入存储引擎里。同理,删除一条数据后也不是马上消失。写入何时可查询?ES内部有一个后台线程,定时将内存中的一批数据写入到存储引擎,此后数据可见。默认后台线程一秒运行一次。该线程运行的越频繁,写入性能越低。运行的频率越低,写入的性能越高(不会无限高)。
4 目前已知的单ES集群可以存储PB级别的数据,不过这个就非常费劲了。TB级别数据没压力。
5 如果使用ES官方提供的jar包访问,需要JDK17及以上。
6 使用对应的版本访问ES server。如果ES server端的版本是17,那么请使用ES 17的client。如果ES server是21,请使用21的client。
7 ES索引存在Linux服务器的文件系统之上(背后是文件系统,不是类似于HDFS的分布式文件系统)
8 ES Java client是线程安全的,全局构建一个即可满足读写需求,不要每次都创建ES client。每次访问ES都构建新的es client即会抛出次异常。
9 非常不建议使用ES的动态识别和创建的机制,因为很多情况下这并非你所需要。推荐的做法是在写数据之前仔细的创建mapping。
10 强烈不建议在ES中使用深分页。可能会导致集群不可用。
11 ES是静态分片,一旦分片数在创建索引时确定那么后继不能修改。
12 ES里提供了type,很多人以为type是物理表,一个type的数据是独立存储的;但是在ES内部并不是这样,type在ES内部仅仅是一个字段。所以在很多数据能分为独立index的情况下,不要放到一个index里用type去分。只有嵌套类和父子类的情况下使用type才是合理的。
13 ES并不提供原生的中文分词的能力。有第三方的中文分词的插件,比如ik等。Ik是个toy分词器,有严肃的分词需求的话,请在使用ES之前使用独立的分词器分好词后向ES写入。
14 ES中的index,首先会进行分片,每一个分片数据一般都会有自己的副本数据,ES分配分片的策略会保证同一个分片数据和自己的副本不会分配到同一个节点上。当集群中的某一节点宕机后,ES的master在ping该节点时通过一定的策略会发现该节点不存活;会开启ES的恢复过程
15 ES没有update的能力。所有的update都是标记删除老文档,然后重新insert一条新文档。
单个匹配termQuery
不分词查询 参数1: 字段名,参数2:字段查询值,因为不分词,所以汉字只能查询一个字,英语是一个单词
QueryBuilder queryBuilder=QueryBuilderstermQuery("fieldName", "fieldlValue");
分词查询,采用默认的分词器
QueryBuilder queryBuilder2 = QueryBuildersmatchQuery("fieldName", "fieldlValue");
多个匹配
不分词查询,参数1: 字段名,参数2:多个字段查询值,因为不分词,所以汉字只能查询一个字,英语是一个单词
QueryBuilder queryBuilder=QueryBuilderstermsQuery("fieldName", "fieldlValue1","fieldlValue2");
分词查询,采用默认的分词器
QueryBuilder queryBuilder= QueryBuildersmultiMatchQuery("fieldlValue", "fieldName1", "fieldName2", "fieldName3");
匹配所有文件,相当于就没有设置查询条件
QueryBuilder queryBuilder=QueryBuildersmatchAllQuery();
模糊查询常见的5个方法如下
1常用的字符串查询
QueryBuildersqueryStringQuery("fieldValue")field("fieldName");//左右模糊
2常用的用于推荐相似内容的查询
QueryBuildersmoreLikeThisQuery(new String[] {"fieldName"})addLikeText("pipeidhua");//如果不指定filedName,则默认全部,常用在相似内容的推荐上
3前缀查询 如果字段没分词,就匹配整个字段前缀
QueryBuildersprefixQuery("fieldName","fieldValue");
4fuzzy query:分词模糊查询,通过增加fuzziness模糊属性来查询,如能够匹配hotelName为tel前或后加一个字母的文档,fuzziness 的含义是检索的term 前后增加或减少n个单词的匹配查询
QueryBuildersfuzzyQuery("hotelName", "tel")fuzziness(FuzzinessONE);
5wildcard query:通配符查询,支持 任意字符串;?任意一个字符
QueryBuilderswildcardQuery("fieldName","ctr");//前面是fieldname,后面是带匹配字符的字符串
闭区间查询
QueryBuilder queryBuilder0 = QueryBuildersrangeQuery("fieldName")from("fieldValue1")to("fieldValue2");
//开区间查询
QueryBuilder queryBuilder1 = QueryBuildersrangeQuery("fieldName")from("fieldValue1")to("fieldValue2")includeUpper(false)includeLower(false);//默认是true,也就是包含
//大于
QueryBuilder queryBuilder2 = QueryBuildersrangeQuery("fieldName")gt("fieldValue");
//大于等于
QueryBuilder queryBuilder3 = QueryBuildersrangeQuery("fieldName")gte("fieldValue");
//小于
QueryBuilder queryBuilder4 = QueryBuildersrangeQuery("fieldName")lt("fieldValue");
//小于等于
QueryBuilder queryBuilder5 = QueryBuildersrangeQuery("fieldName")lte("fieldValue");
QueryBuildersboolQuery()must();//文档必须完全匹配条件,相当于and
QueryBuildersboolQuery()mustNot();//文档必须不匹配条件,相当于not
QueryBuildersboolQuery()should();//至少满足一个条件,这个文档就符合should,相当于or
在es的5x版本,keyword类型字段可以设置ignore_above ,表示最大的字段值长度,超出这个长度的字段将不会被索引,但是会存储。
设置message 的长度最长为20,超过20的不被索引,这里的不被索引是这个字段不被索引,但是其他字段有的话仍然被索引到。
下面造点数据
1)如果你做全部查询是可以查到超过ignore_above的doc的,如下图:
2)如果你用模糊匹配是搜索不到的(注意上面的数据最后带个1是21位下图是20位的)
3)用精确匹配前面20个仍然搜索不到
•对于只需要精确查询的字段,例如时间戳,应该设置为keyword。
•对需要进行全文检索的字段设置合理的分词器,不同的分词器查询效率相差较大。
合理地向Elasticsearch中进行数据索引时,也要注意以下几点:
有些聚合分析的算法,是很容易就可以并行的,比如说max
有些聚合分析的算法,是不好并行的,比如说,count(distinct),并不是说,在每个node上,直接就出一些distinct value,就可以的,因为数据可能会很多,假设图中的协调节点3百万个数据去重后还剩下100万distinct的数据,那么内存需要来存储这100万条数据,这是不可能的
es会采取近似聚合的方式,就是采用在每个node上进行近估计的方式,得到最终的结论,cuont(distcint),100万,1050万/95万 --> 5%左右的错误率
近似估计后的结果,不完全准确,但是速度会很快,一般会达到完全精准的算法的性能的数十倍
precision_threshold优化准确率和内存开销
brand去重,如果brand的unique value,在100个以内,小米,长虹,三星,TCL,HTL。。。
在多少个unique value以内,cardinality,几乎保证100%准确
cardinality算法,会占用precision_threshold 8 byte 内存消耗,100 8 = 800个字节
占用内存很小。。。而且unique value如果的确在值以内,那么可以确保100%准确
100,数百万的unique value,错误率在5%以内
precision_threshold,值设置的越大,占用内存越大,1000 8 = 8000 / 1000 = 8KB,可以确保更多unique value的场景下,100%的准确
field,去重,count,这时候,unique value,10000,precision_threshold=10000,10000 8 = 80000个byte,80KB
doc value正排索引
搜索+聚合 是怎么实现的?
假设是倒排索引实现的
倒排索引来实现是非常不现实的,因为我们搜索的那个字段search_field 有可能是分词的,这就需要去扫描整个索引才能实现聚合 *** 作,效率是及其低下的。
正排索引结构:
doc2: agg1
doc3: agg2
1万个doc --> 搜 -> 可能跟搜索到10000次,就搜索完了,就找到了1万个doc的聚合field的所有值了,然后就可以执行分组聚合 *** 作了
doc value原理
1、doc value原理
(1)index-time生成
PUT/POST的时候,就会生成doc value数据,也就是正排索引
(2)核心原理与倒排索引类似
正排索引,也会写入磁盘文件中,然后呢,os cache先进行缓存,以提升访问doc value正排索引的性能
如果os cache内存大小不足够放得下整个正排索引,doc value,就会将doc value的数据写入磁盘文件中
(3)性能问题:给jvm更少内存,64g服务器,给jvm最多16g
es官方是建议,es大量是基于os cache来进行缓存和提升性能的,不建议用jvm内存来进行缓存,那样会导致一定的gc开销和oom问题
给jvm更少的内存,给os cache更大的内存
64g服务器,给jvm最多16g,几十个g的内存给os cache
os cache可以提升doc value和倒排索引的缓存和查询效率
2、column压缩
doc1: 550
doc2: 550
doc3: 500
合并相同值,550,doc1和doc2都保留一个550的标识即可
(1)所有值相同,直接保留单值
(2)少于256个值,使用table encoding模式:一种压缩方式
(3)大于256个值,看有没有最大公约数,有就除以最大公约数,然后保留这个最大公约数
重点:
对分词的field,直接执行聚合 *** 作,会报错,大概意思是说,你必须要打开fielddata,然后将正排索引数据加载到内存中,才可以对分词的field执行聚合 *** 作,而且会消耗很大的内存
先修改 字段的fielddata属性为true,再查 就能查找到数据
当然,我们也可以使用内置field(keyword)不分词,对string field进行聚合,如果对不分词的field执行聚合 *** 作,直接就可以执行,不需要设置fieldata=true
分词field+fielddata的工作原理
doc value --> 不分词的所有field,可以执行聚合 *** 作 --> 如果你的某个field不分词,那么在index-time,就会自动生成doc value --> 针对这些不分词的field执行聚合 *** 作的时候,自动就会用doc value来执行
分词field,是没有doc value的。。。在index-time,如果某个field是分词的,那么是不会给它建立doc value正排索引的,因为分词后,占用的空间过于大,所以默认是不支持分词field进行聚合的
分词field默认没有doc value,所以直接对分词field执行聚合 *** 作,是会报错的
对于分词field,必须打开和使用fielddata,完全存在于纯内存中。。。结构和doc value类似。。。如果是ngram或者是大量term,那么必将占用大量的内存。。。
如果一定要对分词的field执行聚合,那么必须将fielddata=true,然后es就会在执行聚合 *** 作的时候,现场将field对应的数据,建立一份fielddata正排索引,fielddata正排索引的结构跟doc value是类似的,
但是只会讲fielddata正排索引加载到内存中来,然后基于内存中的fielddata正排索引执行分词field的聚合 *** 作
如果直接对分词field执行聚合,报错,才会让我们开启fielddata=true,告诉我们,会将fielddata uninverted index,正排索引,加载到内存,会耗费内存空间
为什么fielddata必须在内存?因为大家自己思考一下,分词的字符串,需要按照term进行聚合,需要执行更加复杂的算法和 *** 作,如果基于磁盘和os cache,那么性能会很差
我们是不是可以预先生成加载fielddata到内存中来???
query-time的fielddata生成和加载到内存,变为index-time,建立倒排索引的时候,会同步生成fielddata并且加载到内存中来,这样的话,对分词field的聚合性能当然会大幅度增强
以上就是关于ES大数据量下的查询优化全部的内容,包括:ES大数据量下的查询优化、怎么用spring获取es数据、es查询条件构造等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)