前言:本文内容为,我在阅读,学习elasticsearch官方文档时候的随笔。以运维工程师的角度学习elasticsearch。内容比较混乱。详细的内容,请参考官方文档。
1.后台运行elasticsearch
工作目录下,./bin/elasticsearch -d
注意:由于安全问题,elasticsearch不允许root用户直接运行。因此需要用其他用户来执行程序(1.chown -R user;./bin/elasticsearch -d)
2.
节点客户端(Node client)
节点客户端作为一个非数据节点加入到本地集群中。换句话说,它本身不保存任何数据,但是它知道数据在集群中的哪个节点中,并且可以把请求转发到正确的节点。
传输客户端(Transport client)
轻量级的传输客户端可以将请求发送到远程集群。它本身不加入集群,但是它可以将请求转发到集群中的一个节点上。
3.索引数据文档
PUT /megacorp/employee/1
{
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
megacorp:索引名称,employee:类型名称,1:数据的ID
4.检索文档
检索id为1的数据
GET /megacorp/employee/1
检索所有数据(默认返回前10条)
GET /megacorp/employee/_search
根据条件检索(可用于轻量级检索)
GET /megacorp/employee/_search?q=last_name:Smith
查询表达式搜索
GET /megacorp/employee/_search
{
"query" : {
"match" : {
"last_name" : "Smith"
}
}
}
使用filter进行搜索
GET /megacorp/employee/_search
{
"query" : {
"bool": {
"must": {
"match" : {
"last_name" : "smith"
}
},
"filter": {
"range" : {
"age" : { "gt" : 30 }
}
}
}
}
}
全文搜索
GET /megacorp/employee/_search
{
"query" : {
"match" : {
"about" : "rock climbing"
}
}
}
根据相关性得分进行排序输出跟“rock climbing”有关的文档
短语搜索
GET /megacorp/employee/_search
{
"query" : {
"match_phrase" : { //使用match_phrase参数,精准匹配短语
"about" : "rock climbing"
}
}
}
高亮搜索
GET /megacorp/employee/_search
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
},
"highlight": {
"fields" : {
"about" : {}
}
}
}
分析
GET /megacorp/employee/_search
{
#可添加匹配条件
"query": {
"match": {
"last_name": "smith"
}
},
"aggs" : {
"all_interests" : {
"terms" : { "field" : "interests" },
#分级汇总
"aggs" : {
"avg_age" : {
"avg" : { "field" : "age" }
}
}
}
}
}
5.分布式特性
Elasticsearch 尽可能地屏蔽了分布式系统的复杂性。这里列举了一些在后台自动执行的操作:
--分配文档到不同的容器 或 分片 中,文档可以储存在一个或多个节点中
--按集群节点来均衡分配这些分片,从而对索引和搜索过程进行负载均衡
--复制每个分片以支持数据冗余,从而防止硬件故障导致的数据丢失
--将集群中任一节点的请求路由到存有相关数据的节点
--集群扩容时无缝整合新节点,重新分配分片以便从离群节点恢复
集群原理
1.健康状态
GET /_cluster/health
green
所有的主分片和副本分片都正常运行。
yellow
所有的主分片都正常运行,但不是所有的副本分片都正常运行。
red
有主分片没能正常运行。
注:单节点时,监控状态出现yellow:集群原理里,如果持有主分片的节点挂掉了,一个副本分片就会晋升为主分片的角色。那么可以看出来副本分片和主分片是不能放到一个节点上面的,可是在只有一个节点的集群里,副本分片没有办法分配到其他的节点上,所以出现所有副本分片都unassigned得情况。因为只有一个节点,如果存在主分片节点挂掉了,那么整个集群理应就挂掉了,不存在副本分片升为主分片的情况。解决办法就是,在单节点的elasticsearch集群,删除存在副本分片的索引,新建索引的副本都设为0。然后再查看集群状态。
2.创建索引
PUT /blogs
{
"settings" : {
#分片数量,副本数量
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}
3.单播形式加入集群
修改elasticsearch.yml文件:
node.name=node-srv1
network.host=0.0.0.0|x.x.x.x
discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]
集群管理、监控与部署
监控
1.marvel监控
安装marvel
i)每一个ES安装licence、marvel插件
进入elasticsearch/bin下,
bash plugin install licence
bash plugin install marvel-agent
ii)配置elasticsearch.yml配置文件,添加action.auto_create_index: .marvel-*
iii)kibana安装marvel插件
bash kibana plugin --install elasticsearch/marvel/latest
iv)重启ES,kibana服务
bash elasticsearch -d
systemctl restart kibana
2.集群健康
GET _cluster/health
{
#集群名称
"cluster_name": "elasticsearch_zach",
#集群状态
"status": "green",
"timed_out": false,
#node包括node.master、node.data两种类型,
#参考:https://blog.csdn.net/J_bean/article/details/80147277
"number_of_nodes": 1,
"number_of_data_nodes": 1,
#主分片数量
"active_primary_shards": 10,
#分片总量
"active_shards": 10,
#当前正在从一个节点迁往其他节点的分片的数量,当出现扩容或者下线node时会变动
"relocating_shards": 0,
#是刚刚创建的分片的个数,临时事件
"initializing_shards": 0,
#已经在集群状态中存在的分片,但是实际在集群里又找不着
#通常未分配分片的来源是未分配的副本
#如果你的集群是 red 状态,也会长期保有未分配分片
"unassigned_shards": 0
}
集群状态
green
所有的主分片和副本分片都已分配。你的集群是 100% 可用的。
yellow
所有的主分片已经分片了,但至少还有一个副本是缺失的。不会有数据丢失,所以搜索结果依然是完整的。不过,你的高可用性在某种程度上被弱化。如果 更多的 分片消失,你就会丢数据了。把 yellow 想象成一个需要及时调查的警告。
red
至少一个主分片(以及它的全部副本)都在缺失中。这意味着你在缺少数据:搜索只能返回部分数据,而分配到这个分片上的写入请求会返回一个异常。
进一步查询状态
level参数帮助我们进一步查询状态细节
查询索引的情况
GET _cluster/health?level=indices
查询分片状态
GET _cluster/health?level=shards
阻塞等待状态变化
你可以指定一个 wait_for_status 参数,它只有在状态达标之后才会返回。
GET _cluster/health?wait_for_status=green
用于避免网络延时导致的问题:可以用于脚本中,执行创建索引,并写入文档之间的操作。来避免因为创建需要的时间误差导致写入失败
节点监控
参考:https://www.elastic.co/guide/cn/elasticsearch/guide/current/_monitoring_individual_nodes.html#_%E7%B4%A2%E5%BC%95%E9%83%A8%E5%88%86
GET _node/stats 由于输出内容比较长,下面分段来解析重要的内容
1)开头部分
{
"cluster_name": "elasticsearch_zach",
"nodes": {
"UNr6ZMf5Qk-YCPA_L18BOQ": {
"timestamp": 1408474151742,
"name": "Zach",
"transport_address": "inet[zacharys-air/192.168.1.131:9300]",
"host": "zacharys-air",
"ip": [
"inet[zacharys-air/192.168.1.131:9300]",
"NONE"
],
节点是排列在一个哈希里,以节点的 UUID 作为键名。还有一些节点网络信息。
2)索引部分
"indices": {
"docs": {
"count": 6163666,
"deleted": 0
},
"store": {
"size_in_bytes": 2301398179,
"throttle_time_in_millis": 122850
},
返回的统计值被归入以下部分:
docs 展示节点内存有多少文档,包括还没有从段里清除的已删除文档数量。
store 部分显示节点耗用了多少物理存储。这个指标包括主分片和副本分片在内。
如果限流时间很大,那可能表明你的磁盘限流设置得过低。
"indexing": {
"index_total": 803441,
"index_time_in_millis": 367654,
"index_current": 99,
"delete_total": 0,
"delete_time_in_millis": 0,
"delete_current": 0
},
"get": {
"total": 6,
"time_in_millis": 2,
"exists_total": 5,
"exists_time_in_millis": 2,
"missing_total": 1,
"missing_time_in_millis": 0,
"current": 0
},
"search": {
"open_contexts": 0,
"query_total": 123,
"query_time_in_millis": 531,
"query_current": 0,
"fetch_total": 3,
"fetch_time_in_millis": 55,
"fetch_current": 0
},
"merges": {
"current": 0,
"current_docs": 0,
"current_size_in_bytes": 0,
"total": 1128,
"total_time_in_millis": 21338523,
"total_docs": 7241313,
"total_size_in_bytes": 5724869463
},
indexing 显示已经索引了多少文档。这个值是一个累加计数器。在文档被删除的时候,数值不会下降。还要注意的是,在发生内部 索引 操作的时候,这个值也会增加,比如说文档更新。
还列出了索引操作耗费的时间,正在索引的文档数量,以及删除操作的类似统计值。
get 显示通过 ID 获取文档的接口相关的统计值。包括对单个文档的 GET 和 HEAD 请求。
search描述在活跃中的搜索(open_contexts)数量、查询的总数量、以及自节点启动以来在查询上消耗的总时间。用query_time_in_millis / query_total 计算的比值,可以用来粗略的评价你的查询有多高效。比值越大,每个查询花费的时间越多,你应该要考虑调优了。
fetch 统计值展示了查询处理的后一半流程(query-then-fetch里的fetch)。如果fetch耗时比query还多,说明磁盘较慢,或者获取了太多文档,或者可能搜索请求设置了太大的分页(比如, size: 10000 )。
merges 包括了 Lucene段合并相关的信息。它会告诉你目前在运行几个合并,合并涉及的文档数量,正在合并的段的总大小,以及在合并操作上消耗的总时间。
在你的集群写入压力很大时,合并统计值非常重要。合并要消耗大量的磁盘I/O和CPU资源。如果你的索引有大量的写入,同时又发现大量的合并数,一定要去阅读索引性能技巧。
注意:文档更新和删除也会导致大量的合并数,因为它们会产生最终需要被合并的段 碎片 。
"filter_cache": {
"memory_size_in_bytes": 48,
"evictions": 0
},
"fielddata": {
"memory_size_in_bytes": 0,
"evictions": 0
},
"segments": {
"count": 319,
"memory_in_bytes": 65812120
},
...
filter_cache 展示了已缓存的过滤器位集合所用的内存数量,以及过滤器被驱逐出内存的次数。过多的驱逐数可能说明你需要加大过滤器缓存的大小,或者你的过滤器不太适合缓存(比如它们因为高基数而在大量产生,就像是缓存一个 now 时间表达式)。
不过,驱逐数是一个很难评定的指标。过滤器是在每个段的基础上缓存的,而从一个小的段里驱逐过滤器,代价比从一个大的段里要廉价的多。有可能你有很大的驱逐数,但是它们都发生在小段上,也就意味着这些对查询性能只有很小的影响。
把驱逐数指标作为一个粗略的参考。如果你看到数字很大,检查一下你的过滤器,确保他们都是正常缓存的。不断驱逐着的过滤器,哪怕都发生在很小的段上,效果也比正确缓存住了的过滤器差很多。
field_data 显示 fielddata 使用的内存,用以聚合、排序等等。这里也有一个驱逐计数。和filter_cache不同的是,这里的驱逐计数是很有用的:这个数应该或者至少是接近于0。因为fielddata不是缓存,任何驱逐都消耗巨大,应该避免掉。如果你在这里看到驱逐数,你需要重新评估你的内存情况,fielddata 限制,请求语句,或者这三者。
segments 会展示这个节点目前正在服务中的 Lucene 段的数量。 这是一个重要的数字。大多数索引会有大概 50–150 个段,哪怕它们存有 TB 级别的数十亿条文档。段数量过大表明合并出现了问题(比如,合并速度跟不上段的创建)。注意这个统计值是节点上所有索引的汇聚总数。记住这点。
memory 统计值展示了 Lucene 段自己用掉的内存大小。 这里包括底层数据结构,比如倒排表,字典,和布隆过滤器等。太大的段数量会增加这些数据结构带来的开销,这个内存使用量就是一个方便用来衡量开销的度量值。
3)os,process部分基本是系统的自描述信息,通俗易懂
4)Jvm部分
jvm 部分包括了运行 Elasticsearch 的 JVM 进程一些很关键的信息。 最重要的,它包括了垃圾回收的细节,这对你的 Elasticsearch 集群的稳定性有着重大影响。
因为垃圾回收对 Elasticsearch 是如此重要,你应该非常熟悉 node-stats API 里的这部分内容:
"jvm": {
"timestamp": 1408556438203,
"uptime_in_millis": 14457,
"mem": {
"heap_used_in_bytes": 457252160,
"heap_used_percent": 44,
"heap_committed_in_bytes": 1038876672,
"heap_max_in_bytes": 1038876672,
"non_heap_used_in_bytes": 38680680,
"non_heap_committed_in_bytes": 38993920,
jvm 部分首先列出一些和 heap内存使用有关的常见统计值。你可以看到有多少heap被使用了,多少被指派了(当前被分配给进程的),以及 heap被允许分配的最大值。理想情况下,heap_committed_in_bytes应该等于heap_max_in_bytes。如果指派的大小更小,JVM最终会被迫调整 heap 大小——这是一个非常昂贵的操作。如果你的数字不相等,阅读 堆内存:大小和交换 学习如何正确的配置它。
heap_used_percent指标是值得关注的一个数字。Elasticsearch被配置为当heap达到75%的时候开始GC。如果你的节点一直>=75%,你的节点正处于 内存压力 状态。这是个危险信号,不远的未来可能就有慢 GC 要出现了。如果 heap 使用率一直 >=85%,你就麻烦了。Heap在90–95% 之间,则面临可怕的性能风险,此时最好的情况是长达 10–30s 的 GC,最差的情况就是内存溢出(OOM)异常。
"pools": {
"young": {
"used_in_bytes": 138467752,
"max_in_bytes": 279183360,
"peak_used_in_bytes": 279183360,
"peak_max_in_bytes": 279183360
},
"survivor": {
"used_in_bytes": 34865152,
"max_in_bytes": 34865152,
"peak_used_in_bytes": 34865152,
"peak_max_in_bytes": 34865152
},
"old": {
"used_in_bytes": 283919256,
"max_in_bytes": 724828160,
"peak_used_in_bytes": 283919256,
"peak_max_in_bytes": 724828160
}
}
},
新生代,幸存代,老年代等使用情况,通常并不重要
"gc": {
"collectors": {
"young": {
"collection_count": 13,
"collection_time_in_millis": 923
},
"old": {
"collection_count": 0,
"collection_time_in_millis": 0
}
}
}
gc 部分显示新生代和老生代的垃圾回收次数和累积时间。大多数时候你可以忽略掉新生代的次数:这个数字通常都很大。这是正常的。
与之相反,老生代的次数应该很小,而且 collection_time_in_millis 也应该很小。这些是累积值,所以很难给出一个阈值表示你要开始操心了(比如,一个跑了一整年的节点,即使很健康,也会有一个比较大的计数)。这就是像 Marvel 这类工具很有用的一个原因。GC 计数的 时间趋势 是个重要的考虑因素。
GC 花费的时间也很重要。比如,在索引文档时,一系列垃圾生成了。这是很常见的情况,每时每刻都会导致 GC。这些 GC 绝大多数时候都很快,对节点影响很小:新生代一般就花一两毫秒,老生代花一百多毫秒。这些跟 10 秒级别的 GC 是很不一样的。
我们的最佳建议是定期收集 GC 计数和时长(或者使用 Marvel)然后观察 GC 频率。你也可以开启慢 GC 日志记录,在 日志记录 小节已经讨论过。
5)线程池
Elasticsearch 在内部维护了线程池。 这些线程池相互协作完成任务,有必要的话相互间还会传递任务。通常来说,你不需要配置或者调优线程池,不过查看它们的统计值有时候还是有用的,可以洞察你的集群表现如何。
"index": {
"threads": 1,
"queue": 0,
"active": 0,
"rejected": 0,
"largest": 1,
"completed": 1
}
每个线程池会列出已配置的线程数量( threads ),当前在处理任务的线程数量( active ),以及在队列中等待处理的任务单元数量( queue )。
如果队列中任务单元数达到了极限,新的任务单元会开始被拒绝,你会在 rejected 统计值上看到它反映出来。这通常是你的集群在某些资源上碰到瓶颈的信号。因为队列满意味着你的节点或集群在用最高速度运行,但依然跟不上工作的蜂拥而入。
这里的一系列的线程池,大多数你可以忽略,但是有一小部分还是值得关注的:
indexing
普通的索引请求的线程池
bulk
批量请求,和单条的索引请求不同的线程池
get
Get-by-ID 操作
search
所有的搜索和查询请求
merging
专用于管理 Lucene 合并的线程池
5)文件系统与网络
fs,transport,http这些都相对易懂。比较好理解,简单说一下
transport 显示和 传输地址 相关的一些基础统计值。包括节点间的通信(通常是 9300 端口)以及任意传输客户端或者节点客户端的连接。
http 显示 HTTP 端口(通常是 9200)的统计值。如果你看到 total_opened 数很大而且还在一直上涨,这是一个明确信号,说明你的 HTTP 客户端里有没启用 keep-alive 长连接的。
6)断路器
跟 fielddata 断路器(在 断路器 介绍过)相关的统计值:
"fielddata_breaker": {
"maximum_size_in_bytes": 623326003,
"maximum_size": "594.4mb",
"estimated_size_in_bytes": 0,
"estimated_size": "0b",
"overhead": 1.03,
"tripped": 0
}
这里你可以看到断路器的最大值(比如,一个请求申请更多的内存时会触发断路器)。这个部分还会让你知道断路器被触发了多少次,以及当前配置的间接开销。间接开销用来铺垫评估,因为有些请求比其他请求更难评估。
主要需要关注的是 tripped 指标。如果这个数字很大或者持续上涨,这是一个信号,说明你的请求需要优化,或者你需要添加更多内存(单机上添加,或者通过添加新节点的方式)。
集群统计
提供跟节点统计类似的信息,但是针对的是整个集群的情况,并且更加简洁
GET _cluster/stats
索引统计
以索引为中心,看统计值
单个关注的索引,多个索引,全部索引
GET my_index/_stats
GET my_index,another_index/_stats
GET _all/_stats
返回的统计信息和 节点统计 的输出很相似:search 、 fetch 、 get 、 index 、 bulk 、 segment counts 等等。
索引为中心的统计在有些时候很有用,比如辨别或验证集群中的 热门 索引,或者试图找出某些索引比其他索引更快或者更慢的原因。
实践中,节点为中心的统计还是显得更有用些。瓶颈往往是针对整个节点而言,而不是对于单个索引。因为索引一般是分布在多个节点上的,这导致以索引为中心的统计值通常不是很有用,因为它们是从不同环境的物理机器上汇聚的数据。
索引为中心的统计作为一个有用的工具可以保留在你的技能表里,但是通常它不会是第一个用的上的工具。
等待中的任务
有一些任务只能由主节点去处理,比如创建一个新的索引或者在集群中移动分片。由于一个集群中只能有一个主节点,所以只有这一节点可以处理集群级别的元数据变动。在 99.9999% 的时间里,这不会有什么问题。元数据变动的队列基本上保持为零。
在一些 罕见 的集群里,元数据变动的次数比主节点能处理的还快。这会导致等待中的操作会累积成队列。
等待中的任务 API 会给你展示队列中(如果有的话)等待的集群级别的元数据变更操作:
GET _cluster/pending_tasks
无等待任务情况
{
"tasks": []
}
有等待任务情况
{
"tasks": [
{
"insert_order": 101,
"priority": "URGENT",
"source": "create-index [foo_9], cause [api]",
"time_in_queue_millis": 86,
"time_in_queue": "86ms"
},
{
"insert_order": 46,
"priority": "HIGH",
"source": "shard-started ([foo_2][1], node[tMTocMvQQgGCkj7QDHl3OA], [P],
s[INITIALIZING]), reason [after recovery from gateway]",
"time_in_queue_millis": 842,
"time_in_queue": "842ms"
},
]
}
可以看到任务都被指派了优先级(比如说URGENT要比HIGH更早的处理,任务插入的次序、操作进入队列多久,以及打算处理什么。在上面的列表中,有一个 创建索引(create-index) 和启动分片(shard-started) 的操作在等待。
Q:什么时候应该担心等待中的任务?
主节点很少会成为集群的瓶颈。唯一可能成为瓶颈的是集群状态非常大而且更新频繁。例如,如果你允许客户按照他们的意愿创建任意的动态字段,而且每个客户每天都有一个独立索引,那么你的集群状态会变得非常大。集群状态包括 ( 但不限于 )所有索引及其类型,以及每个索引的全部字段。
所以如果你有 100000 客户,然后每个客户平均有1000个字段,而且数据有90天的保留期—这就有九十亿个字段需要保存在集群状态中。不管它何时发生变更,所有的节点都需要被通知。主节点必须处理这些变动,这需要不小的CPU开销,加上推送更新的集群状态到所有节点的网络开销。
这就是那些可以看到集群状态操作队列上涨的集群。没有简单的办法可以解决这个问题,不过你有三个选择:
--使用一个更强大的主节点。不幸的是,这种垂直扩展只是延迟这种必然结果出现而已。
--通过某些方式限定文档的动态性质来限制集群状态的大小。
--到达某个阈值后组建另外一个集群。
cat API
用 Linux 的 cat 命令命名,这些 API 也就设计成像 *nix 命令行工具一样工作了。