Lucence 到 Elastic-Search
lucence就是倒排索引,很快。NB的文章,lucence在底层使用FST的存储结构,就是对数据进行了压缩。
这结构在项目中使用lucence,直接拿Util就可以了,就是使用NRTManager这个近实时的搜索类,方面实现lucence的搜索。有人问,什么时候重建索引,就添加文档,帖子的时候直接建立,这些都是在内存中进行的。
直观 近似实时搜索简单几句话说清楚Elastic-Search
了解集群
1:可能搜索的数据量太大,要多几个机器存放,把Lucence服务分布到几台服务上面。一个搜索请求所有的机器,结果合并回来。搜索量太大,你要netty吧,数据丢失,你要备份,增加机器,数据分割。这些都不用做了,这就是ES,Y就是干这个的。
使用ES,就是索引--类型--文档 同时考虑到集群,有shard和replica(备份)两种结点类型。直接安装,增删改查一下,你看到聚合的过程对你是透明的,感觉跟用一个lucence服务一样,json语法,马上就能入门了。负载均衡,路由,集群扩容,shard重分配都跟你透明。
ES就长这个样子
就这样里面的结点是shard和replica,但是,他们都提供服务。有master结点,但是,他只是维护一下集群信息而已。搜索请求会到所有的结点,每一个都能提供搜索和聚合功能。master不会成为瓶颈,换个名称更合适。结点对等,有点类似redis3.0的集群模式,但是,这里没有slave,都负载提供服务!
创建一个index进来的时候,默认是5个Primary Shard,每一个配一个Replica Shard。这个PrimaryShard是固定的,因为他的路由算法要使用这个基数,这个不能改变。
自动实现这种分配 3台机器的分配过程然后是集群的容错管理
集群就是这样!
集群的样子了解了,看一下
增删改查
1:增加的时候,自动生成的id,长度为20个字符,URL安全,base64编码,GUID,分布式系统并行生成时不可能会发生冲突。这个可以借鉴一下。
2:查询的时候,就是get/{}写你的json,match等,指定字段_source,这些都直接百度即可。
3:删除的时候,是懒删除。就是删除并没有真正的删除,只是标记了deleted标记,当我们创建越来越多的document的时候,es会在适当的时机在后台自动删除标记为deleted的document。
4:修改问题,这里有并发的问题。一句话吧,es使用版本号的乐观锁机制完成并发修改。
对查询+修改的时候,要在修改的时候,加上你查询的条件。
post /index/type/id/_update?retry_on_conflict=5&version=6 还提供了这种重试,就是我一直修改,连续5次还是没有修改成功,并发量太大呗,就修改失败,别浪费了。
批量增删改查
1:就一个命令_mget即可,就好像你mysql批量插入的时候,一条或者values()()(),效率高些。
2:批量修改_bulk。好像mysql里面的存储过程吧。bulk request会加载到内存里,如果太大的话,性能反而会下降,因此需要反复尝试一个最佳的bulk size。一般从1000~5000条数据开始,尝试逐渐增加。另外,如果看大小的话,最好是在5~15MB之间。就在内存里面玩呗。
到目前为止,我们就是类似对lucence一样,通过restful api+json的方式对es操作,感觉很简单,增删改查,扩容,集群。那他强大,可能就是数据组织的灵活,json格式,感觉没有硬货。
搜索的时候
一个查询搜索请求过来,到集群的任何一个结点上面,结点都是对等的。会怎样?默认会把doc的id,就是生成的GUID,作为路由编号。shard = hash(routing) % number_of_primary_shards,找到数据在的shard。这里也说了,primary shard数量不可变的谜底。我去,那这就是不能扩容呗?我增加一个Primary shard结点呢?只能重启集群吗?只能这样,这个问题也无解,一致性Hash也会有问题,redis数据分片,这里直接干脆重启吧要造成重建吗? 太弱了。
一个增删改请求来的时候,(1)客户端选择一个node发送请求过去,这个node就是coordinating node(协调节点)(2)coordinating node,对document进行路由,将请求转发给对应的node(有primary shard)(3)实际的node上的primary shard处理请求,然后将数据同步到replica node(4)coordinating node,如果发现primary node和所有replica node都搞定之后,就返回响应结果给客户端 这是全同步的方式呀?全同步?。这个同步操作,全同步从结点都搞定返回。异步直接返回,等都不等从。半同步,至少等一个从结点返回。Fuck,我以为这里是等待大多数从结点反馈呢?不是,es就是全同步的。put /index/type/id?consistency=quorum,他这个参数是说
没关系呀请求落到具体Doc上,为了效率
具体到docement上面,再细化一次,上面没有说负载均衡。
1、客户端发送请求到任意一个node,成为coordinate node。(Fuck,他们都是均等的)2、coordinate node对document进行路由,将请求转发到对应的node,此时会使用round-robin随机轮询算法,在primary shard以及其所有replica中随机选择一个,让读请求负载均衡。3、接收请求的node返回document给coordinate node。4、coordinate node返回document给客户端。5、特殊情况:document如果还在建立索引过程中,可能只有primary shard有,任何一个replica shard都没有,此时可能会导致无法读取到document,但是document完成索引建立之后,primary shard和replica shard就都有了。Fuck,如果落到了正在构建的replica shard就会说没有,就不能重定向一下吗?可能为了效率吧。
2:_bluk批量增删改操作对api有要求,最大的优势在于,不需要将json数组解析为一个JSONArray对象,形成一份大数据的拷贝,浪费内存空间,尽可能地保证性能。不能使用一般的json格式。
搜索图解
为了更快
1:超时,一个搜索请求打到所有的结点上面,聚合,这个过程可能很慢。我就指定,比如5s,在5s内你拿到多少返回多少就行了,不要影响UI显示。GET /_search?timeout=10m
2:一个搜索,结果肯定可能每一个分片都有的。他会达到一个shard的primary或者replica。
3:es是简单的,你期待的分页搜索,GET /_search?size=10&from=20 这Y根本就没有考虑深排序的问题,我要查询10000,100010,他也是内存聚合,返回。没有二次查询的优化等。
4:建立一个doc的时候,会给你来一个字段,_all field。GET /test_index/test_type/_search?q=test这种查询没有指定字段。{"name": "jack","age": 26,"email": "jack@sina.com","address": uamgzhou"}默认的还会给你加一个_all metadata:"jack 26 jack@sina.com guangzhou"就这样。
5:doc中的各个字段,会默认的哟一个mapping映射。对不同的字段,比如日期是不会分词的,对搜索有影响。GET /index/_mapping/type 查看mapping。这里有一个问题!复杂类型的mapping,我们的java类映射过来的时候,底层的存储,横向的存储转换为列式存储。
object底层6:搜索的时候,有同义词,近义词,复数,这些搜索,normalization,建立倒排索引的时候,会执行一个操作,也就是说对拆分出的各个单词进行相应的处理,以提升后面搜索的时候能够搜索到相关联的文档的概率。
7:他默认的分词器我们不感兴趣,就是IK中文分词器。
8:各种搜索自己百度,没有干货,keyword,不分词的搜索。
搜索相关
1:就是TF/IDF,就是在一个doc里面出现越多越好,但是,在多个doc里面出现越多越低。
2:搜索的结果排序,doc value(就是文档对应分词,这名字)在建立索引的时候,一方面会建立倒排索引,以供搜索用;一方面会建立正排索引,也就是doc values,以供排序,聚合,过滤等操作使用 es不仅仅是搜索,关键就是他的聚合下钻分析功能。
3:每一次的搜索结果都不一样,bouncing results问题,还起了个名字,就是你每一次的搜索都可能打到不同的shard上面。简单说,就是指定一下rote_id即可。
4:_scroll大数据的分页搜索,根据上一次的scroll_id继续搜索即可,这都不是什么知识。如果一次性要查出来比如10万条数据,那么性能会很差,此时一般会采取用scoll滚动查询,一批一批的查,直到所有数据都查询完处理完。scoll,看起来挺像分页的,但是其实使用场景不一样。分页主要是用来一页一页搜索,给用户看的;scoll主要是用来一批一批检索数据,让系统进行处理的
5:定制自己的分词器,百度,都不用google的。
6:type的底层存储。
就这mapping的时候,还可以指定type的策略,true:遇到陌生字段,就进行dynamic mapping ,false:遇到陌生字段,就忽略,strict:遇到陌生字段,就报错
倒排索引近实时搜索
1:不停机的重建索引,可能你一个index下面的type字段设置错了。就是不操作索引,操作索引的别名,真正的索引错误,新建索引,然后更改别名的指向,解决问题。先起个别名,操作别名。
有点干货
就是,写入内存中,刷到segment,很多很多的segment,写入系统缓存,写到磁盘才能搜索。实时就是在写入系统缓存这里就可以提供搜索了,osCahe聚集了大量的数据,防止,丢失又提供了translog,这和lucence是差不多类似的!