当前位置: 首页 > news >正文

【实战ES】实战 Elasticsearch:快速上手与深度实践-5.4.2用户画像聚合(Terms Aggregation + Cardinality)

👉 点击关注不迷路
👉 点击关注不迷路
👉 点击关注不迷路


文章大纲

  • 5.4.2 用户画像聚合深度实践:Terms Aggregation与Cardinality高阶应用
    • 1. 核心聚合原理
      • 1.1 聚合类型对比矩阵
      • 1.2 组合聚合公式
    • 2. 全链路配置实战
      • 2.1 索引与映射设计
      • 2.2 多级聚合查询模板
    • 3. 性能调优策略
      • 3.1 参数调优矩阵
      • 3.2 性能测试数据
    • 4. 企业级应用案例
      • 4.1 电商账号风险识别
      • 4.2 社交网络异常检测
    • 5. 深度优化技巧
      • 5.1 存储层优化
      • 5.2 查询层优化
    • 6. 问题排查与监控
      • 6.1 常见问题矩阵
      • 6.2 `关键监控指标`

5.4.2 用户画像聚合深度实践:Terms Aggregation与Cardinality高阶应用

  • 用户画像聚合分析系统架构与数据流向
应用层
结果存储层
Elasticsearch集群
数据处理层
数据源层
实时查询服务
用户画像看板
个性化推荐引擎
画像标签索引
主分片存储
副本分片冗余
聚合计算节点
Terms Aggregation(标签分布)
Cardinality(唯一用户数)
Logstash/Beats数据管道
数据清洗与脱敏
特征工程(标签提取)
Elasticsearch实时索引
Kafka/Pulsar消息队列
用户行为日志
第三方数据API
数据库变更日志
  • 典型应用场景
    • 精准营销: 根据 city 和 interest_tags 定向推送广告。
    • 用户分层: 通过 age_group 和 consumption_level 划分高价值用户。
    • 产品优化: 分析 device_type 和 user_behavior 优化交互设计。
  • Cardinality(基数)
    • 在数据处理和分析领域,Cardinality(基数)是一个重要的概念,尤其在数据库、数据分析工具以及 Elasticsearch 等技术中经常被提及和使用。
    • Cardinality 指的是数据集中唯一值的数量。例如,在一个包含用户 ID 的数据列中,Cardinality 就是该列中不同用户 ID 的个数。
    • 在数据库中,Cardinality 常用于评估数据的分布情况和查询性能。高 Cardinality 意味着数据集中有很多不同的唯一值,而低 Cardinality 则表示数据集中的唯一值较少,存在较多的重复值。

1. 核心聚合原理

1.1 聚合类型对比矩阵

聚合类型计算方式空间复杂度精确度适用场景
Terms Aggregation分桶统计Top N项O(n)精确高频值分布分析
CardinalityHyperLogLog++近似算法O(log n)99.9%海量数据去重统计
Composite多维度组合分桶O(n²)精确多维交叉分析
PercentilesTDigest算法O(n)近似数据分布分析
  • HyperLogLog++近似算法

    • HyperLogLog++ 是 Elasticsearch 中用于近似计算 Cardinality(唯一值数量)的核心算法,其设计目标是在内存使用和计算精度之间取得平衡。
    • 其核心思想是通过概率统计来估算唯一值数量,而非精确计算。
    • 典型应用场景
      • 用户行为分析: 统计日活用户(DAU)、独立访客(UV)。
      • 日志分析: 统计异常请求的唯一 IP 数量。
      • 电商场景: 分析商品浏览的唯一用户数或设备类型分布。
  • TDigest算法

    • TDigest 是 Elasticsearch 中用于高效计算近似百分位数的核心算法,其设计目标是在内存占用和计算精度之间取得平衡。
    • 适用场景
      • 百分位数统计:如响应时间 P99、用户访问延迟 P95 等。
      • 数据分布分析:监控数值型数据的分布特征(如交易金额、请求耗时)。
      • 实时分析:需快速返回结果的场景(如监控仪表盘)。

1.2 组合聚合公式

用户画像维度
Terms(field=user_id)
子聚合: Cardinality(field=behavior_type)
子聚合: Terms(field=geoip.city)

2. 全链路配置实战

2.1 索引与映射设计

// 向 Elasticsearch 发送 PUT 请求,创建一个名为 user_behavior 的索引
PUT /user_behavior
{
    "settings": {
        "index": {
            // 设置索引的主分片数量为 9。分片是 Elasticsearch 中数据的分布式存储单元,
            // 合理的分片数量有助于提高索引的读写性能和扩展性。这里设置为 9 可根据集群规模和数据量进行调整
            "number_of_shards": 9,
            // 设置每个主分片的副本分片数量为 1。副本分片用于提高数据的可用性和容错性,
            // 当主分片出现故障时,副本分片可以接替工作。同时,副本分片也可以分担查询请求,提高查询性能
            "number_of_replicas": 1,
            // 设置索引的刷新间隔为 30 秒。刷新操作会将内存中的数据写入磁盘,使数据可以被搜索到。
            // 较长的刷新间隔可以减少磁盘 I/O 操作,提高写入性能,但会增加数据的可见延迟
            "refresh_interval": "30s"
        }
    },
    "mappings": {
        // 设置动态映射策略为 strict,表示只允许显式定义的字段被索引。
        // 如果遇到未定义的字段,会抛出异常,避免意外索引不必要的字段
        "dynamic": "strict",
        "properties": {
            // 定义 @timestamp 字段,类型为 date,用于存储时间戳信息。
            // 该字段常用于时间序列分析和基于时间的查询
            "@timestamp": { "type": "date" },
            "user_id": {
                // 定义 user_id 字段,类型为 keyword,用于存储用户的唯一标识符。
                // keyword 类型适用于精确匹配和聚合操作
                "type": "keyword",
                // 启用 doc_values,这是一种磁盘数据结构,用于在内存中高效访问字段值。
                // 对于需要进行排序、聚合和脚本操作的字段,启用 doc_values 可以提高性能
                "doc_values": true 
            },
            "behavior_type": {
                // 定义 behavior_type 字段,类型为 keyword,用于存储用户的行为类型。
                // keyword 类型适合存储离散的、可枚举的值
                "type": "keyword",
                // 启用 eager_global_ordinals,这会在索引刷新时预先计算全局序号。
                // 对于频繁用于聚合操作的 keyword 字段,启用该选项可以提高聚合性能
                "eager_global_ordinals": true
            },
            "device_fingerprint": {
                // 定义 device_fingerprint 字段,类型为 keyword,用于存储设备的指纹信息。
                // keyword 类型确保可以精确匹配设备指纹
                "type": "keyword",
                // 设置 ignore_above 为 256,表示如果该字段的值长度超过 256 个字符,将被忽略不进行索引。
                // 这可以避免存储过长的、可能无意义的数据,节省存储空间
                "ignore_above": 256
            },
            "geoip": {
                // 定义 geoip 字段,类型为 object,用于存储地理信息相关的数据。
                // object 类型可以包含多个子字段
                "type": "object",
                "properties": {
                    // 定义 geoip 对象下的 city 字段,类型为 keyword,用于存储用户所在的城市名称。
                    // keyword 类型适合进行精确匹配和聚合操作
                    "city": { "type": "keyword" },
                    // 定义 geoip 对象下的 location 字段,类型为 geo_point,用于存储用户的地理位置坐标(经纬度)。
                    // geo_point 类型支持地理空间查询,如距离计算、区域过滤等
                    "location": { "type": "geo_point" }
                }
            }
        }
    }
}

2.2 多级聚合查询模板

// 向 Elasticsearch 的 user_behavior 索引发送 GET 请求进行搜索
GET /user_behavior/_search
{
    // 设置返回的文档数量为 0,因为我们主要关注聚合结果,不需要具体的文档内容
    "size": 0,
    "query": {
        // 使用范围查询来筛选符合时间条件的文档
        "range": {
            "@timestamp": {
                // gte 表示大于等于,now-7d/d 表示当前时间往前推 7 天并取当天的开始时间
                "gte": "now-7d/d",
                // lte 表示小于等于,now/d 表示当前时间所在天的开始时间
                "lte": "now/d"
            }
        }
    },
    "aggs": {
        // 定义一个名为 user_profile 的聚合操作
        "user_profile": {
            // 使用 terms 聚合按 user_id 字段对文档进行分组
            "terms": {
                // 指定分组的字段为 user_id
                "field": "user_id",
                // 设置返回的分组数量上限为 1000,即最多返回 1000 个不同的 user_id 分组
                "size": 1000,
                // 按照每个分组中文档的数量(_count)进行降序排序
                "order": { "_count": "desc" },
                // 设置执行提示为 map,这种模式适合在数据量较小或者每个分片上数据分布比较均匀的情况下使用,能提高性能
                "execution_hint": "map" 
            },
            "aggs": {
                // 在 user_id 分组内定义一个名为 unique_behaviors 的子聚合
                "unique_behaviors": {
                    // 使用 cardinality 聚合计算每个 user_id 分组下 behavior_type 字段的唯一值数量
                    "cardinality": {
                        // 指定要计算唯一值数量的字段为 behavior_type
                        "field": "behavior_type",
                        // 设置精度阈值为 3000,该值越高,计算结果越精确,但会消耗更多的内存
                        "precision_threshold": 3000
                    }
                },
                // 在 user_id 分组内定义一个名为 city_distribution 的子聚合
                "city_distribution": {
                    // 使用 terms 聚合按 geoip.city 字段对每个 user_id 分组内的文档进行进一步分组
                    "terms": {
                        // 指定分组的字段为 geoip.city
                        "field": "geoip.city",
                        // 设置返回的分组数量上限为 10,即每个 user_id 分组下最多返回 10 个不同的城市分组
                        "size": 10
                    }
                },
                // 在 user_id 分组内定义一个名为 risk_score 的子聚合
                "risk_score": {
                    // 使用 bucket_script 聚合,用于根据其他聚合结果进行自定义的脚本计算
                    "bucket_script": {
                        // 指定要引用的其他聚合结果的路径,这里引用了 unique_behaviors 聚合的结果
                        "buckets_path": {
                            "behaviorCount": "unique_behaviors"
                        },
                        // 定义一个 Painless 脚本,根据 unique_behaviors 的计算结果进行风险评分
                        // 如果 behaviorCount 大于 10,则风险评分为 100,否则为 0
                        "script": "params.behaviorCount > 10 ? 100 : 0"
                    }
                }
            }
        }
    }
}

3. 性能调优策略

3.1 参数调优矩阵

参数默认值推荐值优化效果
shard_sizesize*1.5size*3分片级精度提升25%
execution_hintglobal_ordinalsmap查询速度提升40%
precision_threshold300010000精度提升至99.99%
max_buckets10000100000支持更大规模分桶分析
doc_valuestrue按需关闭内存使用降低30%
  • execution_hint
    • execution_hint 能够指导 Elasticsearch 采用何种算法与策略来执行聚合操作,特别是在 terms 聚合中应用广泛。通过合理设置该参数,可以减少内存占用、提升计算速度,从而提高聚合操作的整体性能。
    • 取值及含义: map、global_ordinals、global_ordinals_hash
      • map 模式会在每个分片上直接对文档进行遍历,为每个文档的指定字段值维护一个计数器。
      • global_ordinals 模式会先在所有分片上构建全局序号(global ordinals),这是一种用于表示字段值的整数映射。对于高基数字段(即字段包含很多不同的值)和大规模数据,global_ordinals 模式可以显著减少内存占用,提高性能。
      • 相比于 global_ordinals,global_ordinals_hash 可以更快地构建和使用全局序号,尤其在内存资源相对充足的情况下。

3.2 性能测试数据

数据规模聚合复杂度默认配置耗时优化后耗时内存消耗降低
1千万文档单层Terms820ms480ms28%↓
5千万文档Terms+Cardinality6.2s3.8s42%↓
1亿文档三级嵌套聚合14.5s8.9s55%↓

4. 企业级应用案例

4.1 电商账号风险识别

// 向 Elasticsearch 的 user_login 索引发送 GET 请求进行搜索操作
GET /user_login/_search
{
    // 设置返回的文档数量为 0,因为我们主要关注聚合结果,而非具体的文档内容
    "size": 0,
    "aggs": {
        // 定义一个名为 risk_users 的聚合操作,用于找出可能存在风险的用户
        "risk_users": {
            // 使用 terms 聚合按 user_id 字段对文档进行分组
            "terms": {
                // 指定分组依据的字段为 user_id
                "field": "user_id",
                // 设置返回的分组数量上限为 100,即最多返回 100 个不同的 user_id 分组
                "size": 100,
                // 按照 device_count 子聚合的结果进行降序排序
                // 意味着会优先展示使用设备数量较多的用户分组
                "order": { "device_count": "desc" }
            },
            "aggs": {
                // 在 user_id 分组内定义一个名为 device_count 的子聚合
                "device_count": {
                    // 使用 cardinality 聚合计算每个 user_id 分组下 device_id 字段的唯一值数量
                    // 即统计每个用户使用的不同设备的数量
                    "cardinality": { "field": "device_id" }
                },
                // 在 user_id 分组内定义一个名为 ip_variety 的子聚合
                "ip_variety": {
                    // 使用 cardinality 聚合计算每个 user_id 分组下 login_ip 字段的唯一值数量
                    // 即统计每个用户登录时使用的不同 IP 地址的数量
                    "cardinality": { "field": "login_ip" }
                },
                // 在 user_id 分组内定义一个名为 time_span 的子聚合
                "time_span": {
                    // 使用 stats 聚合对每个 user_id 分组下的 @timestamp 字段进行统计分析
                    // 会返回该分组内 @timestamp 字段的最小值、最大值、平均值、总和以及文档数量等统计信息
                    // 可用于了解每个用户登录时间的跨度和分布情况
                    "stats": { "field": "@timestamp" }
                }
            }
        }
    }
}
  • 风险判定规则

    • 典型应用场景
      • 反欺诈系统:检测账号接管(Account Takeover)攻击
      • 风控系统:识别异常登录模式
      • 安全审计:监控特权账号的异常行为
    // 风险评分规则:当以下三个条件同时满足时,判定为高风险用户
    // 用户使用超过 3 种不同设备登录。异常场景:正常用户通常不会在短时间内频繁更换设备,可能存在账号共享或被盗用
    // 用户使用超过 5 个不同 IP 地址登录。异常场景:跨地域 / 运营商频繁切换 IP,可能涉及账号暴力破解或恶意操作
    // 登录时间跨度小于 24 小时(86400000ms)。异常场景:在一天内使用多设备多 IP 登录,符合自动化攻击工具的特征
    
    if (device_count > 3 && ip_variety > 5 && time_span.max - time_span.min < 86400000) {
        return RISK_SCORE.HIGH;
    }
    
  • 实施效果

指标优化前优化后提升幅度
风险账号识别率68%93%37%↑
误报率22%6%73%↓
平均响应时间2.8s1.2s57%↓
最大并发处理能力800 QPS3500 QPS337%↑

4.2 社交网络异常检测

// 向 Elasticsearch 的 social_activity 索引发送 GET 请求进行搜索,重点在于进行聚合分析
GET /social_activity/_search
{
    "aggs": {
        // 定义一个名为 suspicious_groups 的聚合,用于找出可能存在可疑行为的用户组和行为类型组合
        "suspicious_groups": {
            // 使用 composite 聚合,它允许通过多个维度组合生成聚合桶
            // 适用于需要对多个字段进行分组聚合,并且可能有大量唯一组合的场景
            "composite": {
                "sources": [
                    // 第一个聚合源,按 user_group 字段进行分组,创建名为 user_cluster 的分组
                    { "user_cluster": { "terms": { "field": "user_group" } } },
                    // 第二个聚合源,按 action_type 字段进行分组,创建名为 behavior_type 的分组
                    { "behavior_type": { "terms": { "field": "action_type" } } }
                ]
            },
            "aggs": {
                // 在每个由 user_group 和 action_type 组合的分组内,定义一个名为 unique_devices 的子聚合
                "unique_devices": {
                    // 使用 cardinality 聚合计算每个分组下 device_hash 字段的唯一值数量
                    // 即统计每个用户组和行为类型组合下使用的不同设备的数量
                    "cardinality": { "field": "device_hash" }
                },
                // 在每个由 user_group 和 action_type 组合的分组内,定义一个名为 ip_entropy 的子聚合
                "ip_entropy": {
                    // 使用 cardinality 聚合计算每个分组下 client_ip 字段的唯一值数量
                    // 即统计每个用户组和行为类型组合下使用的不同 IP 地址的数量
                    "cardinality": { "field": "client_ip" }
                }
            }
        }
    }
}

5. 深度优化技巧

5.1 存储层优化

优化策略实施方法性能收益适用场景
列式存储优化启用doc_values+压缩编码35%↑高基数字段聚合
预聚合数据使用Rollup API60%↑历史数据分析
分层存储冷热数据分离40%↑时序数据场景
字段类型优化keyword代替text+fielddata50%↑精确值聚合

5.2 查询层优化

// 向 Elasticsearch 集群发送 PUT 请求,用于更新集群的持久化设置。
// 持久化设置会在集群重启后依然生效。
PUT _cluster/settings
{
    "persistent": {
        // 设置一次搜索请求中聚合操作所能生成的最大桶(Bucket)数量为 1000000。
        // 在 Elasticsearch 里,聚合操作会对搜索结果进行分组和统计,每个分组就是一个桶。
        // 此设置可防止因聚合操作生成过多桶而导致内存溢出或查询性能下降。
        // 若搜索请求中的聚合操作尝试生成超过该数量的桶,Elasticsearch 会返回错误。
        "search.max_buckets": 1000000,
        // 设置布尔查询(bool query)中允许的最大子句数量为 10000。
        // 布尔查询是 Elasticsearch 中常用的查询类型,可通过 must、should、must_not 等子句组合多个查询条件。
        // 当子句数量过多时,会增加查询的复杂度和资源消耗,此设置可避免因复杂查询导致性能问题。
        "indices.query.bool.max_clause_count": 10000,
        // 设置搜索线程池的大小为 32。
        // 搜索线程池用于处理搜索请求,线程池中的线程会并行执行搜索任务。
        // 合适的线程池大小可确保搜索请求能高效处理,若设置过小,可能导致请求排队等待;若设置过大,会造成资源竞争。
        "thread_pool.search.size": 32,
        // 设置搜索线程池的队列大小为 2000。
        // 当搜索线程池中的所有线程都在忙碌时,新的搜索请求会被放入队列等待处理。
        // 队列大小决定了最多可容纳多少个等待处理的请求,若队列已满,新的请求会被拒绝。
        "thread_pool.search.queue_size": 2000
    }
}

6. 问题排查与监控

6.1 常见问题矩阵

现象根因分析解决方案工具支持
聚合结果不完整分片大小限制调整shard_size参数Search Profiler
内存溢出全局序数加载过多启用eager_global_ordinalsHot Threads API
响应时间波动分片负载不均定制routing策略Cluster Allocation
聚合精度不足HyperLogLog精度限制提高precision_thresholdExplain API

6.2 关键监控指标

指标名称健康阈值采集方式优化方向
aggregation_latency<1sNodes Stats API优化聚合顺序
fielddata_memory_usage<30% JVMCluster Stats限制fielddata使用
query_cache_hit_rate>85%Indices Stats增加缓存内存
circuit_breaker_tripped0次/分钟Cluster Health调整断路器阈值

附录:聚合分析工具箱

工具类别推荐方案核心功能
可视化分析Kibana Lens聚合结果动态展示
性能诊断Elasticsearch Profile聚合阶段耗时分析
数据采样Sampler Aggregation大数据量下快速抽样
压力测试Rally Benchmark聚合查询性能压测

实施建议

  1. 生产环境必须设置断路器阈值
  2. 高频聚合字段启用 eager_global_ordinals
  3. 定期执行_forcemerge优化段文件
  4. 重要聚合查询需进行版本控制

相关文章:

  • SpringCloud——环境搭建
  • html css网页制作成品——糖果屋网页设计(4页)附源码
  • Java中数据库索引选择B+树而非红黑树的详细解析
  • 【前端拓展】Canvas性能革命!WebGPU + WebAssembly混合渲染方案深度解析
  • 【MySQL】增删改查进阶
  • 学习C2CRS Ⅲ (Response Generation Module)
  • 【编程向导】-JavaScript-基础语法-类型检测
  • 软考高级信息系统项目管理师笔记-第23章组织通用管理
  • redis趣味解读
  • SpringMVC工作原理
  • Python :Pandas
  • harmonyOS(鸿蒙)— 网络权限(解决app网络资源无法加载,图片无法显示)
  • 帕金森病如何 “偷走” 患者的正常生活?
  • gin框架
  • ORACLE EBS数据库RELINK方式搭建克隆环境
  • 黑色RGB是什么
  • C#实现AES-CBC加密工具类(含完整源码及使用教程)
  • 浮点数 NaN 彻底研究(linux 下 c环境测试)
  • 贝壳找房:以 OceanBase 为 JuiceFS 元数据引擎,构建 AI 存储底座
  • tomcat配置应用----server.xml文件具体配置
  • 长期供应小企业网站建设/百度关键词搜索量排名
  • 广西住建局官方网站/郑州学校网站建设
  • 网页界面设计用什么软件/长沙seo优化排名
  • 中小型门户网站/dz论坛seo
  • 网站建设制作设计营销 上海/seo包括什么
  • wordpress最好用的编辑器/抖音搜索优化