ES提高写入性能的目标
增大写吞吐量,越高越好
基本原则
- 客户端:多线程,批量写
- 可以通过性能测试,确定最佳文档数量
- 多线程:需要观察是否由HTTP429返回,实现Retry以及线程数量的自动调节
- 服务器端:先分解问题,在单个节点上测试调整以达到最高吞吐量
- 使用更好的硬件(通过观察CPU/IO Block)
- 线程切换/堆栈状况
服务器端优化写入性能的一些手段
- 降低IO操作
- 使用ES自动生成的文档ID(可以避免get操作)
- 一些相关的ES配置,如Refresh Interval
- 降低CPU和存储开销
- 减少不必要的分词
- 避免不需要的doc_values
- 文档的字段尽量保证相同的顺序,可以提高文档的压缩率
- 尽可能做到写入和分片的负载均衡,实现水平扩展
- Shard Filering
- Write Load Balancer
- 调整Bulk线程池和队列
索引建模实践(关闭无关的功能)
- 如果只需要聚合不需要搜索,Index设置成false
- 如果不需要算分,Norms设置成false
- 不要对字符串使用默认的dynamic mapping。字段数量过多,会对性能产生较大的影响
- Index_options控制在创建倒排索引时,哪些内容会被添加到倒排索引中。优化这些设置,一定程度节约CPU
- 关闭_source,减少IO操作(适合指标型数据,关闭会影响update等操作);
PUT index
{
"mappings": {
"properties": {
"foo": {
"type": "integer",
"index": false
}
}
}
}
-------------------------------
PUT index
{
"mappings": {
"properties": {
"foo": {
"type": "text",
"norms": false
}
}
}
}
针对性能的取舍
- 如果需要追求极致的写入速度,可以牺牲数据可靠性及搜索及时性以换取性能
- 牺牲可靠性:将副本分片设置为0,写入完毕再调回去
- 牺牲搜索及时性:增加Refresh Inteval的时间
- 牺牲可靠性:修改Translog的配置
数据写入过程分析
Refresh:将文档先保存在Index Buffer中,以refresh_interval为间隔时间,定期清空buffer, 生成segment, 借助文件系统缓存的特性,先将segment放在文件系统缓存中,并开放查询,以提升搜索的实时性
Translog:segment没有写入磁盘,即便发生了宕机,重启后数据也能恢复,默认配置是每次请求都会落盘
Flush:删除旧的translog文件,生成segment并写入磁盘,更新commit point并写入磁盘,这里一般由ES自动完成,优化空间不大
写入配置优化
Refresh Interval
- 通过调大数值来降低Refresh的频率,(默认1s ,如果设置为-1 ,会禁止自动refresh),避免生成过多的segment文件,但是会降低搜索的实时性
- 增大静态配置参数indices.memory.index_buffer_size, 默认10%,会导致自动触发refresh
Translog
降低写磁盘的频率,但是会降低容灾能力
- Index.translog.durability: 默认为request, 每个请求都落盘。设置成async,异步写入
- Index.translog.sync_interval设置为60s ,每分钟执行一次
- index.translog.flush_threshod_size:默认512mb,可以适当调大。当translog超过这个数值会触发flush
分片设定
- 副本在写入时设为0,完成后再增加
- 合理设置主分片数,确保均匀分配在所有数据节点上
- Index.routing.allocation.total_share_per_node:限定每个索引在每个节点上可分配的主分片数量
Bulk,线程池和队列大小
客户端
- 单个builk请求体的数据量不要太大,官方建议大约5-15mb
- 写入端的bulk请求超时需要足够长,建议60s以上
- 写入端尽量将数据轮询到不同节点
服务端
- 索引创建属于计算密集型任务,应该使用固定大小的线程池来配置,来不及处理的放入队列,线程数应该配置为CPU核数+1,避免过多的上下文切换
- 队列大小可以适当增加,不要过大,否则占用的内存会导致GC
优化配置DEMO
DELETE myindex
PUT myindex
{
"settings": {
"index": {
"refresh_interval": "30s",//设置30S一次refresh,默认1S
"number_of_shards": "2"
},
"routing": {
"allocation": {
"total_shards_per_node": "3" //控制分片,避免数据热点
}
},
"translog": {
"sync_interval": "30s", //降低translog落盘
"durability": "async"
},
"number_of_replicas": 0 //避免副本影响(允许丢数据)
},
"mappings": {
"dynamic": false, //避免不必要的字段索引,必要时可以通过update by query索引必要的字段
"properties": {}
}
}