MST

星途 面试题库

面试题:ElasticSearch百分位数聚合在复杂业务场景中的应用

假设你正在处理一个电商平台的订单数据,需要分析不同地区用户购买金额的分布情况。不仅要计算每个地区购买金额的常见百分位数(如25、50、75、90),还要结合时间维度(按月)展示这些百分位数的变化趋势。同时,需要考虑数据的高基数和实时性要求。请设计一个完整的ElasticSearch解决方案,包括索引结构设计、聚合查询策略以及可能用到的优化手段。
27.5万 热度难度
数据库ElasticSearch

知识考点

AI 面试

面试题答案

一键面试

索引结构设计

  1. 文档结构
    • 订单ID:唯一标识每个订单,例如order_id,类型为keyword
    • 用户ID:关联用户,user_id,类型为keyword
    • 地区:购买地区,region,类型为keyword。这是为了处理高基数问题,keyword类型适合精确匹配和聚合操作。
    • 购买金额:amount,类型为double
    • 购买时间:purchase_time,类型为date,格式可以是yyyy - MM - dd HH:mm:ss。这个字段用于按时间维度(按月)进行分析。
  2. 映射设置
{
    "mappings": {
        "properties": {
            "order_id": {
                "type": "keyword"
            },
            "user_id": {
                "type": "keyword"
            },
            "region": {
                "type": "keyword"
            },
            "amount": {
                "type": "double"
            },
            "purchase_time": {
                "type": "date",
                "format": "yyyy - MM - dd HH:mm:ss"
            }
        }
    }
}

聚合查询策略

  1. 按月和地区聚合计算百分位数
{
    "aggs": {
        "by_month": {
            "date_histogram": {
                "field": "purchase_time",
                "calendar_interval": "month",
                "format": "yyyy - MM"
            },
            "aggs": {
                "by_region": {
                    "terms": {
                        "field": "region"
                    },
                    "aggs": {
                        "p25_amount": {
                            "percentiles": {
                                "field": "amount",
                                "percents": [25]
                            }
                        },
                        "p50_amount": {
                            "percentiles": {
                                "field": "amount",
                                "percents": [50]
                            }
                        },
                        "p75_amount": {
                            "percentiles": {
                                "field": "amount",
                                "percents": [75]
                            }
                        },
                        "p90_amount": {
                            "percentiles": {
                                "field": "amount",
                                "percents": [90]
                            }
                        }
                    }
                }
            }
        }
    }
}
  • 外层date_histogram按月份对订单数据进行分组。
  • 内层terms按地区对每个月的数据进行分组。
  • 然后针对每个地区每月的数据计算25、50、75、90百分位数的购买金额。

优化手段

  1. 数据预热:在系统启动或业务低峰期,提前执行一些常见的聚合查询,将结果缓存起来。这样在业务高峰期,直接从缓存获取数据,减少对ElasticSearch的压力。
  2. 索引优化
    • 分片和副本设置:根据数据量和服务器资源合理设置分片数和副本数。对于高基数数据,可以适当增加分片数,但要注意过多分片会增加管理开销。例如,对于数据量较大的电商订单数据,可以设置较多的分片,如8 - 16个分片,副本数可以根据可用性要求设置为1 - 2个。
    • 字段数据存储:对于不需要聚合或排序的字段,设置doc_values: false,以减少磁盘占用。但对于regionpurchase_timeamount这些用于聚合的字段,需要保留doc_values
  3. 缓存机制
    • 使用ElasticSearch的请求缓存:开启请求缓存,对于相同的聚合查询,ElasticSearch可以直接从缓存返回结果,提高查询效率。可以在elasticsearch.yml中配置indices.requests.cache.enable: true
    • 应用层缓存:在应用程序层面,可以使用Redis等缓存工具,将聚合结果缓存起来,设置合理的过期时间,以减少对ElasticSearch的重复查询。
  4. 实时性处理
    • 使用ElasticSearch的近实时搜索功能:ElasticSearch默认每隔1秒刷新一次数据到磁盘,这使得数据在1秒内就可以被搜索到。对于实时性要求较高的场景,可以适当缩短刷新间隔,但这会增加I/O开销。可以通过index.refresh_interval参数进行设置,例如index.refresh_interval: 5s,将刷新间隔设置为5秒。
    • 采用异步处理:将订单数据写入ElasticSearch的操作放到异步线程或消息队列中处理,以避免影响业务主线程的响应时间,确保系统的实时性。