Elasticsearch相关知识
- Elasticsearch:集群中的节点通过端口 9300 彼此通信。如果这个端口没有打开,节点将无法形成一个集群。
- Elasticsearch 尽可能地屏蔽了分布式系统的复杂性。这里列举了一些在后台自动执行的操作:
①分配文档到不同的容器 或 分片 中,文档可以储存在一个或多个节点中
②按集群节点来均衡分配这些分片,从而对索引和搜索过程进行负载均衡
③复制每个分片以支持数据冗余,从而防止硬件故障导致的数据丢失
④将集群中任一节点的请求路由到存有相关数据的节点
⑤集群扩容时无缝整合新节点,重新分配分片以便从离群节点恢复
Elasticsearch 集群健康:
curl -XGET 'localhost:9200/_cluster/health?pretty' { "cluster_name" : "v_tlonghe", "status" : "yellow", //green 所有的主分片和副本分片都正常运行。 yellow 所有的主分片都正常运行,但不是所有的副本分片都正常运行。red 有主分片没能正常运行。 "timed_out" : false, "number_of_nodes" : 1, "number_of_data_nodes" : 1, "active_primary_shards" : 15, "active_shards" : 15, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 15, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 50.0 }
Elasticsearch 索引
索引:实际上是指向一个或者多个物理分片的逻辑命名空间。 分片: ①一个分片是一个底层的工作单元它仅保存了全部数据中的一部分,一个分片是一个Lucene的实例,以及它本身就是一个完整的搜索引 擎。 ②Elasticsearch 是利用分片将数据分发到集群内各处的。分片是数据的容器,文档保存在分片内,分片又被分配到集群内的各个节点 里。 ③当你的集群规模扩大或者缩小时, Elasticsearch 会自动的在各节点中迁移分片,使得数据仍然均匀分布在集群里。 ④一个分片可以是 主 分片或者 副本 分片。 索引内任意一个文档都归属于一个主分片,所以主分片的数目决定着索引能够保存的最大 数据量。一个主分片最大能够存储 Integer.MAX_VALUE - 128 个文档,但是实际最大值还需要参考你的使用场景。 ⑤在索引建立的时候就已经确定了主分片数,但是副本分片数可以随时修改。
增大吞吐量:
读操作——搜索和返回数据——可以同时被主分片 或 副本分片所处理,所以当你拥有越多的副本分片时,也将拥有越高的吞吐量。
CURL中请求的pretty含义:
在请求的查询串参数中加上 pretty 参数, 正如前面的例子中看到的,这将会调用 Elasticsearch 的 pretty-print 功能,该功能 使 得 JSON 响应体更加可读。
index内字段含义:
_id:自动生成的 ID 是 URL-safe、 基于 Base64 编码且长度为20个字符的 GUID 字符串。 这些 GUID 字符串由可修改的 FlakeID 模 式生成,这种模式允许多个节点并行生成唯一 ID ,且互相之间的冲突概率几乎为零。 _version:每个doc都有一个,当这个doc被修改时,_version变化
处理更新文档的冲突:
每个文档都有一个_version,在我们修改时,可以把_version做为条件。例: PUT /website/blog/1?version=1 { "title": "My first blog entry", "text": "Starting to get the hang of this..." } 修改时,Elasticsearch判断_version>old_version?我们能修改成功:返回错误信息,status:409
Elasticsearch索引分片
如何确定分配到哪个分片? shard = hash(routing) % number_of_primary_shards routing:一个可变值,默认是文档的_id number_of_primary_shards:主分片的数量 注:这就解释了为什么我们要在创建索引的时候就确定好主分片的数量 并且永远不会改变这个数量:因为如果数量变化了,那么所有之 前路由的值都会无效,文档也再也找不到了。
如何确定索引在哪个分片
这些都是写操作,主分片都要求分片副本处于活跃状态达到一定数量,才会执行,避免发送网络分区故障的时候进行写操作, 进而导致数据不一致。 consistency = int( (primary + number_of_replicas) / 2 ) + 1 如果没有足够的,那么会一直等待,直到满足或者timeout
Elasticsearch查询
精确值很容易查询。结果是二进制的:要么匹配查询,要么不匹配。 查询全文数据要微妙的多。我们问的不只是“这个文档匹配查询吗”,而是“该文档匹配查询的程度有多大?”
Elasticsearch分析
个人建议:不确定做全文查询(即按匹配度返回结果)的字段,全部都不去分析,减少存储空间,减少token
Elasticsearch评分查询与过滤的区别
评分查询:不仅仅要找出 匹配的文档,还要计算每个匹配文档的相关性,结果不缓存 过滤:只是简单的检查包含或者排除,结果少且结果缓存 如何选择查询与过滤编辑 通常的规则是,使用 查询(query)语句来进行 全文 搜索或者其它任何需要影响 相关性得分 的搜索。除此以外的情况都使用过滤 (filters)。
Elasticsearch查询语法:
1.match_all:查询所有; 2.match:全文搜索时,会根据查询的字段是否分析而去判断是否分析查询字符串,精确查询时,精确匹配 3.term:用于精确值匹配 4.terms:类似sql中的in 5.exists、missing:用于查找指定字段有没有值 注:boost:1(default),查询时可以增加该字段修改权重
Elasticsearch处理NULL值
首先了解Lucene一些概念: Document: 它是在索引和搜索过程中数据的主要表现形式,或者称“载体”,承载着我们索引和搜索的数据,它由一个或者多个域(Field)组成。 Field: 它是Document的组成部分,由两部分组成,名称(name)和值(value)。 Term: 它是搜索的基本单位,其表现形式为文本中的一个词。 Token: 它是单个Term在所属Field中文本的呈现形式,包含了Term内容、Term类型、Term在文本中的起始及偏移位置。 所以如果字段不存在,也就无法在倒排索引结构中表现,无法存于倒排索引中。 对于这一类数据的查询,使用exists、missing exists:返回那些在指定字段有任何值的文档(is not null) missing:返回某个特定 _无_ 值字段的文档(is null) 我们可以选择将显式的 null 值替换成我们指定 占位符(placeholder) 。在为字符串(string)、数字(numeric)、布尔值 (Boolean)或日期(date)字段指定映射时,同样可以为之设置 null_value 空值,用以处理显式 null 值的情况。
Elasticsearch缓存:
只缓存非评分查询,且文档数超过10000或超过总文档数量的3%才会被缓存 缓存的剔除规则:缓存满时,最近最少被使用将被剔除
Elasticsearch数据建模:
关联关系处理:
①应用层关联 ②非规范化数据 ③嵌套对象 ④父子关系文档
-①应用层关联:索引之间,通过类似于主键进行关联,在应用层面,通过上一个索引的返回结果,座位后一个查询的条件,得到最终结果。优点是数据标准化,缺点是会有额外的查询。
-②非规范化数据:即冗余一定的数据。优点是速度快,缺点数据冗余,非规范。
-③嵌套对象:允许将一个对象实体和另外一个对象实体关联起来存在一个文档中。优点:由于所有的信息都在一个文档中,当我们查询时就没有必要去联合文章和评论文档,查询效率就很高。缺点:可能造成数据冗余,嵌套文档是隐藏存储的,我们不能直接获取。如果要增删改一个嵌套对象,我们必须把整个文档重新索引才可以。值得注意的是,查询的时候返回的是整个文档,而不是嵌套文档本身。
-④父子关系文档:类似于嵌套对象,区别在于父对象和子对象都是完全独立的文档。优点:更新父文档时,不会重新索引子文档;创建,修改或删除子文档时,不会影响父文档或其他子文档。这一点在这种场景下尤其有用:子文档数量较多,并且子文档创建和修改的频率高时;子文档可以作为搜索结果独立返回.限制:父文档和其所有子文档,都必须要存储在同一个分片中;父子文档创建时间点:1)创建 索引时;2)在子文档 type 创建之前指定 mapping中的_parent,子文档创建时需要指明父文档的ID(确保父子文档在同一个分片)如果你想要改变一个子文档的 parent 值,仅通过更新这个子文档是不够的,因为新的父文档有可能在另外一个分片上。因此,你必须要先把子文档删除,然后再重新索引这个子文档。官网建议:当文档索引性能远比查询性能重要 的时候,父子关系是非常有用的,但是它也是有巨大代价的。其查询速度会比同等的嵌套查询慢5到10倍!
扩容
一个分片即一个lucene索引,一个Elasticsearch索引即一系列分片的集合。
索引扩容很麻烦,所以需要预设索引的分片数量。
分片数量的考量规则:
- 一个分片的底层即为一个 Lucene 索引,会消耗一定文件句柄、内存、以及 CPU 运转。
- 每一个搜索请求都需要命中索引中的每一个分片,如果每一个分片都处于不同的节点还好, 但如果多个分片都需要在同一个节点上竞争使用相同的资源就有些糟糕了。
- 用于计算相关度的词项统计信息是基于分片的。如果有许多分片,每一个都只有很少的数据会导致很低的相关度。
容量规划:
如果一个分片太少而 1000 个又太多,那么我怎么知道我需要多少分片呢? 一般情况下这是一个无法回答的问题。因为实在有太多相关的因素了:你使用的硬件、文档的大小和复杂度、文档的索引分析方式、运行的查询类型、执行的聚合以及你的数据模型等等。
幸运的是,在特定场景下这是一个容易回答的问题,尤其是你自己的场景:
基于你准备用于生产环境的硬件创建一个拥有单个节点的集群。
创建一个和你准备用于生产环境相同配置和分析器的索引,但让它只有一个主分片无副本分片。
索引实际的文档(或者尽可能接近实际)。
运行实际的查询和聚合(或者尽可能接近实际)。
基本来说,你需要复制真实环境的使用方式并将它们全部压缩到单个分片上直到它“挂掉。” 实际上 挂掉 的定义也取决于你:一些用户需要所有响应在 50 毫秒内返回;另一些则乐于等上 5 秒钟。
一旦你定义好了单个分片的容量,很容易就可以推算出整个索引的分片数。 用你需要索引的数据总数加上一部分预期的增长,除以单个分片的容量,结果就是你需要的主分片个数。
扩容:
当你需要在不停服务的情况下增加容量时,下面有一些有用的建议。相较于将数据迁移到更大的索引中,你可以仅仅做下面这些操作:
① 创建一个新的索引来存储新的数据。
② 同时搜索两个索引来获取新数据和旧数据。
实际上,通过一点预先计划,添加一个新索引可以通过一种完全透明的方式完成,你的应用程序根本不会察觉到任何的改变。
在索引别名和零停机,我们提到过使用索引别名来指向当前版本的索引。
负载均衡(例子 场景:起始两个节点,后增加一个节点)
搜索性能取决于最慢的节点的响应时间,所以尝试均衡所有节点的负载是一个好想法。 如果我们只是增加一个节点而不是两个,最终我们会有两个节点各持有一个分片,而另一个持有两个分片做着两倍的工作。
我们可以通过调整副本数量来平衡这些。通过分配两份副本而不是一个,最终我们会拥有六个分片,刚好可以平均分给三个节点
PUT /my_index/_settings { "number_of_replicas": 2 //修改副本数 }
按时间粒度建索引
- 按天建索引,即时间后缀不同
- 索引时,可以在应用层,直接根据索引名索引数据
- 按时间粒度创建索引时,可以使用别名,通过add和remove修改别名指向的索引,这样应用层就不需要特殊处理
POST /_aliases { "actions": [ { "add": { "alias": "logs_current", "index": "logs_2014-10" }}, { "remove": { "alias": "logs_current", "index": "logs_2014-09" }}, { "add": { "alias": "last_3_months", "index": "logs_2014-10" }}, { "remove": { "alias": "last_3_months", "index": "logs_2014-07" }} ] }
索引模版
PUT /_template/my_logs ① { "template": "logstash-*", ② "order": 1, ③ "settings": { "number_of_shards": 1 ④ }, "mappings": { "_default_": { ⑤ "_all": { "enabled": false } } }, "aliases": { "last_3_months": {} ⑥ } }
①创建一个名为 my_logs 的模板。
②将这个模板应用于所有以 logstash- 为起始的索引。
③这个模板将会覆盖默认的 logstash 模板,因为默认模板的 order 更低。
④限制主分片数量为 1 。
⑤为所有类型禁用 _all 域。
⑥添加这个索引至 last_3_months 别名中。
索引优化
- 段合并 参考optimize API
- 关闭旧索引
- 归档旧索引
部署
日志记录
- 日志记录级别调整
PUT /_cluster/settings { "transient" : { "logger.discovery" : "DEBUG" } } 避免使用 TRACE 。这个级别非常的详细,详细到日志反而不再有用了。
PUT /_cluster/settings { "transient" : { "logger.index.search.slowlog" : "DEBUG", ① "logger.index.indexing.slowlog" : "WARN" ② } }
①设置搜索慢日志为 DEBUG 级别。
②设置索引慢日志为 WARN 级别。
- 慢日志
PUT /my_index/_settings { "index.search.slowlog.threshold.query.warn" : "10s", ① "index.search.slowlog.threshold.fetch.debug": "500ms", ② "index.indexing.slowlog.threshold.index.info": "5s" ③ }
①查询慢于 10 秒输出一个 WARN 日志。
②获取慢于 500 毫秒输出一个 DEBUG 日志。
③索引慢于 5 秒输出一个 INFO 日志。
Elasticsearch升级,重启,备份,快照方式请参考官网
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_rolling_restarts.html