提升你的Elasticsearch性能

2023-11-16 15:04:41 浏览数 (1)

面试官: 项目中有用到Elasticsearch?

了不起: 有的

面试官: 知道如何提升查询效率吗?

数据写入过程

  1. 数据写入到内存buffer
  2. 同时写入到数据到translog buffer,这是为了防止数据不会丢失
  3. 每隔1s数据从buffer中refresh到FileSystemCache中,生成segment文件,这是因为写入磁盘的过程相对耗时,借助FileSystemCache,一旦生成segment文件,就能通过索引查询到了
  4. refresh完,memory buffer就清空了, 但是translog并不会被清空。
  5. 每隔5s中,translog 从buffer flush到磁盘中(6.0开始每次请求translog都会落盘)
  6. 定期/定量从FileSystemCache中,结合translog内容flush index到磁盘中。做增量flush的。
  7. 当translog达到一定程度的时候会出发一次commit操作, 也叫flush操作(默认情况下30分钟或者512M执行一次flush)。可以用index.translog.flush_threshold_period和index.translog.flush_threshold_size修改默认配置。

commit(flush)程分为以下几步:

7.1 Memory buffer清空并且Refresh

7.2 调用fsync, 将FileSystemCache中的Segments写入磁盘

7.3 清空删除translog重启一个新的translog

ES利用这个commit point来决定哪些Segments属于当前shard。Commit point和Segments的关系如下

这里有两个知识点

  • ES之所以被称为准实时是因为数据refresh到了FileSystemCache之后就能被搜索到
  • [数据可能会丢失每隔5秒translog才会落盘,如果这个时候机器宕机,内存数据就没有了,所以有5秒的数据丢失]

数据查找过程

1、Query阶段 ⽤用户发出搜索请求到 ES 节点。节点收到请求 后, 会以 Coordinating 节点的身份,在 6 个 主副分⽚片中随机选择 3 个分片,发送查询请求。被选中的分⽚执⾏行查询,进行排序。然后,每 个分片都会返回 From Size 个排序后的⽂文 档 Id 和排序值 给 Coordinating 节点。2、Fetch阶段 Coordinating Node 会将 Query 阶段,从 从每个分⽚片获取的排序后的文档 Id 列表, 重新进行排序。选取 From 到 From Size 个⽂文档的 Id。最后以 multi get 请求的⽅方式,到相应的分⽚片获 取详细的⽂文档数据

提高读取效率

杀手锏FileSystemCache

根据之前数据写入过程分析我们可以看到,如果数据已经写入了Filesystem cache, 那么数据就能被搜索到了,所以如果给Filesystem cache更多的内存, 让内存容纳所有的Segment file索引文件, 性能会很高。有人做过测试,走磁盘的搜索性能是秒级的,纯走内存基本是毫秒级别,从几毫秒到几百毫秒不等。怎样才能尽量把数据都存在FileSystem cache里?可以采用ES Mysql/Hbase的架构。举个例子, 假如你有一行数据差不多有30个字段,如:id, name, age...。ES中仅仅存储用来检索的少数几个字段。其他字段都存在HBase中。从ES中根据name和age进行检索,拿到doc id, 然后根据doc id去Hbase中查询doc id对应的完整数据,返回给前端。写入ES的数据量最好小于等于Filesystem cache的内容容量。采用这种方式,从ES检索花费20ms, 去查询HBase花费30m,总共也就50ms, 相比于把1T数据都放在ES中检索花费5~10s, 性能提升很大

数据预热

虽然FileSystem cache是杀手锏,但是难免会有机器配置不高,ES的数据量还是远远大于Filesystem cache的情况, 这种情况下可以做数据预热。举个例子,对于电商平台来说,iphone是比较热门的商铺,可以自己搞一个后端程序,每隔一段时间访问一下iphone, 就能把数据刷到filesystem cache中。所以最好做一个专门的缓存预热子系统, 让数据进入到FileSystem cache中。

冷热分离

类似mysql的水平拆分,对于大量的访问频率比较少的数据,单独建一个索引存储,和热数据区分开。假设你有 6 台机器,2 个索引,一个放冷数据,一个放热数据。热数据可能就占总数据量的 10%,此时数据量很少,几乎全都保留在 filesystem cache 里面了,就可以确保热数据的访问性能是很高的。但是对于冷数据而言,是在别的 index 里的,跟热数据 index 不在相同的机器上,大家互相之间都没什么联系了。

分页性能优化

如果每页有10条数据,你现在要查询第100页,实际会把每个shard上存储的前1000条数据查到协调节点,有5个shard就有5000条数据,然后协调节点再进行合并处理,最终获得第100页的10条数据。可能翻到第10页就要5到10秒的时间了。

  • 和产品经理协商不允许深度翻页
  • 采用类似微博下拉加载新数据的交互,参考Scroll API

数据建模

  • ES里面复杂的关联模型尽量不要使用。尽可能Denormalize数据
    • 使用Nested类型数据,查询会慢几倍
    • 使用Parent/Child关系,查询会慢几百倍
  • 尽量使用Filter Context, 利用缓存机制减少不必要的算法如下例子
  • 应该避免在查询时使用脚本, 可以使用Index pipeline替代
  • 优化分片
    • 避免Over Sharding
    • 控制单个分片的尺寸
    • For-merge read-only 索引

提高写入效率

客户端

  • 每个bulk请求体数据量不要太大,官方建议5-15MB
  • bulk请求超时时长可以大一点,建议60s
  • 写入端尽量将数据打到不同的节点

服务端

  • 降低IO操作
  • 使用ES自动生成的文档ID,修改ES相关配置,比如提高Refresh inveral时间
  • 降低CPU和存储开销
  • 减少没有必要的分词,避免不必要的doc_values(可以节省磁盘空间), 文档字段每次写入的时候保证相同顺序,提高文档压缩率
  • 尽可能做到写入和分片的负载均衡
  • Shard Filtering/Write Load Balancer
  • 调整Bulk线程池和队列

关闭没必要的功能

下面是设置mapping的例子

代码语言:javascript复制
{
    "english":{
        "_source": {
         "enabled": false
      },
       "properties": {
         "content":{
            "type":"text",
            "store":true,
            "index":false
        },
         "title":{
             "type":"text",
            "store":true,
            "index":false
        }
      }
    }
}

1、不需要聚合和搜索时, index设置成false,比如上面的titlecontent

2、不要对字符串使用默认的dynamic mapping, 比如text类型会默认额外生成keywords, 字符串数量过多时,会对性能产生影响

3、index_options可以控制在创建倒排索引的时候,哪些内容可以被加入到倒排索引中,可以节省CPU

4、关闭_source, 比如上面的english。这样可以减少IO操作,适合指标型数据

针对性能的取舍

如果追求极致的写入速度,可以牺牲数据可靠性和搜索的实时性换取性能。

  • 牺牲可靠性:将副本分片设置为0,写入完毕再调整回去
  • 牺牲搜索实时性:增加Refresh interval的时间
  • 牺牲可靠性:修改Translog的配置, 如下请求,可以增大间隔时间,同时改成了异步写的方式
代码语言:javascript复制
PUT /my_index/_settings
{
    "index.translog.durability": "async",
    "index.translog.sync_interval": "60s"
}

下图是一个索引优化的例子

我是了不起
和我一起学习更多精彩知识!!!

0 人点赞