ES知识点

时间:2024-03-22 17:57:20

Elasticsearch是个开源分布式搜索引擎,提供搜集、分析、存储数据三大功能。它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。在ES中,索引(index)指的是具有相同属性的文档的集合,每个索引(index)包含多个类型(type),每个类型又包含多个文档(document),每个文档包含了多个字段(field)。如果跟关系型数据库的结构对照的话,大致可以如下表示:

Elasticsearch

关系型数据库

Index

Database

Type

Table

Document

Row

Field

Column

Elasticsearch能够对全文本进行快速的搜索,得益于其实用的一种数据结构:倒排索引(Inverted Index)。在倒排索引中,包含一个在所有文档中出现的字词的列表,其中,每一个字词又对应一个列表,这个列表的内容就是所有包含这个字词的文档。当搜索时,首先会对我们搜索的内容进行分词处理,然后检查每个分好的词对应哪些文档,最后汇总给我们结果。

******************REST API*********************
#查询集群健康状况及当前节点
curl –XGET "http://192.168.1.14:9200/_cluster/health?pretty"
curl 'http://192.168.1.14:9200/?pretty'
#查询集群节点信息
curl –XGET "http://192.168.1.14:9200/_cat/nodes?v"
#查询索引及条数
curl –XGET "http://192.168.1.14:9200/_cat/indices?v"
#新建索引并写入
curl -XPOST http://192.168.1.14:9200/dt-2018.7/employee/ -d '{"name" : "xiaoming","age":"26","@timestamp":"2018-07-26 13:00:00"}' 
#删除对应的索引
curl –XDELETE "http://192.168.1.14:9200/dt-2018.07"
#查询索引下的所有文档/条件查询
curl -XGET 'http://192.168.1.36:9200/csv1/_search?pretty' -d '{"query": { "match_all": {} }}'
curl -XGET 'http://192.168.1.14:9200/dt-2018.7/_search?pretty' -d '{"query": { "match": { "age": 20 } }}'
#清除缓存
http://10.22.2.201:9200/*/_cache/clear
#设置分片和副本
curl -XPUT http://192.168.1.19:9200/myindex -d'
{
    "settings" : {
        "index" : {
            "number_of_shards" : 3, 
            "number_of_replicas" : 1 
        }
    }
}'

 

******************启动*********************
启动es    ./bin/elasticsearch -d
启动head  elasticsearch-head/node_modules/grunt/bin/grunt server &
启动kibana bin/kibana &
nohup $ES_HOME/../kibana-5.6.10/bin/kibana > /dev/null &

******************界面*********************
192.168.1.14:9200
192.168.1.14:9100
192.168.1.14:5601

 

集群结点角色分配:es集群中得三个角色 master ,data,client

master结点:该node服务器只作为一个主节点,但不存储任何索引数据,该node服务器将使用自身空闲得资源,来协调各种创建索引请求或者查询请求,将这些请求合理分发到相关得node服务器上。

data结点:该node服务器只作为一个数据节点,只用于存储索引数据。使该node服务器功能 单一,只用于数据存储和数据查询,降低其资源消耗率。

client结点(负载均衡结点):该node服务器即不会被选作主节点,也不会存储任何索引数据。该服务器主要用于查询负载均衡。在查询的时候,通常会涉及到从多个node服务器上查询数据,并请求分发到多个指定的node服务器,并对各个node服务器返回的结果进行一个汇总处理, 最终返回给客户端。

green

每个索引的primary shard和replica shard都是active状态的

yellow

每个索引的primary shard都是active状态的,但是部分replica shard不是active状态,处于不可用的状态

red

不是所有索引的primary shard都是active状态的,部分索引有数据丢失了

为什么现在会处于一个yellow状态?

我们现在就一个笔记本电脑,就启动了一个es进程,相当于就只有一个node。现在es中有一个index,就是kibana自己内置建立的index。由于默认的配置是给每个index分配5个primary shard和5个replica shard,而且primary shard和replica shard不能在同一台机器上(为了容错)。现在kibana自己建立的index是1个primary shard和1个replica shard。当前就一个node,所以只有1个primary shard被分配了和启动了,但是一个replica shard没有第二台机器去启动。

 

ES性能优化

1.节点优化

关闭data结点得http功能、一台服务器上最好只部署一个node、master和data分离

关闭data结点得http功能

    针对ElasticSearch集群中的所有数据节点,不用开启http服务。将其中的配置 参数这样设置:http.enabled: false,同时也不要安装head, bigdesk, marvel等监控 插件,这样保证data节点服务器只需处理创建/更新/删除/查询索引数据等操作。

    http功能可以在非数据节点服务器上开启,上述相关的监控插件也安装到这些服 务器上,用于监控ElasticSearch集群状态等数据信息。这样做一来出于数据安全考虑,二来出于服务性能考虑。

 一台服务器上最好只部署一个node

一台服务器上的CPU,内存,硬盘等资源毕竟有限,从服务器性能考虑,不建议一台服务器上启动多个node节点

2.分片策略

节点数<=主分片数*(副本数+1)

3.路由优化

相同路由属性的文档,一定会被分配到同一个分片上,无论是主分片还是副本。

ES中所谓的路由和IP网络不同,是一个类似于Tag的东西。在创建文档的时候,可以通过字段为文档增加一个路由属性的Tag。ES内在机制决定了拥有相同路由属性的文档,一定会被分配到同一个分片上,无论是主分片还是副本。那么,在查询的过程中,一旦指定了感兴趣的路由属性,ES就可以直接到相应的分片所在的机器上进行搜索,而避免了复杂的分布式协同的一些工作,从而提升了ES的性能。于此同时,假设机器1上存有路由属性A的文档,机器2上存有路由属性为B的文档,那么我在查询的时候一旦指定目标路由属性为A,即使机器2故障瘫痪,对机器1构不成很大影响。

4.内存设置

不要超过32G、预留一半内存给Lucene、避免内存交换

  1):预留一半内存给Lucene使用

    一个常见的问题是配置堆太大。你有一个64 GB的机器,觉得JVM内存越大越好,想给Elasticsearch所有64 GB的内存。

    当然,内存对于Elasticsearch来说绝对是重要的,用于更多的内存数据提供更快的操作。而且还有一个内存消耗大户-Lucene

    Lucene的设计目的是把底层OS里的数据缓存到内存中。Lucene的段是分别存储到单个文件中的,这些文件都是不会变化的,所以很利于缓存,同时操作系统也会把这些段文件缓存起来,以便更快的访问。

    Lucene的性能取决于和OS的交互,如果你把所有的内存都分配给Elasticsearch,不留一点给Lucene,那你的全文检索性能会很差的。

    最后标准的建议是把50%的内存给elasticsearch,剩下的50%也不会没有用处的,Lucene会很快吞噬剩下的这部分内存。

  2):32GB限制

    在java中,所有的对象都分配在堆上,然后有一个指针引用它。指向这些对象的指针大小通常是CPU的字长的大小,不是32bit就是64bit,这取决于你的处理器,指针指向了你的值的精确位置。

    对于32位系统,你的内存最大可使用4G。对于64系统可以使用更大的内存。但是64位的指针意味着更大的浪费,因为你的指针本身大了。浪费内存不算,更糟糕的是,更大的指针在主内存和缓存器(例如LLC, L1等)之间移动数据的时候,会占用更多的带宽。

    java 使用一个叫内存指针压缩的技术来解决这个问题。它的指针不再表示对象在内存中的精确位置,而是表示偏移量。这意味着32位的指针可以引用40亿个对象,而不是40亿个字节。最终,也就是说堆内存长到32G的物理内存,也可以用32bit的指针表示。

    一旦你越过那个神奇的30-32G的边界,指针就会切回普通对象的指针,每个对象的指针都变长了,就会使用更多的CPU内存带宽,也就是说你实际上失去了更多的内存。事实上当内存到达40-50GB的时候,有效内存才相当于使用内存对象指针压缩技术时候的32G内存。

这段描述的意思就是说:即便你有足够的内存,也尽量不要超过32G,因为它浪费了内存,降低了CPU的性能,还要让GC应对大内存。

5.控制索引合并

ES中的分片和副本本质上都是Lucene索引,而Lucene索引又基于多个索引段构建(至少一个),索引文件中的绝大多数都是只被写一次,读多次,在Lucene内在机制控制下,当满足某种条件的时候多个索引段会被合并到一个更大的索引段,而那些旧的索引段会被抛弃并移除磁盘,这个操作叫做段合并。

Lucene要执行段合并的理由很简单充分:索引段粒度越小,查询性能越低且耗费的内存越多。频繁的文档更改操作会导致大量的小索引段,从而导致文件句柄打开过多的问题,如修改系统配置,增大系统允许的最大文件打开数。总的来讲,当索引段由多一个合并为一个的时候,会减少索引段的数量从而提高ES性能。对于研发者来讲,我们所能做的就是选择合适的合并策略,尽管段合并完全是Lucene的任务,但随着Lucene开放更多配置借口,新版本的ES还是提供了三种合并的策略tiered,log_byte_size,log_doc。另外,ES也提供了两种Lucene索引段合并的调度器:concurrent和serial。其中各者具体区别,这里暂不赘述,只是抛砖引玉。

6.动态关闭不必要的索引

脑裂

概述:一个正常es集群中只有一个主节点,主节点负责管理整个集群,集群的所有节点都会选择同一个节点作为主节点所以无论访问那个节点都可以查看集群的状态信息。而脑裂问题的出现就是因为从节点在选择主节点上出现分歧导致一个集群出现多个主节点从而使集群分裂,使得集群处于异常状态。

原因:

1:网络原因

内网一般不会出现此问题,可以监控内网流量状态。外网的网络出现问题的可能性大些。

2:节点负载

主节点即负责管理集群又要存储数据,当访问量大时可能会导致es实例反应不过来而停止响应,此时其他节点在向主节点发送消息时得不到主节点的响应就会认为主节点挂了,从而重新选择主节点。

3:回收内存

大规模回收内存时也会导致es集群失去响应。

预防方案:

1:角色分离

在es集群中配置2到3个主节点并且让它们只负责管理不负责存储,从节点只负责存储。另外从节点禁用自动发现机制并为其指定主节点,在elasticsearch.yml文件中。

         discovery.zen.ping.multicast.enabled:false

         discovery.zen.ping.unicast.hosts:["host1", "host2:port"]

 

2:参数配置

        discovery.zen.ping_timeout:3

       此参数指定从节点访问主节点后如果3秒之内没有回复则默认主节点挂了,我们可以适当的把它改大,改成5到6秒这样可以减少出现脑裂的概率。

       discovery.zen.minimum_master_nodes:1

       该参数的意思是,当具备成为主节点的从节点的个数满足这个数字且都认为主节点挂了则会进行选举产生新的主节点。

       例如:es集群有三个从节点有资格成为主节点,这时这三个节点都认为主节点挂了则会进行选举,此时如果这个参数的值是4则不会进行选举。

       我们可以适当的把这个值改大,减少出现脑裂的概率,官方给出的建议是(n/2)+1,n为有资格成为主节点的节点数node.master=true。

 

Elasticsearch架构原理

Lucene的处理方法:新收到的数据写到新的索引文件里

Lucene 把每次生成的倒排索引,叫做一个段(segment)。然后另外使用一个 commit 文件,记录索引内所有的 segment。而生成 segment 的数据来源,则是内存中的 buffer。也就是说,动态更新过程如下:

1.当前索引有 3 个 segment 可用

2.ES把新接收的数据进入内存 buffer,还另外记录了一个 translog 日志防止数据丢失。

3.内存 buffer 刷到磁盘,生成一个新的 segment,commit 文件同步更新。

ES知识点ES知识点ES知识点

 

 

 

 

利用磁盘缓存实现的准实时检索?

既然涉及到磁盘,那么一个不可避免的问题就来了:磁盘太慢了!对我们要求实时性很高的服务来说,这种处理还不够。所以,在第 3 步的处理中,还有一个中间状态:

  1. 内存 buffer 生成一个新的 segment,刷到文件系统缓存中,Lucene 即可检索这个新 segment。

 

  1. 文件系统缓存真正同步到磁盘上,commit 文件更新。translog文件才清空。这一步,叫做 flush。

ES知识点

 

 

 

 

 

 

副本一致性:

1.客户端请求发送给 Node 1 节点, Node 1可为 Master 节点可以不是。

2.Node 1 用数据的 _id 取余计算得到应该将数据存储到 shard 0 上。通过 cluster state 信息发现 shard 0 的主分片已经分配到了 Node 3 上。Node 1 转发请求数据给 Node 3。

3.Node 3 完成请求数据的索引过程,存入主分片 0。然后并行转发数据给分配有 shard 0 的副本分片的 Node 2。当收到汇报副本分片数据写入成功,Node 3 即返回给初始的接收节点 Node 1,宣布数据写入成功。Node 1 返回成功响应给客户端。

 

某个 shard 分配在哪个节点上,一般来说,是由 ES 自动决定的。也可以自己手动分配到某个节点上

 

 

 

冷热数据的读写分离

Elasticsearch 集群一个比较突出的问题是: 用户做一次大的查询的时候, 非常大量的读 IO 以及聚合计算导致机器 Load 升高, CPU 使用率上升, 会影响阻塞到新数据的写入, 这个过程甚至会持续几分钟。所以,可能需要仿照 MySQL 集群一样,做读写分离。

1.N 台机器做热数据的存储, 上面只放当天的数据。这 N 台热数据节点上面的 elasticsearc.yml 中配置 node.attr.tag: hot

2.之前的数据放在另外的 M 台机器上。这 M 台冷数据节点中配置 node.attr.tag: stale

3.模板中控制对新建索引添加 hot 标签:

4.每天计划任务更新索引的配置, 将 tag 更改为 stale, 索引会自动迁移到 M 台冷数据节点

这样,写操作集中在 N 台热数据节点上,大范围的读操作集中在 M 台冷数据节点上。避免了堵塞影响。

 

 

集群自动发现:

ES 是一个 P2P 类型(使用 gossip 协议)的分布式系统,除了集群状态管理以外,其他所有的请求都可以发送到集群内任意一台节点上,这个节点可以自己找到需要转发给哪些节点,并且直接跟这些节点通信。

默认的自动发现方式为单播(unicast)方式。配置里提供几台节点的地址,ES 将其视作 gossip router 角色,借以完成集群的发现。由于这只是 ES 内一个很小的功能,所以 gossip router 角色并不需要单独配置,每个 ES 节点都可以担任。所以,采用单播方式的集群,各节点都配置相同的几个节点列表作为 router 即可。

 

 

Gossip(八卦算法、闲话算法)

只要一个人八卦一下, 在有限的时间内所有人都会知道该八卦的信息,

 

特点:在一个有界网络中, 每个节点都随机地与其他节点通信, 经过一番杂乱无章的通信, 最终所有节点的状态都会达成一致. 每个节点可能知道所有其他节点, 也可能仅知道几个邻居节点, 只要这些节可以通过网络连通, 最终他们的状态都是一致的, 当然这也是疫情传播的特点. 要注意到的一点是, 即使有的节点因宕机而重启, 有新节点加入, 但经过一段时间后, 这些节点的状态也会与其他节点达成一致, 也就是说, Gossip 天然具有分布式容错的优点.

 

本质:Gossip 是一个带冗余的容错算法, 更进一步, Gossip 是一个最终一致性算法。虽然无法保证在某个时刻所有节点状态一致, 但可以保证在”最终“所有节点一致, ”最终“是一个现实中存在, 但理论上无法证明的时间点。 因为 Gossip 不要求节点知道所有其他节点, 因此又具有去中心化的特点, 节点之间完全对等, 不需要任何的中心节点。实际上 Gossip 可以用于众多能接受“最终一致性”的领域:失败检测、路由同步、Pub/Sub、动态负载均衡。 但 Gossip 的缺点也很明显, 冗余通信会对网路带宽、CPU 资源造成很大的负载, 而这些负载又受限于通信频率, 该频率又影响着算法收敛的速度。

 

总结:

Gossip 是一种去中心化、容错而又最终一致性的绝妙算法, 其收敛性不但得到证明还具有指数级的收敛速度。使用 Gossip 的系统可以很容易的把 Server 扩展到更多的节点, 满足弹性扩展轻而易举。 唯一的缺点是收敛是最终一致性, 不适应那些强一致性的场景, 比如 2PC。

 

 

 

Elasticsearch的master选举机制

 

 

Elasticsearch是如何实现Master选举的?

1.Elasticsearch的选主是ZenDiscovery模块负责的,主要包含Ping(节点之间通过这个RPC来发现彼此)和Unicast(单播模块包含一个主机列表以控制哪些节点需要ping通)这两部分;

2.对所有可以成为master的节点(node.master: true)根据nodeId字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。

3.如果对某个节点的投票数达到一定的值(可以成为master节点数n/2+1)并且该节点自己也选举自己,那这个节点就是master。否则重新选举一直到满足上述条件。

补充:master节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data节点可以关闭http功能。

 

 

 

简单来说master的作用跟单个jvm中的同步关键字synchronized相同,集群中多节点协调工作必须要保证数据的一致性,但是不同节点分布在不同的jvm中,不可能用jvm的同步机制。所以需要一个“锁”,节点操作集群中的资源时都通过它来解决一致性问题,这就是master。关于分布式系统的master选举算法有很多,paxos算法,ZAB算法,Bully算法

Bully:它通过一定的直接给每个节点赋予一唯一的ID,这些ID是可以排序的,每次master选举都会选举ID最大的节点。这种实现非常简单。但是会存在一些问题,在master负载过重时它会假死,于是第二大节点就成为了master节点。因此假死master节点因负载减轻又活了过来,于是他又被选为master,然后又假死……,这种情况可能一直存在导致系统不稳定。

 

集群还有一个问题就是brain split:一个集群因为网络问题导致多个master选举出来而分裂。这也是master选举必须要解决的问题。elasticsearch的master选举原理我觉得是在bully的基础上做了改进。相比于paxos实现的zookeeper它完美的解决了master选举问题,但不如zookeeper强大,因为zookeeper功能远远超出了master选举,它的master选举却不需要这么多功能。它原理如下:

 

对所有可以成为master的节点根据nodeId排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。

如果对某个节点的投票数达到一定的值(可以成为master节点数n/2+1)并且该节点自己也选举自己,那这个节点就是master。否则重新选举。

对于brain split问题,需要把候选master节点最小值设置为可以成为master节点数n/2+1(quorum )

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Zookeeper(ZAB)

阶段一:发现

阶段一主要就是Leader选举过程,用于在多个分布式进程中选举出主进程,准Leader和Follower的工作流程分别如下:

 

1.Follower将自己最后接受的事务Proposal的epoch值发送给准Leader;

 

2.当接收到来自过半Follower的消息后,准Leader会生成消息给这些过半的Follower。关于这个epoch值e’,准Leader会从所有接收到的CEPOCH消息中选取出最大的epoch值,然后对其进行加1操作,即为e’。

 

3.当Follower接收到来自准Leader的NEWEPOCH消息后,如果其检测到当前的CEPOCH值小于e’,那么就会将CEPOCH赋值为e’,同时向这个准Leader反馈ACK消息。在这个反馈消息中,包含了当前该Follower的epoch CEPOCH(F p),以及该Follower的历史事务Proposal集合:hf。

 

当Leader接收到来自过半Follower的确认消息ACK之后,Leader就会从这过半服务器中选取出一个Follower,并使用其作为初始化事务集合Ie’。

 

 

 

 

 

 

 

脑裂

假设之前选举了A节点为master,两个switch之间突然断线了,这样就分成了两部分。CDE和AB,因为 minimumMasterNodes的数目为3(集群中5个节点都可以成为master,3=5/2+1),因此cde会可以进行选举假设C成为master。AB两个节点因为少于3所以无法选举,只能一直寻求加入集群,要么线路连通加入到CDE中要么就一直处于寻找集群状态,这样就保证了集群不分裂。

ES知识点

 

 

容错机制

容错的步骤:

a、原来node1是一个master,假如因为某种原因node1宕机了,node中是R0,R1和R2全部丢失。

b、进行master选举,自动选举另外一个node成为新的master,承担起master的责任来,例如现在全部选举了node2为master。

c、新的master,将丢失的primary shard的某个replica shard提升为primary  shard。此时集群的状态就会变为yellow,原因是虽然目前所有的primary  shard全部变成active,但是replice shard少了,也就并不是所有的replica  shard都是active。

d、重启故障的node1,新的master(node2)会将缺失的副本都copy一份到这个node1上面去,而且这个node1会使用之前已经有的shard数据,只是同步一下宕机之后发生的修改,此时,集群是status再次变回green。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

背景:系统日志的查找

 

 

ES的核心机制

1 主备机制

ES是一个分布式的,那么对于分布式的架构,肯定是由主备机制的,在ES中,就是有shard和replica机制。

 

(1)index包含多个shard

(2)每个shard都是一个最小工作单元,承载部分数据,lucene实例,完整的建立索引和处理请求的能力

(3)增减节点时,shard会自动在nodes中负载均衡

(4)primary shard和replica shard,每个document肯定只存在于某一个primary shard以及其对应的replica shard中,不可能存在于多个primary shard

(5)replica shard是primary shard的副本,负责容错,以及承担读请求负载

(6)primary shard的数量在创建索引的时候就固定了,replica shard的数量可以随时修改

(7)primary shard的默认数量是5,replica默认是1,默认有10个shard,5个primary shard,5个replica shard

(8)primary shard不能和自己的replica shard放在同一个节点上(否则节点宕机,primary shard和副本都丢失,起不到容错的作用),但是可以和其他primary shard的replica shard放在同一个节点上

 

2 容错机制

Elasticsearch容错机制:master选举,replica容错,数据恢复。

容错步骤:

a、原来node1是一个master,假如因为某种原因node1宕机了,node中是R0,R1和R2全部丢失。

b、进行master选举,自动选举另外一个node成为新的master,承担起master的责任来,例如现在全部选举了node2为master。

c、新的master,将丢失的primary shard的某个replica shard提升为primary  shard。此时集群的状态就会变为yellow,原因是虽然目前所有的primary  shard全部变成active,但是replice shard少了,也就并不是所有的replica  shard都是active。

d、重启故障的node1,新的master(node2)会将缺失的副本都copy一份到这个node1上面去,而且这个node1会使用之前已经有的shard数据,只是同步一下宕机之后发生的修改,此时,集群是status再次变回green。

 

3 ES的并发控制

悲观锁方案

 

常见于关系型数据库中。在各种情况下都上锁,就只有一个线程可以操作这条数据,不同的场景下,有行级锁、表级锁、读锁和写锁。

 

工作流程:

读取鞋子数据的时候,就在这行加锁,线程A先不改数据,

线程b去读的时候会发现都不到数据,被卡住不能动。

线程A把库存改为99。

线程b可以读取库存数据了,是99件。

然后线程B再把库存改为98。

 

乐观锁方案

 

乐观锁是不加锁的,每个线程都可以操作,通过一个version号来识别,看是不是一样的,例如线程A操作后是version=1,库存改为99,同一时间有一个用户B也读到了这数据,下单也把库存变为99件,version=2,线程B去判断,version=1与es中的版本version=2,说明数据已经被其他人修改过了,然后就重新去es中读取最新版本数据,也就是99,然后再减一,就变为98件了。

总之:99—version1 98–version2.

 

悲观锁的优点:方便,对应用程序来说透明,缺点:并发能力低。

乐观锁:并发能力高,都需要重新比对版本号,然后可能需要重新加载数据,再写,这个过程,可能需要重复好几次

 

每次执行修改和删除,这个version的版本号会自动加1

在删除一个document之后,并没有物理删除掉的,因为它的版本号信息还是保留着的,先删除一条document,再重新创建这条document,其实会在delete version基础之上,再把version号加1.

 

es的后台都是多线程异步的,多个修改请求,是没有顺序的,例如后修改的先到。

es内部的多线程异步并发是基于自己的verison版本号进行乐观锁控制的,在后修改的先到时,filed=test3,version =2,修改后到时,先修改filed=test2,veriosn=1,此时会比较veriosn,是否相等,如果不相等的话,就直接丢弃这条数据了。

 

 

 

 

倒排索引与全文搜索

倒排索引(Inverted index):通常的索引是通过文档找关键词,即通过文档id找到文档,再从中找关键词。而倒排索引则是通过关键词找到其所在的文档。即:

 

传统索引:文档 ---> 单词

倒排索引:单词 ---> 文档

ES知识点

 

单词存在内存的“词典”中,相当于索引(传统意义上的索引,不是ES中的index)文件,获得单词后,通过字典树之类的方式确定存储该单词的倒排索引存储位置,其中存储了单词信息和指向倒排文件的指针,倒排文件中存储倒排列表,倒排列表中存储的是出现过该单词的所有文档,以及该单词在文档中出现的位置。这样就可以通过单词来获取文档了。

 

全文搜索

 

传统的数据库搜索如mysql,是结构化搜索,也就是文档(表中的一条数据)是结构化的(由很多指定的字段组成)

 

而全文搜索的文档内容不是结构化的,如一篇文章,一个句子这样,没有根据指定字段进行划分。而搜索引擎(使用全文搜索)通常处理的就是这类文档。

 

全文搜索处理的过程为(以ES为例):

 

对于存储文档:

 

先进行字符过滤,将一些无效字符(如HTML的标签)给过滤掉或转成合适的字符(如&转成and)

 

然后一个文档(可以是一个句子或文章)分词

 

例如:明天就放假了 分词为:明天 就 放假 了

 

接着通过token(就是指单词)过滤器对单词进行过滤 如:“了”没有意义根据算法可能会被token过滤器过滤掉。

 

然后根据分词创建倒排索引

 

以下过程为个人猜测,具体实现没有看,仅仅用于帮助理解:

 

例如:对于单词“明天” 先判断词典中是否有“明天“这个单词,如果有,则根据该单词的倒排索引找到倒排文件,将“明天就放假了”文档的指针(或者是id)添加到倒排文件中。如果词典中没有,就在词典中创建一个“明天“的倒排索引,并新建一个倒排文件存储出现“明天"的文档指针或id,然后将该倒排文件的指针添加到倒排索引中。

 

这样当搜索明天时,就会根据明天在词典中找到对应的倒排索引,从而获取文件,从中读出出现过明天的文档,“明天就放假了“就会被搜索出来,如果还有其他包含单词“明天”的文档也会被一起搜索出来。

 

对于搜索而言:

 

例如此时ES中有几个文档:

 

1“明天就放假了”

 

2“明天不加班”

 

3“明天睡懒觉”

 

4“明天出去吃饭”

 

5“今晚不加班”

 

当搜索“明天“时,

 

与之前存储的过程类似,可以找到“明天“的倒排文件,从而获得前4个文档,然后根据评分算法

 

决定哪些文档可以显示,以什么顺序进行显示。

 

如以词频/逆文本(根据搜索词出现的频率以及不出现的频率评分)进行评分,显示超过0.2分(举个例子)的文档。1,4为1/3,2,3为2/5。那么4个文档都超过0.2分,都可以显示,

 

显示的结果就是2,3在前 1,4在后。

 

而当搜索“明天放假“时,分词结果为 “明天”“放假” 根据两个词进行检索,对于文档1,两个词都能检索到,即评分为 1/3+1/3=2/3,而对于2,“明天“命中得2/5分,“放假”不命中,扣分x(具体扣多少根据算法定)。最后文档1分大于文档2,结果显示文档1在文档2之前,对于文档2,若减分很多,低于了0.2就不显示了。