写在前面
从初次了解elastic产品到正式投入使用,拖拖拉拉的也有小半年了,刚接触的时候看到一些帖子都是安装教程,后来看到一些都是深入教程,此篇文章较居中一点,总结了我在踩的一些坑和记录一些周边插件的使用方式、方法,便于自己后续回顾,也希望能给新用户一些引导,少走一些弯路;核心其实是想表达一下对rockybean和KennyW的爱,这期间非常感谢两位的协助,在非工作日深夜排查问题多次,正文多处采用二位给予的讲解,万分感谢。
ElasticSearch简介
- 基于Lucene构建的分布式,RESTful搜索和分析引擎;
- 实时搜索、分析,稳定,可靠,快速;
- JAVA编写,开源,使用JSON开源通过HTTP来索引数据;
项目介绍
- 历史:
由前同事工作交接,原有一套ES1.7集群,但是经常jvm跑满导致集群不可用,没有ES调优经验,甚至没有使用经验,从零了解ES,过度版本ES5.3~5.6; - 背景:
数据源主要是Nginx访问日志,由于Nginx是集群,相应的日志分布落在每台机器上,当对整体日志做一些数据分析、故障排查等,因数据分散且量很大,通过脚本已经不能做分析处理了,为了对Nginx日志做全文搜索、分析,实时监控等,所以踏上ELK之路; - 数据量:
每天6-7T,Docs在100亿左右
架构图
第一部分:日志收集(Nginx + Rsyslog)
- 采用Nginx内置的syslog模块,每台机器启用本地的rsyslog,通过UDP方式传输本地的514端口(Rsyslog),然后Rsyslog在将数据转发至Kafka;
- 选型比较:日志收集有很多种方式,如flume、filebeat、lua脚本等,但是这些组件都需要额外安装客户端,而rsyslog在linux都集成了。简单对比过Rsyslog和其他区别,首先在Nginx中的syslog对于一个更改相对灵活,JSON日志与落地日志毫无干系(目前是两份格式:人肉读取的本地日志格式,机器读取JSON网络传输格式),日志输出直接通过网络传输走(网络消耗很低)不受本地磁盘影响,对不同的server_name或location等可以灵活的修改,总之控制权在Nginx手里,Nginx的维护同学就可以自定义源数据格式,管理成本相对较低;
-
Rsyslog配置(双打Kafka)
现有的版本是0.8,而刚开始测试的用logstash5.x需要kafka0.10(最终hangout替换logstash),所以新搭建了一组新的集群,Rsyslog向两个Kafka集群分别写数据,配置如下Module (load="imudp") Module (load="omkafka") Input (type="imudp" port="514") Module (load="mmsequence") $MaxMessageSize 4k local5.none /var/log/messages local5.none @log.domain.com:514 set $!newmsg = replace($msg,'\\x','\\u00') template(name="kafka_topic" type="string" string="%programname%") template(name="kafka_msg" type="string" string="%!newmsg%") if ($syslogfacility-text == 'local5' and $syslogseverity-text == 'info') then{ action(type="omkafka" topic="kafka_topic" partitions.auto="on" dynatopic="on" dynatopic.cachesize="1000" confParam=["compression.codec=snappy"] #kafka broker addr broker=["10.10.10.1:9092","10.10.10.2:9092",] template="kafka_msg" errorfile="/var/log/omkafka/log_kafka_failures.log") action(type="omkafka" topic="kafka_topic" partitions.auto="on" dynatopic="on" dynatopic.cachesize="1000" confParam=["compression.codec=snappy"] #kafka broker addr broker=["20.20.20.1:9092","20.20.20.2:9092",] template="kafka_msg" errorfile="/var/log/omkafka/log_kafka_failures.log") stop }
-
配置Nginx JSON格式日志
log_format json_format '{"@timestamp":"$time_iso8601",' '"cookie_id":"$cookie_id",' #内部cookie_id '"client_ip":"$remote_addr",' '"remote_user":"$remote_user",' '"request_method":"$request_method",' '"domain":"$host",' '"user_agent":"$http_user_agent",' '"xff":"$http_x_forwarded_for",' '"upstream_addr":"$upstream_addr",' '"upstream_response_time":"$upstream_response_time",' '"request_time":"$request_time",' '"size":"$body_bytes_sent",' '"idc_tag":"tjtx",' '"cluster":"$host_pass",' '"status":"$status",' '"upstream_status":"$upstream_status",' '"host":"$hostname",' '"via":"$http_via",' '"protocol":"$scheme",' '"request_uri":"$request_uri",' '"http_referer":"$http_referer"}';
-
Nginx内置syslog模块配置,并且引用刚刚定义的json日志格式
access_log syslog:local5:info:127.0.0.1:514:nginx_aggregation_log json_format; #nginx_aggregation_log 这是自定义的Topic
NginxSyslog模块介绍
注:
1) UDP传输虽快,但是以太网(Ethernet)数据帧的长度必须在46-1500字节之间,UDP不能像TCP重组数据包,去除IP和UDP的数据包,最终可使用只剩1472字节。如果传输大于这个长度的消息,并不会想UDP本身一样直接丢弃,只是会损坏源数据格式,截断超过限制字节以外的数据;
2) 对于Nginx日志来说,只要不保留POST数据,基本一条消息不会超过限制字节,我在NginxSyslog介绍中没看到支持TCP,用lua脚本实现的TCP方式传输,但是看了很多帖子都不建议在Nginx中用TCP日志传输。就是因为TCP传输可靠,但像网络抖动、传输异常,可能会不停的重试多次或等待,直接影响这条请求,也直接影响到了用户;
3) 消息超过了UDP传输限制怎么办,我这目前是保留一条消息的重要字段,如上述的json_format的格式,将 request_uri、http_referer等可能会较大的字段放到最后,如果真的发现消息不完整,直接丢弃http_referer,取request_uri问号前的内容;(在logstash或hangout中filters实现,具体配置详见下文Hangout-filters)
第二部分-存储中间件(Kafka)
- Kafka性能很强,顺序写入磁盘,高吞吐量的分布式发布订阅消息系统
- Kafka一直不是瓶颈,也没太多深入优化,Topic数据保留了12小时,副本为1
- 针对不同的Topic,对Partition的数量有稍微改变,目前是5台服务器,之前简单测过增加Partition的性能,从8、16、32、64增加来看,明显情况就是Partition增加,CPU使用也会随之增加,因为kafka本身不是瓶颈,其他明显问题也未遇到;
- 目前这边最大的Topic是一天近5T数据,64Partition没有任何问题,部分小的Topic都是16Partitio,Kafka整个集群的CPU空闲都在80%以上,内存、IO均无压力,后续也考虑缩减机器。这边的kafka团队有个建议值,供大家参考:【每天数据规模小于50G选择4分区、50G且小于100G选择8分区、大于100G且小于500G选择16分区、大于500G,选择24分区】
-
Kafka监控插件:kafka-monitor 和 kafka-manager
注:
目前我们这kakfa集群是kafka_2.10-0.8.1.1版本,但是logstash5.x对kafka有版本要求>0.10版本。后来采用hangout,更换了几个jar包解决了此问题
第三部分-数据搬运工(Hangout)
-
模仿logstash做的一个应用,功能没有logstash多,但是基本使用都有了,java编写,性能可以翻好几倍,用到的功能就是从kafka订阅消息,做一些简单的过滤,然后写入ES;目前hangout部署到2台服务器上,每个进程开8G内存,CPU在60-70左右;
inputs: - Kafka: topic: nginx_aggregation_log: 32 codec: json consumer_settings: group.id: es-nginx_aggregation_log zookeeper.connect: "10.10.10.1:2181,10.10.10.2:2181" auto.commit.interval.ms: "20000" socket.receive.buffer.bytes: "1048576" fetch.message.max.bytes: "1048576" num.consumer.fetchers: "1" filters: - Filters: if: - '<#if message??>true</#if>' #如果不是完整的JSON,会出现message,则走此逻辑 filters: - Grok: match: - '(?<msg>{"@timestamp":.*"request_uri":([^\?]+)\?)' #正则匹配@timestamp开始到request_uri后边的第一个?截止 - Gsub: fields: msg: ['$','"}'] #补全符号,完整新的JSON格式 - Json: field: msg remove_fields: ['message'] #干掉错误的数据 - Convert: fields: request_time: to: float remove_if_fail: true upstream_response_time: to: float remove_if_fail: true size: to: integer remove_if_fail: true - GeoIP2: source: client_ip database: '/opt/soft/hangout/etc/other/GeoLite2-City.mmdb' - Json: field: geoip - Remove: fields: - msg - Add: fields: request_url: '<#assign a=request_uri?split("?")>${a[0]}' #request_uri这个term的cardinality很高,所以?前用于聚合,原有的用于搜索 if: - '<#if request_uri??>true</#if>' outputs: - Elasticsearch: cluster: es-nginx timezone: "Asia/Shanghai" hosts: "10.10.10.1:9300,10.10.10.2:9300" index: 'hangout-nginx_aggregation_log-%{+YYYY.MM.dd}'
- Hangout进程管理工具(supervisord)
主要是守护hangout进程,在web界面对hangout进行启、停、重启等操作 topic: nginx_aggregation_log: 32,无论是logstash还是hangout都有这个概念,这个32代表需要建立多少子线程去kafka读取数据,数量最好与Partition相等,如果少于Partition,会一个线程同时去2个Partition读取消息,若大于Partition则会有不工作的进程
第四部分-Elasticsearch(后面简称ES)
-
硬件环境
CPU:32C,内存:128G ,硬盘:STAT 6T * 12,网卡:万兆
-
软件环境:
【系统】: Centos7 内核3.10 【JDK】: 1.8.0_66/31G (据说此版本JDK有BUG,请安装最新JDK) 【系统参数修改1】: vm.swappiness=1 [降低对硬盘的缓存] 【系统参数修改2】: vm.max_map_count=262144 [Elasticsearch针对各种文件使用NioFS和MMapFS的混合。以便有足够的虚拟内存可用于mmapped文件]
-
ES配置文件
cluster.name: es-nginx node.name: 10.10.10.1 #为后期冷热数据使用 node.attr.rack_id: hdd path.data: /data path.logs: /opt/logs/elasticsearch/ network.host: 0.0.0.0 http.port: 9200 #设置新节点被启动时能够发现的主节点列表 discovery.zen.ping.unicast.hosts: ["10.10.10.1","10.10.10.2","10.10.10.3"] #防止脑裂(n/2+1) discovery.zen.minimum_master_nodes: 2 node.master: true node.data: false
-
ES跳入的第一坑:node.master与node.data同时服务
刚刚开始测试ES的第一个版本是ES5.3,先搞了3台机器,每个机器一个节点,配置是master和data共同提供服务,高可用架构集群搭建完成,但是写入性能特别差,cpu使用在20-30%,少量io.wait,下图是当时3w左右的性能图当时觉得既然ES硬件很空闲一定是logstash出问题了,查看logstash确实有很严重的Full GC,开始从2台服务器扩至4台服务器,后来发现无果,期间各种调整ES的shard的数量都没效果,又怀疑kafka性能,从2、4、6、8...64分区依旧无果。当时这个坑可爬了一段时间,后来在Google的游荡中无意中看到帖子说,不要将master和data都启用,然后我照着做了改变,master单点,data两台,问题搞定,效果图找不到了,起码翻倍是有的;
[Master除了网卡,其他没什么消耗]
-
template
因shard数量、字段类型、其他设置等都是都是在创建时生成,所以要提前创建好相应的模板,便于规范管理和引用,下面针对shard和aliases做的一些设置,如下:
{ "template": "agg-nginx-*", "aliases": { "agg-nginx": {} }, "settings": { "number_of_shards": 4, "number_of_replicas": 1, "index.routing.allocation.include.rack_id": "ssd" }
通过上述配置PUT到 _template/ur_name下在分片上的定义已经成功,但是像agg-nginx-和test-agg-test-这样的2个索引名字,即使你创建了另一个"template": "agg-nginx-test-*"的模板依旧都匹配第一个,当然换名字最简单有效,在template的order的是专门解决这个问题的。默认创建"order": "0",值越高优先级越高,所以在想要先匹配的将order值调高即可
-
mapping
ES的mapping非常类似于静态语言中的数据类型:声明一个变量为int类型的变量, 以后这个变量都只能存储int类型的数据。同样的, 一个number类型的mapping字段只能存储number类型的数据。同语言的数据类型相比,mapping还有一些其他的含义,mapping不仅告诉ES一个field中是什么类型的值, 它还告诉ES如何索引数据以及数据是否能被搜索到
下列是一个删减版的mapping
"mappings": { "ngx_log": { "_all": { "enabled": false }, "properties": { "@timestamp": { "type": "date" }, "client_ip": { "type": "ip" }, "domain": { "type": "keyword" }, "geoip": { "properties": { "city_name": { "type": "keyword" }, "country_name": { "type": "keyword" }, "latitude": { "type": "float" }, "location": { "type": "geo_point" }, "longitude": { "type": "float" }, } }, "request_time": { "type": "float" }, "request_url": { "type": "keyword" }, "status": { "type": "keyword" ype": "keyword" }, } } }
_all字段
该_all字段是一个特殊的catch-all字段,它将所有其他字段的值连接成一个大字符串,使用空格作为分隔符,然后对其进行分析和索引,但不存储。也就是说它能被查询,但不能被取回显示。因为Nginx每个Key对应的value都是提前定义好的,所以不用全文查询,不需要开启_all字段,另外也节省了一半的存储空间默认的text类型
上边这英文有点多,其实简单理解就是不分词,你就最好别用text了,而且Text类型也会相应的多占用空间,依照上述,数据主要是日志分析,每条数据的格式已经很明确,主要用于日志分析,所以不需要分词。像一些所有引擎的业务更适合需要分词;- 比如说像这个字段,get_ip中location这个字段类型默认text,但是如果不指定geo_point类型,根本无法使用地图功能,类型的指定是很重要的
- 向request_time这样的数据类型需要做计算,比如说平均值、和、大于、小于等等的,默认的text也能使用,但是效率远远小于float类型
- 字段类型有很多种,什么IP啊、DATE啊等等,根据相应的需要去官网查看详解吧,mapping-types介绍
-
shard & replicas
[摘取部分苏若年博客内容]
1)分片算法:
shard = hash(routing) % number_of_primary_shards
routing值是一个任意字符串,它默认是_id但也可以自定义,这个routing字符串通过哈希函数生成一个数字,然后除以主切片的数量得到一个余数(remainder),余数的范围永远是0到number_of_primary_shards - 1,这个数字就是特定文档所在的分片。这也解释了为什么主切片的数量只能在创建索引时定义且不能修改:如果主切片的数量在未来改变了,所有先前的路由值就失效了,文档也就永远找不到了。
所有的文档API(get、index、delete、bulk、update、mget)都接收一个routing参数,它用来自定义文档到分片的映射。自定义路由值可以确保所有相关文档.比如用户的文章,按照用户账号路由,就可以实现属于同一用户的文档被保存在同一分片上。
2)分片与副本交互:新建、索引和删除请求都是写(write)操作,它们必须在主分片上成功完成才能复制到相关的复制分片上,下面我们罗列在主分片和复制分片上成功新建、索引或删除一个文档必要的顺序步骤:
1、客户端给Node 1发送新建、索引或删除请求。
2、节点使用文档的_id确定文档属于分片0。它转发请求到Node 3,分片0位于这个节点上。
3、Node 3在主分片上执行请求,如果成功,它转发请求到相应的位于Node 1和Node 2的复制节点上。当所有的复制节点报告成功,Node 3报告成功到请求的节点,请求的节点再报告给客户端。
客户端接收到成功响应的时候,文档的修改已经被应用于主分片和所有的复制分片。你的修改生效了。
一个索引要分多少片?什么时候该扩容?
取决于硬件和你对响应速度的要求,一般来说一个shard的数据量控制在1、2千万的级别,速度都还好,过亿会比较缓慢。 但是任何事物都有两面,shard划分比较小,必然数量就比较多。 在用户做1、2天数据搜索的时候可能还好,如果搜更长时间的数据,一次搜索需要并行搜索的shard就比较多。如果节点数量有限,就会比较吃力,需要扩充更多的节点 -
routing
据说是优化之王道,经常拿城市举的例子,比如说我想看下网站的北京pv是多少,如果按照默认hash逻辑,一定要全shard扫描,然后聚合结果,但是如果提前设置好routing,比如说指定城市字段做hash计算,routing值一样的放到特定几个分片,这样查起来也不需要全shard扫了;这样弊端就是会造成shard大小不均,所以routing优化需要花一些功夫来观察、测试;目前kibana还不支持routing查询,所以目前在kibana上还没有使用routing,这是优化的重点所以先记录下来。后续我的想法是,像nginx日志的域名的字段都是英文字母,针对首字母做下routing,当想看某一个域名时不在全盘扫,查询优化会有明显效果,后续有结果在与大家分享;
另外hangout开始对routing的支持,后来在GitHub提了一个小issue,很快就加上了,点个赞;
第五部分-Kibana图形展示
Kibana是一个开源的分析与可视化平台,设计出来用于和Elasticsearch一起使用的。你可以用kibana搜索、查看、交互存放在Elasticsearch索引里的数据,使用各种不同的图表、表格、地图等kibana能够很轻易地展示高级数据分析与可视化。
先介绍下几块功能:
- X-pack可以免费使用部分功能,最强就是monitoring功能了,基本重要指标都收集了
- Dev_tools的代码补全神器,类似于IDE;还有强劲的可视化Search Profiler,便于直接定位问题点
- 其他就是常用的Discover、Visualize、Dashboard、Timelion搜索和绘图功能了,没有找到合适的帖子,直接吧迷妹儿的帖子放过来吧,主要介绍了如何优雅的使用kibana的搜索框
- 还有就是Elastic官方提供了一个Demo,便于大家做图形展示的时候参考和借鉴
(╯﹏╰)吐槽一下,用SF写到现在,Chrome都快没法用了,打完字一会才显示出来
直接上几张最终成果图吧
- [QPS展示]
- [QPS环比]
- [异常状态码趋势]
- [慢请求QPS]
- [状态码比例]
- [Domin与URL TOP]
- 再来一张深夜[热图],北京和上海的孩子总是不碎觉
-
上图中环比图是Timelion完成的,其他都是Visualize的功能
Timelion语法:.es(index=index1,timefield=@timestamp).label('Today').title(QPS).color(#1E90FF), .es(offset=-24h,index=index2,timefield=@timestamp).label('Yesterday').lines(fill=1,width=0.5).color(gray)
- kibana认证问题
由于x-pack试用版将认证功能被阉割掉了,直接暴露内容太风险,所以利用Nignx插件auth_basic的功能,做了一个简单验证模块
最后调优
核心问题:查询慢、查询15Min数据都超时
- 数据量:
每天数据在6-7T,Docs在100亿左右,其中一个大的索引在4-5T
-
ES升级
版本升级会带来一些新特性,以及一些bug修复,当然也会引发新问题,我就踩到了新版本的坑;
ES5.x升级到5.5是滚动升级,相对升级步骤简单,但是由于数据太大,动辄几十T,一台台完成升级,却是一个漫长的过程。另外,1.x升级据说很痛苦,有兴趣的去看看,连接放在这里reindex-upgrade
简述下5.x升级的过程
1)关掉分片自动分配:"cluster.routing.allocation.enable": "none"
2)强制刷新,尽可能将缓存中的数据,写入磁盘: _flush/synced
3)停止一个节点,开始升级及升级插件,然后启动
4)打开分片自动分配:"cluster.routing.allocation.enable" :"all"
5)等待集群恢复至绿色,继续下一台
- 扩容
扩容很简单,没什么可说的,除了配置文件中node.name不一样,其他都一样;
最初扩容只是将data节点从3台扩至20台,写入没问题,1-2k的数据,单机性能写入在30-40k; -
mapping字段优化:
主要像上述的mapping介绍,做类型优化,不分词的keyword,数学计算的改成整型or浮点等
status这个字段的类型,value一般都是200、301、302、404、502,最多估计也就几百个,像这样的字段就不适合做long,long类型的索引是为范围查找优化的,用的是二叉树这样的索引,适合值范围比较大的字段,比如body_size,可能最大值和最小值相差很多,而用keyword索引是倒排,只用存放一个所有status的词典,所以keyword更适合
-
shard数量调整:
开始主分片数量是每台机器1个,副本1(20shard x 1replicas),每个shard已经达到将近200G,看这个量级已经超过官方建议值30~50G之间(具体要根据实际情况测试为准)。于是开始将数量翻倍40shard * 1replicas,调整后查询并没有明显改善,对写入没有什么改变,继续double,依旧没效而且分片越多,写入的时候消耗的CPU就越高
如果过度分配shard,就增大了Lucene在合并分片查询结果时的复杂度,从而增大了耗时,在新建索引时,更是一笔大的开销。
一般来说,刚开始的时候尽量少分片为佳, 只有到一个分片上数据太多,单次查询太慢在考虑加分片。
加分片也有负面作用,提高了并发量,随之开销就会越大,更多的shard一般也伴随更多的segment文件。如果说节点数量没变,每个节点上会有更多的小文件,搜索的时候并发量是高了,前提是在磁盘io和cpu都还能应付的过来的时候,速度才会快 -
拆分索引
更改shard已经得不到显著效果,于是从拆索引下手。数据都是Nginx日志,从源头拆分没什么好的方法,于是从hangout这层开始做处理,很简单就是将域名的第一个字母取出来,写入相应的索引时候带过去,例如:nginx-log-{首字母},这样一拆,一下创建26个索引(shard20 * 1replicas),CPU立马load 30+,负载直接上来,然后ES数据还跟不上,拒绝了很多内容,如下图,最终还是无果而告终此方案上述的索引拆分是比较傻瓜式,首先已知的问题就是可能A开头的域名很大,其他很小就是很不均匀
- Pcle-SSD测试
经过上面的折腾,已经没有什么突破点,于是借用了8台SSD的机器,修改ES配置node.attr.rack_id: ssd,将数据写入SSD,全程io无压力,写入一个一天4-5T的索引,CPU在50%左右,但是查询依旧不给力,经过无数shard调整依旧不给力,IO一直没空闲,最终结论就是没有将ssd的性能发挥出来,还是未解决根本问题 - kibana BUG
之前没有ES相关经验,能想到的办法、关注点都没有什么在突破的地方了,在束手无策的时候,又非常有幸认识了KennyW,再次感谢KennyW的支持;问题根本原因是kibana5.4-5.5这两个版本在查询问题,为了节省空间,我关掉了_all这个字段,如果搜索框不输入内容的时候,则会补充*查询所有字段,生成的bool条件就非常多,引发kibana的缺陷,引用:
当时的版本KennyW给的知道是临时修复方法如下:
详细问题请看这里ES 5.4+ 引起的Kibana性能问题,Kibana5.5.2后已经修复此问题,所以最终版本还是升级到了5.6,也是目前的最新版 -
回归HDD
经过SSD的折腾,查到了kibanaBug问题,其实SSD并未发现其他的性能亮点,经过与KennyW的交流,他们自己有做过在SSD和多块HDD的盘对比,写入量并无明显提升,所以在磁盘并不是真正的瓶颈,查询方面SSD明显提高。但是SSD的高额付出换来那么几秒钟的意义不大。相对,对一些公司的搜索业务,数据量级小,还有像一些监控业务,要求实时性非常高,对SSD是很好的选择,速度快,容量不需要太多,也比较经济实惠[KennyW经验指导]
当做复杂计算,bucket很多的时候主要消耗的是CPU和内存,磁盘并不是瓶颈,hdd和ssd在我们实际用下来,感觉在大数据场景分别不大,ssd优势在大量的随机磁盘io,ES本身做了很多优化,让数据变成顺序的磁盘访问,而且搜索过的数据块,ES都能利用文件系统缓存加速,所以即使使用hdd,可能第一次搜索磁盘访问会带来额外的几秒耗时,但多次执行同一个搜索时,后面几次几乎没什么磁盘io开销,速度会有明显提升 - RAID0的重要性
当所有的集群全部更改回hdd的集群时候,发现一个之前没太关注的指标,每块盘的利用率。刚开始解决的时候我们的机器都是裸盘直接挂上,path.data:data1,data2,data...这样挂载,但是在5.x版本发现这样磁盘问题(CMD:iostat -x 1)这样看来很奇怪,为什么这块盘这么忙,多观察一会发现,一会又发现其他的盘又很忙,然后df查看磁盘使用率,扎心了
挂了这么多盘,真实写入连一半的盘都不到,既然这样,没得说的了,直接上raid0吧 -
收尾
做到目前这些调整,现在一天小于1T的索引任何查询都没有问题,查询在10s左右返回所有数据(上图kibana展示),但是那个一天4-5T的索引还是有问题,一查询IO就跑满,到30s直接超时了,先说下IO跑满的问题吧,问题是request_uri台过于分散,聚合出现问题如图:15分钟就出现了2000多万不同值,如果长时间计算,不可想象的恐怖【KennyW指导】
request_uri 会产生大量的磁盘IO。 ES做terms聚合的时候,为了节省内存,不会将所有term的内容直接读出来做bucket,因为有些term的内容可能很长,会比较耗费内存。 所以他会借助一种叫做oridinals的数据结构, 这种数据结构类似这样
1 abc
2 efg
3 hfa
.............一个该字段所有不同值的顺序列表。 做分桶聚合的时候,只需要拿这个顺序数字做key就可以了,等聚合出结果,再通过这个key查ordinals表,得到实际的key值填充结果。 但是这个ordinals是随着segment 文件生成的,每个segment文件的ordinals顺序可能不一样。 因此在聚合的时候,需要一个全局的global ordinals。 这个数据结构是在聚合的时候,实时生成后缓存在内存里的。 如果某个字段的不同值非常多,计算价值非常的昂贵,那么这个构造过程就非常的缓慢,除了大量的磁盘IO消耗,还会有很大的内存消耗。
下图是关掉这个有问题的visualize前后对比图,虽然不快,但是latency降了很多
后来对uri的?后边的参数全部丢弃,像这样的问题只能减少然后做聚合使用,原有数据做搜索使用,但是由于数据太大,做复杂计算还是会超时30s,这种情况只能是降低team的cardinality,或者加分片、加机器或者拆索引了,所以对kibana的超时时间做了一点调整,还有一些周边小的修改如下
1)kibana默认的30s超时改成2min,kibana.yml中修改elasticsearch.requestTimeout: 120000
2)kibana默认地图使用高德,kibana.yml中新增tilemap.url: 'http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}'
3)结合cerebro插件,高效管理索引
后续
到这里基本的一期项目算是结束,虽然部分查询并没有迅速返回,但是基本数据都可以展示,后续会关注几个点继续深入优化和调整,有结果在与大家分享
- routing优化查询
- curator管理过期索引
- 比较大index索引做拆分
- 增加client节点,减少集群影响
- 冷热数据分离(node.attr.rack)、定期对冷数据force_merge,优化查询速度
总结
首先在这里还是先要感谢rockybean和KennyW的大力支持。
对自己的总结就是,对ES经验太少,踩了很多不必要的坑,另外就是没有好好统一阅读下官网文档,这样极其影响效率与进度,好多时候也会束手无策,不知从何下手。
第一次写技术帖,而且时间稍紧,有哪些地方写的不好或敏感烦请指出,我会及时更正。希望这篇文章,能给予像我这样的小白用户少踩一点坑,多一点爱。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。