You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Elasticsearch统计去重IP文档数:每日唯一请求计数方案

问题描述

我们计划将早年搭建的一款实现不佳的PHP每日唯一IP日志记录器迁移至Elasticsearch,拟将每个请求作为单独文档存储,文档结构示例如下:

{
  "_index": "logger",
  "_type": "_doc",
  "_id": "-1q04XEBfzHON7FKVuMY", // Auto-generated
  "_source": {
    "ip": "211.543.232.533",
    "user": "",
    "request": "GET /index.php HTTP/1.1",
    "status": 200,
    "bytes": 10984,
    "refer": "https://www.google.com/search?q=some%20website",
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
    "domain": "example.org",
    "timestamp": 1662865208000
  }
}

现在需要统计每日0点之后的唯一IP请求数(比如6个文档对应3个唯一IP时,计数为3)。我们已经写出基础查询,但不知道如何添加去重逻辑,请问该需求可通过查询实现,还是需要配置映射或分析器?目前每日约有50万请求,其中约3万为唯一IP。

基础查询示例:

POST /logger/_doc/_count
{
    "query": {
        "bool": {
            "must": [
                {
                    "range": {
                        "timestamp": {
                            "gt": 1662854400000 // Epoch ms time at 24:00
                        }
                    }
                }
            ]
            // And then something here? I'm not really sure what to do
        }
    }
}
解决方案

核心实现:使用Cardinality聚合

这个需求完全可以通过聚合查询实现,不需要额外配置映射或分析器(只要ip字段的映射是keyword类型,默认自动映射的字符串字段会同时生成text和keyword子字段,直接用ip.keyword即可)。

具体来说,用cardinality聚合统计唯一IP数量,配合时间范围查询,完整请求如下:

POST /logger/_search
{
  "size": 0, // 无需返回具体文档,仅获取聚合结果
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "timestamp": {
              "gt": 1662854400000 // 每日0点的毫秒级时间戳
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "unique_ip_count": {
      "cardinality": {
        "field": "ip.keyword" // 使用keyword类型确保IP作为完整字符串去重
      }
    }
  }
}

关键注意点

  • 字段类型要求:必须用keyword类型的ip字段(或其子字段)。如果用text类型,IP会被分词拆分(比如211.543.232.533会拆成多个数字token),导致去重结果错误。若当前是自动映射,默认会有ip.keyword可用;若没有,需修改映射将ip设为keyword类型。
  • 性能优化:针对每日50万请求、3万唯一IP的规模,cardinality聚合性能完全够用。如果需要更高精度,可调整precision_threshold参数(默认值3000,你的唯一IP数接近这个值,设为5000能提升精度且性能损耗极小):
    "cardinality": {
      "field": "ip.keyword",
      "precision_threshold": 5000
    }
    

为什么不用_count接口

_count接口仅能返回匹配查询的文档总数,无法实现去重统计,必须用_search配合聚合完成唯一值计数需求。

内容的提问来源于stack exchange,提问作者Typewar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 15:50:28