ElasticSearch

概率检索模型和BM25

BIM模型的推导:从P(R|D,q)/P(NR|D,q)推导,用的贝叶斯公式,最后转换成P(D|R,q)/P(D|NR,q), 再根据词项独立假设, 把P(D|R,q)拆分成P(qt1|R,q)*(1-P(qt2|R,q))*P(qt3|R,q)*..., (假设D里面包含查询词1和3,不包含查询词2) P(qt1|R,q)是指query词1在相关文档里的出现概率,(1-P(qt2|R,q))是指词2在相关文档里不出现的概率; P(qt1|R,q)=r1/R, R是相关文档的个数,r1是相关文档里出现过qt1的文档个数; P(D|NR,q)同理;

BM25模型:=BIM*query词在该文档中的重要性(考虑了词频和文档长度, 类似TF)*query词在该query中的重要性(考虑了词频, 类似TF);  BIM中的R和ri如果设为0,则化简为类似IDF; (我的感受:query和该文档碰上的词,计算:该词在文档集的IDF*该词在query的TF做压制*该词在该文档的TF做压制!);淘宝的例子 里有一个例子把所有参数代入进去了,一目了然;

BM25考虑了4个因素:IDF因子,文档长度因子,文档词频因子和查询词频因子;  文档词频因子归一化的解释: 每一个词对于文档相关性的分数不会超过一个特定的阈值,当词出现的次数达到一个阈值后,其影响不再线性增长,而这个阈值会跟文档本身长度有关。

和TF-IDF类似,都是字面匹配,没有LDA基于共现关系的语义模型号,更没有基于监督信息的DSSM提取的语义好;

 

优化干货

建索引性能优化:1. 批量提交; 2. SSD; RAID0(不需要带备份功能的RAID); 3. 增大内存缓冲区refresh至segment文件的时间间隔(segment文件flush至磁盘的频率较低,一般是30分钟;挂了靠translog进行恢复,translog fsync至磁盘的频率高,默认5秒),这样可以减少; 4.减少副本数量;

查询效率优化:1. 路由,让关联性强的数据放到同一个分片上,查询的时候不用所有分片都查再Merge了;2.多用Filter少用Query,前者不需要计算评分,而且可缓存;3.避免大翻页,from+size适合头几页,如果经常查后面的,可用快照scroll方式;

JVM设置:尽量让JVM内存不要超过32GB,否则指针变成64位比32位浪费内存;

 

enabled, index, store: 这三个属性,分别决定"是否建立用于查询或聚合的索引","是否建立用于搜索的索引",“是否存储(不存储的话也可以在_source里得到它)”

IKAnalyzer: 好用的分词插件;可自定义词库,自定义停用词词典;

Tika: 好用的文件内容提取库(可自动判断文件类型)

Head插件:UI方式查看ES集群和数据;

RESTful接口来操作ES

 

分片:索引默认分成这么多份子集,彼此不相交(默认5个分片,0~4号)

副本:整份索引的副本数量,1表示除了原本还有1份副本,0表示只有原本(默认1个副本)

客户端过来新文档写入请求后,这个ES节点先用hash(_id)%shards_num来得知路由到哪个分片,然后根据集群信息得到该分片在哪个节点上,然后把请求转发给那个节点(A);那个节点(A)完成写入索引过程,完成后把写数据转发给所有副本节点;有1~N(可配置)个副本写入完成的ACK回来后,A节点才会给客户端返回成功ACK;

 

悲观锁:传统锁,适用于写操作较多的情况;

乐观锁:适用于读多写少的情况;用版本号控制一致性;客户端写操作传过来的版本号如果小于服务器数据当前版本,则返回失败,一致则写成功并版本号自动加一;基于版本号实现乐观锁的例子

 

routing参数,可以指定文档的hash_key,(slice_id <== hash(hash_key) % slice_num), 经常会被一起查询出来的文档可以指定相同的routing参数,以使得这种查询只被路由到一个分片上,节省网络和系统延迟;创建文档和查询文档时都要指定这个参数才行;默认的话是按照_id来做hash_key,查询的时候向所有分片发送查询请求,最后Merge;

 

text字段和keyword字段的区别:text字段会被分词然后建立索引;keyword不会分词,直接建立索引;_analyze命令查看文档字段的索引key情况;text用于可全文检索的;keyword用于过滤、排序、聚合;

用"analyzed"属性控制是否全文索引(是否分词)

geo_point和geo_shape是对地理位置等数据的支持;

哪些字段支持索引(查找),哪些字段不支持:enabled, index, store这几个的区别;

 

term代表完全匹配,也就是精确查询,搜索前不会再对搜索词进行分词,所以我们的搜索词必须是文档分词集合中的一个

match查询会先对搜索词进行分词,分词完毕后再逐个对分词结果进行匹配; 可以按照分词后的分词集合的or或者and进行匹配,默认为or,这也是为什么默认只要有一个分词出现在文档中就会被搜索出来

match_phrase为按短语搜索; 查询先分词,要求所有分词必须在文档中出现(像不像operator为and的match查询),除此之外,还必须满足分词在文档中出现的顺序和搜索词中一致, 且各搜索词之间必须紧邻(可用slop参数控制最大间隔)

multi_match可以搜索多个字段;多个字段对评分都会有印象,权重可以用例如"^3"来指定。

详细解释

fuzzy: 模糊查询,查询词和文档关键词的“编辑距离”越小越排前面;

 

过滤:只按条件过滤,不产生评分不影响排序;

ES默认使用BM25评分模型;在每一个分片上单独打分,所以分片的数量和哈希映射会影响打分的结果;

分词器也会影响打分结果,因为不同的分词器会分成不同的词,对应倒排索引key就不同了;

 

Java Client一定要和ES节点具有相同的版本!

TransportClient打开某选项可以自动探测到集群里节点挂掉或新节点加入这种变化;

"双重加锁单例模式":判空-->加锁-->判空-->创建;声明static全局变量的时候,要用volatile关键字禁止编译器优化!

 

master节点:负责集群调度;

data节点:负责存储分片数据;

client节点:负责处理用户请求,实现请求转发,负载均衡

配置成master为true,表示有资格参与主节点选举(最后只有一个成为主节点);

默认是每个节点3个角色都有;建议master节点转做master,data节点专放data,专机专用;

参考

 

有很多参数,控制分片负载均衡和分片恢复等任务的密集度不要太高,防止影响正常查询搜索功能;参考

分片可以设置对Rack的感知,node设置自己的rack_id,则分片及其副本不会自动分配到同一个rack上(防灾);

每个node可以设置自己的tag; 索引可以设置过滤,让自己的分片只分配到具有某种tag的node上,或者让自己的分片不要分配到具有某种tag的node上;可以借助这个功能实现读写分离,即设置几个node的tag是hot, 其他node的tag是stale, 当天的新索引设置tag为hot只放到hot的node上,大量的查询都是读的放老文档的node们(stale),每天凌晨低负载的时候用定时任务来将这天新索引的tag更改为stale,使其分片迁移到stale机器上去

 

query和filter:

1. query是要相关性评分的, filter不要;2.query的结果无法缓存, filter可以缓存;

1. 全文搜索、评分排序,使用query;   2. 是非过滤、精确匹配,使用filter;

 

query_then_fetch和DFS_query_then_fetch

1. 发送查询到每个shard

2. 找到所有匹配的文档,并使用本地的Term/Document Frequency信息进行打分

3. 对结果构建一个优先队列(排序,标页等)

4. 返回关于结果的元数据到请求节点。注意,实际文档还没有发送,只是分数

5. 来自所有shard的分数合并起来,并在请求节点上进行排序,文档被按照查询要求进行选择

6. 最终,实际文档从他们各自所在的独立的shard上检索出来

7. 结果被返回给用户
当每个分片里的文档数量过少时,自己分片上建立的TF/IDF评分就很不准确;所以可以使用策略DFS_query_then_fetch, 这个过程基本和Query Then Fetch类似,除了它执行了一个预查询来计算整体文档的frequency

 

ElasticSearch索引实现原理

1. Term Index --> Term Dictionary --> Posting List; 

2. TermIndex是为了快速查找到某个term而建立的索引结构(数量远小于term数量),可以是前缀树实现,可以是FST技术压缩成自动机来实现;(也可以是多级调表)

3. Posting List的压缩存储1: 增量编码压缩(A+B): A. Delta-encode,第一个数存本身,后面每个数只存和前一个数的delta; B. 分成Block, 每个Block存N个数,每个Block第一个byte存本block内每个数用多少bit表示,后面存所有的数;

4. Posting List的压缩存储2:Roaring Bitmaps技术:老版本lucence使用bitmap存储Posting List,缺点是文档数目多的话要用的内存较大,即使其中全是0(即本term只在很少几个Doc中出现)。Roaring Bitmaps使用分块,docID/65536==>BlockID, docID%65536==>OffsetInBlock, 每个Block根据其中文档的数目(比如以4096为界), 大于界的就用Bitmap实现(65536个bit即8192Bytes), 小于界的就用数组实现(每个doc用2个byte存OffsetInBlock,<=4096*2B=8192Bytes)

5. 跳表: Posting List上建一两层"稀疏链表",加快查找速度;求交时用到

6. Bitset求交: 如果2个Term求交时,对应Block用的是Bitset实现,可直接按位与;

7. 文档内容是存储在磁盘上的,文档是按照主ID顺序存储的,查询时如果经常跳跃读取文档,会造成寻道开销(SSD硬盘请忽略。。。),所以主ID的设计尽量要考虑常用查询,查询出来的文档尽量让按主ID连续排着为好!

 

底层Lucene:

准实时索引:新收到的文档数据(默认最多攒一秒),会写到新的索引文件里;

一个索引就是一个独立目录,一个索引由多个segment构成(每个segment的文件前缀都相同),每个segment由多个不同类型的文件构成;

新文档到来后,先在内存buffer建立索引结构形成一个新segment,然后写到文件缓冲区(其实还是内存),然后Lucene就可以检索这个semgment了; 文件缓冲区被后台线程flush到磁盘上之后,commit文件更新,这才稳了;

新文档的到来信息,会存储在Translog中(append式的),如果segment的文件缓冲区没有flush至磁盘机器就挂了,那么会用Translog来恢复这个semgment的重建;flush至磁盘且commit文件更新后(稳了),Translog自动清空;

Translog文件自身的flush频率较高(默认5秒),磁盘挂了也能容忍;忍不了的话就打开副本功能(啥用?)

后台线程负责把零散的segment文件们归并成大segment文件;线程个数默认是CPU核的一半且最多3个;默认磁盘带宽占用20MB/s,对于SSD可以调高这个;多路归并,默认最多10路;

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值