2.1 集群数据量规划
在业务初期,经常被问到的问题,要几个节点的集群,内存、CPU要多大,要不要SSD?最主要的考虑点是:你的目标存储数据量是多大?可以针对目标数据量反推节点多少。
2.2要留出容量Buffer
Elasticsearch有三个警戒水位线,磁盘使用率达到85%、90%、95%。
2.3 ES集群各节点尽量不要和其他业务功能复用一台机器
因为es还是比较吃内存的
2.4 磁盘尽量选择SSD
如果业务对写入、检索速率有较高的速率要求,建议使用SSD磁盘, SSD磁盘比机械硬盘的速率提升了5倍
2.5 内存配置要合理
官方建议:堆内存的大小是官方建议是:Min(32GB,机器内存大小/2)。Medcl和wood大叔都有明确说过,不必要设置32/31GB那么大,建议:热数据设置:26GB,冷数据:31GB。总体内存大小没有具体要求,但肯定是内容越大,检索性能越好。经验值供参考:每天200GB+增量数据的业务场景,服务器至少要64GB内存。除了JVM之外的预留内存要充足,否则也会经常OOM。
2.6 CPU核数不要太小
CPU核数是和ESThread pool关联的。和写入、检索性能都有关联。建议:16核+.
2.7 集群节点个数无需奇数
ES内部维护集群通信,不是基于zookeeper的分发部署机制,所以,无需奇数。但是discovery.zen.minimum_master_nodes的值要设置为:候选主节点的个数/2+1,才能有效避免脑裂。
3.1 设置多少个索引
建议根据业务场景进行存储(根据关系数据库差不多)
3.2 设置多少分片
建议根据数据量衡量。经验值:建议每个分片大小不要超过30GB
3.3 分片数设置
建议根据集群节点的个数规模,分片个数建议>=集群节点的个数。5节点的集群,5个分片就比较合理。注意:除非reindex操作,分片数是不可以修改的。
3.4 副本数设置
除非你对系统的健壮性有异常高的要求,比如:银行系统。可以考虑2个副本以上。否则,1个副本足够。注意:副本数是可以通过配置随时修改的。
3.5 不要再在一个索引下创建多个type
即便你是5.X版本,考虑到未来版本升级等后续的可扩展性。建议:一个索引对应一个type。6.x默认对应_doc,5.x你就直接对应type统一为doc。
3.6 按照日期规划索引
随着业务量的增加,单一索引和数据量激增给的矛盾凸显。按照日期规划索引是必然选择。
好处1:可以实现历史数据秒删。很对历史索引delete即可。注意:一个索引的话需要借助delete_by_query+force_merge操作,慢且删除不彻底。
好处2:便于冷热数据分开管理,检索最近几天的数据,直接物理上指定对应日期的索引,速度快的一逼!
操作参考:模板使用+rollover API使用。
3.7 务必使用别名
ES不像mysql方面的更改索引名称。使用别名就是一个相对灵活的选择。
4.1 不要使用默认的Mapping
默认Mapping的字段类型是系统自动识别的。其中:string类型默认分成:text和keyword两种类型。如果你的业务中不需要分词、检索,仅需要精确匹配,仅设置为keyword即可。
根据业务需要选择合适的类型,有利于节省空间和提升精度,如:浮点型的选择。
4.2 Mapping各字段的选型流程
4.3 选择合理的分词器
常见的开源中文分词器包括:ik分词器、ansj分词器、hanlp分词器、结巴分词器、海量分词器、“ElasticSearch最全分词器比较及使用方法” 搜索可查看对比效果。
如果选择ik,建议使用ik_max_word。因为:粗粒度的分词结果基本包含细粒度ik_smart的结果。
4.4 date、long、还是keyword
根据业务需要,如果需要基于时间轴做分析,必须date类型;如果仅需要秒级返回,建议使用keyword。
5.1 要不要秒级响应
Elasticsearch近实时的本质是:最快1s写入的数据可以被查询到。如果refresh_interval设置为1s,势必会产生大量的segment,检索性能会受到影响。所以,非实时的场景可以调大,设置为30s,甚至-1。
5.2 减少副本,提升写入性能。
写入前,副本数设置为0,写入后,副本数设置为原来值。
5.3 减少副本,提升写入性能
批量接口为bulk,批量的大小要结合队列的大小,而队列大小和线程池大小、机器的cpu核数。
5.4 禁用swap
6.1 禁用 wildcard模糊匹配
数据量级达到TB+甚至更高之后,wildcard在多字段组合的情况下很容易出现卡死,甚至导致集群节点崩溃宕机的情况。
后果不堪设想。
替代方案:
方案一:针对精确度要求高的方案:两套分词器结合,standard和ik结合,使用match_phrase检索。
方案二:针对精确度要求不高的替代方案:建议ik分词,通过match_phrase和slop结合查询。
6.2 极小的概率使用match匹配
中文match匹配显然结果是不准确的。很大的业务场景会使用短语匹配“match_phrase"。
match_phrase结合合理的分词词典、词库,会使得搜索结果精确度更高,避免噪音数据。
6.3 结合业务场景,大量使用filter过滤器
对于不需要使用计算相关度评分的场景,无疑filter缓存机制会使得检索更快。
举例:过滤某邮编号码。
6.4 控制返回字段和结果
和mysql查询一样,业务开发中,select * 操作几乎是不必须的。同理,ES中,_source 返回全部字段也是非必须的。
要通过_source 控制字段的返回,只返回业务相关的字段。网页正文content,网页快照html_content类似字段的批量返回,可能就是业务上的设计缺陷。显然,摘要字段应该提前写入,而不是查询content后再截取处理。
6.5 分页深度查询和遍历
分页查询使用:from+size;
遍历使用:scroll;
并行遍历使用:scroll+slice。
斟酌集合业务选型使用。
6.6 聚合Size的合理设置
聚合结果是不精确的。除非你设置size为2的32次幂-1,否则聚合的结果是取每个分片的Top size元素后综合排序后的值。实际业务场景要求精确反馈结果的要注意。尽量不要获取全量聚合结果——从业务层面取TopN聚合结果值是非常合理的。因为的确排序靠后的结果值意义不大。
6.7 聚合分页合理实现
聚合结果展示的时,势必面临聚合后分页的问题,而ES官方基于性能原因不支持聚合后分页。
如果需要聚合后分页,需要自开发实现。包含但不限于:
方案一:每次取聚合结果,拿到内存中分页返回。
方案二:scroll结合scroll after集合redis实现。
Wikipedia 使用 Elasticsearch 提供带有高亮片段的全文搜索,还有 search-as-you-type 和 did-you-mean 的建议。
卫报 使用 Elasticsearch 将网络社交数据结合到访客日志中,实时的给它的编辑们提供公众对于新文章的反馈。
Stack Overflow 将地理位置查询融入全文检索中去,并且使用 more-like-this 接口去查找相关的问题与答案。
GitHub 使用 Elasticsearch 对1300亿行代码进行查询。
避免大量写入被拒绝,可以通过观察 elasticsearch 后台日志或是通过使用 Thread pool Api 来观察内部线程池的使用情况,以及相应使用的队列大小,判断是否还可以继续调整写入配置参数。
curl -uusername:pwd-XGET "http://esip:port/_cat/thread_pool?v" | grep write
通过 tasks api 可以直观的观察到集群在忙什么?,结果会显示包括父级任务,任务的持续时间等指标。命令如下:
curl -u username:pwd ip:port/_cat/tasks/?v | more
如下是 bulk 请求的简易写入流程,我们知道客户端会选择一个节点发送请求,
这个节点被之称为协调节点,也叫客户端节点,但是在执行之前,如果定义了预处理的 pipline 操作(比如写入前将 key 值转换,或者增加时间戳等),则此写操作会被拦截并进行对应逻辑处理。从图中可以看出,写入操作会现根据路由出来的规则,决定发送数据到那个分片上去,默认情况下,是通过数据的文档 id 来进行路由的,这能保证数据平均分配到各个节点上去,也可以自定义路由规则
通过 ES 提供的 API 观察各个节点的热线程,api 结果会显示出占用 cpu 高的线程,这也是我们可以优化的地方。大量写入场景下,这里一般大多数会显示:Lucene
Merge Thread 或者[write],查询命令为:
curl -uusername:pwd -XGET "http://ip:9200/_nodes/hot_threads"
每个目录挂载不同的磁盘
在 data 目录下,我们分出了 10 个子目录,分别挂载到不同的硬盘上去。这相当于做了 raid0。能大大的提高写入速度。
配置多个 path.data 路径
由于在前面我们将 10 个目录分别挂载到不同的硬盘上去,所以在 elasticsearch.yml 的 path.data 属性中,我们配置多个路径,让数据能高效的写入不同的目录(硬盘),需要注意的是,如果只有一个索引,它的分片在某个节点的存储目录是固定的。所以这个特性,也只有在存在多个索引的情况下,能发挥出它的作用。