Summer Blog

Elasticsearch基础知识

基本概念

基本接口

集群管理

GET /_cat/health?v  // 了解集群健康状态,green:每个索引的shard和replica都是可用的yellow:所以的shard可用,部分replica不可用;red:部分shard不可用

GET /_cat/indices?v // 查看有哪些索引

索引操作

PUT /{index-name}/pretty // 创建
DELETE /{index-name}/pretty // 删除
GET /{index-name}/_mapping // 获取index的mapping类型

文档操作

PUT /{index-name}/_doc/1 //创建,若id为1的文档已存在会更新
{
    "user" : "kim",
    "post_date" : "2009-11-15T14:12:12",
    "message" : "trying out Elasticsearch"
}

GET /{index-name}/{id} // 查询

PUT /{index-name}/_doc/{id} // 更新
{
    "user" : "kim"
}

DELETE /{index-name}/{id} // 删除

批量操作

GET /_mget // 批量查询
GET /{index-name}/_mget // 同一个index下批量查询
GET /{index-name}/_doc/_mget // 同一个index和type下批量查询
{
    "docs" : [
        {
            "_index" : "test",
            "_type" : "_doc",
            "_id" : "1",
            "stored_fields" : ["field1", "field2"]
        },
        {
            "_index" : "test",
            "_type" : "_doc",
            "_id" : "2",
            "stored_fields" : ["field1", "field2"]
        }
    ]
}

GET /{index-name}/_doc/_mget?stored_fields=field1,field2 // 第三种,可简化为ids写法
{
    "ids" : ["1", "2"]
}

POST /{index-name}/_bulk
// 一般的形式为,一个json对象不能换行,不同的json对象直接换行
// 这种格式会省略一步json转换,直接将数据路由到处理node,减少内存占用
{"": ""}
{  }

搜索

// 使用size和from可以分页查询
// es是分布式系统,分页每个node需要返回的是截至前的所有数据,交给coordinate node进行排序,所以会产生deep paging问题
// index和type都可以使用通配符
GET /[{index1},{index2}]/[{type1},{type2}]/_search?size=10&from=0 //搜索

// +表示必须包含value;-表示必须不包含
// 不添加field表示对每一个field进行搜索,使用es内置的_all字段(所有值拼在一起)
GET /_search?q=[+/-][{field}:]{value} // query string

// 获取数据类型
GET /{index-name}/_mapping

// 判断查询是否合法,request body为要验证的查询对象
GET /{index-name}/{type}/_validate/query?explain

// 滚动搜索,1m为失效时间,request body为查询对象,该操作会返回scroll_id
GET /{index-name}/{type}/_search?scroll=1m
// 滚动搜索之后的内容
GET /_search/scroll
{
    "scroll": 1m,
    "scroll_id": 123132
}

// 分析当前搜索
PUT /{index-name}/_analyze

分布式架构

路由

算法:shard = hash(id or custom routing value) % number_of_primary_shards

自定义 routing value 可以将某一类的数据路由到同一个 shard,提升批量操作的性能,也能更好的控制负载均衡

容错的机制

  1. 选举新 master
  2. 将丢失的 shard 的 replica 提升变为 primary shard
  3. 重启故障及,将挂机时产生的数据 copy 回原 shard 进行同步

并发冲突

当并发请求越多,用户操作读和写之间时间较长会发生并发冲突。es 解决方式是使用乐观锁,通过版本号(内置的if_seq_noif_primary_term参数;也可以自定义,通过参数versionversion_type=external)控制并发操作。es 同步时是多线程进行的,可能出现后修改的先同步到 replica,所以也有乐观锁控制。

数据存储

document id

_source 元数据

创建时传给 es 的 json 串,默认都返回,可以添加参数_source指定返回的字段

替换,删除

增删改操作过程

  1. 客户端选择一个 node 发送请求,这个 node 就是 coordinating node(协调节点)
  2. coordinating node 将 document 进行路由,转发给对应的 shard
  3. shard 所在的 node 处理请求,并将信息同步给 replica。当所有 replica 同步成功后,shard 将消息报告给 coordinating node,coordinating node 将消息报告给客户端
    • consistency,一致性。值可以设为 one (只要主分片状态 ok 就允许执行操作),all(必须要主分片和所有副本分片的状态没问题才允许执行操作), 或 quorum 。默认值为 quorum , 即大多数的分片副本状态没问题就允许执行操作。算法int( (primary + number_of_replicas) / 2 ) + 1
    • timeout,当没有足够副本时会等待,默认为 1 分钟,可以自行设置

读请求操作过程

  1. 客户端选择一个 node 发送请求,这个 node 就是 coordinating node(协调节点)
  2. coordinating node 将 document 进行路由,使用负载均衡转发给对应的 shard 或 replica
  3. 实际执行 node 返回结果给 coordinating node
  4. coordinating node 将结果返回给客户端

写入更新索引内部逻辑(todo)

搜索

倒排索引

分词器

  1. character filter:预处理,去除干扰项目
  2. tokenizer:分词
  3. token filter:二次加工,比如:大小写,去停,同义词

mapping

搜索模式

filter 与 query 的区别

搜索类型

搜索连接词

排序

分数算法

es 使用:term frequency/inverse document frequency 算法,简写为 TF/IDF

搜索过程

查询阶段(query phrase):

  1. 协调节点构建一个优先队列(大小为 from + size)
  2. 将请求转发到有这个索引分区的节点
  3. 每个查询节点构建优先队列,并将查询结果放入,返回给协调节点
  4. 协调节点合并队列,构建全局队列,此时获取的是 doc id

获取阶段(fetch phrase):

  1. 协调节点根据查询阶段得到的 doc id 发送 mget 请求至索引分区节点获取对象 document
  2. 各个节点返回结果给协调节点
  3. 协调节点合并返回,将其发送至客户端

实际问题解决方案

不停机重建索引

当创建索引字段类型错误时,因为不能直接修改索引字段类型,此时唯一办法是重建索引(将旧数据批量查询出来,然后插入新索引)。步骤如下:

  1. 新建正确的索引 mapping
  2. 使用scroll api将数据批量查询出来
  3. 使用bulk api批量插入数据
  4. 重复 2 ~ 3 步骤,直到所有数据写入新索引
  5. 将新索引使用 alias 重命名

选型比较

项目 优势 劣势 特点
ES Elasticsearch 是分布式的。不需要其他组件,分发是实时的,被叫做”Push replication”;Elasticsearch 完全支持 Apache Lucene 的接近实时的搜索;处理多租户(multitenancy)不需要特殊配置,而 Solr 则需要更多的高级设置;Elasticsearch 采用 Gateway 的概念,使得完备份更加简单;各节点组成对等的网络结构,某些节点出现故障时会自动分配其他节点代替其进行工作 只支持 json Elasticsearch 是一个实时的分布式搜索和分析引擎
Solr Solr 有一个更大、更成熟的用户、开发和贡献者社区;支持添加多种格式的索引;不考虑建索引的同时进行搜索,速度更快 建立索引时,搜索效率下降,实时索引搜索效率不高 Solr 是用 Java 编写、运行在 Servlet 容器(如 Apache Tomcat 或 Jetty)的一个独立的全文搜索服务器。 Solr 采用了 Lucene Java 搜索库为核心的全文索引和搜索,并具有类似 REST 的 HTTP/XML 和 JSON 的 API。Solr 强大的外部配置功能使得无需进行 Java 编码,便可对其进行调整以适应多种类型的应用程序。Solr 有一个插件架构,以支持更多的高级定制。
Lucene 成熟的解决方案,有很多的成功案例。apache 顶级项目,正在持续快速的进步。庞大而活跃的开发社区,大量的开发人员。它只是一个类库,有足够的定制和优化空间:经过简单定制,就可以满足绝大部分常见的需求;经过优化,可以支持 10 亿+ 量级的搜索。 需要额外的开发工作。所有的扩展,分布式,可靠性等都需要自己实现;非实时,从建索引到可以搜索中间有一个时间延迟,而当前的“近实时”(Lucene Near Real Time search)搜索方案的可扩展性有待进一步完善 Lucene 是一个 JAVA 搜索类库,它本身并不是一个完整的解决方案,需要额外的开发工作

调优

索引速度

  1. 使用批量请求批量请求将产生比单文档索引请求好得多的性能
  2. 充分利用 es 的分布式特性,从多个线程或进程发送数据
  3. 使用自动生成的 id(auto-generated ids),设置的id需要去验证id是否已存在去更新
  4. 加载大量数据时禁用 refresh 和 replicas(可能造成数据丢失的危险,完成后恢复原始值)
  5. 配置修改
    1. 调大 refresh interval
    2. 为 filesystem cache 分配一半的物理内存
    3. 加大 indexing buffer size

搜索速度

  1. document不要过大
  2. filesystem cache 越大越好
  3. 用更好的硬件
  4. 文档模型(document modeling),避免 join 查询
  5. 预索引数据,将经常进行的查询做更多的优化处理
  6. Mappings 设置,合理使用字段类型、索引字段,减小索引大小,对不需要搜索的字段设置为 keyword
  7. 避免运行脚本
  8. 搜索 rounded 日期
  9. 强制 merge 只读的 index,只读的 index 可以从“merge 成 一个单独的 大 segment”中收益
  10. 预热 filesystem cache
  11. 使用 preference 来优化高速缓存利用率。例如,使用标识当前用户或会话的优选值可以帮助优化高速缓存的使用

comments powered by Disqus