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

Solr/Solarium统计唯一字段值计数的性能问题咨询

问题描述

我正在使用Solarium PHP库连接Solr实例,现有包含约350万条文档的企业信息索引,检索与过滤功能运行正常,但存在一项需求难以高效实现:索引文档存储企业信息,需统计指定查询条件下索引内的唯一电话号码总数量——部分关联企业共用同一电话号码,部分企业未登记电话号码。

此前尝试的多种统计方案均存在性能问题:

  • 分面(Facets)方案不适用于该场景:单次分面请求最多返回100条结果,针对350万文档量级需要发起海量请求
  • 调用getStats()方法实现统计的执行速度同样较慢
  • 最终采用GroupComponent分组查询实现该统计逻辑,但当匹配结果集规模超过10万条时,查询加载耗时极长,最终甚至会导致Solr服务崩溃。已调高内存限制避免服务崩溃,但查询响应时长仍无法满足正常使用要求,实现代码如下:
$groupComponent = $select->getGrouping();
$groupComponent->addField('phone');
$groupComponent->setNumberOfGroups(true);
$groupComponent->setLimit(0);
$groupComponent->setTruncate(true);
$groupComponent->setFormat('simple');
$groupComponent->setFacet(true);

$resultset = $this->client->execute($select);
$groups = $resultset->getGrouping();

当前存在的疑问:

  • 我仅需要统计得到的唯一值计数结果,不需要返回具体匹配文档。虽已将limit参数设置为0,但不确定该参数在此配置下是代表返回0条结果还是无限制返回,即使将limit设为1,查询性能也没有明显提升,不确定Solr是否支持仅返回分组计数、不返回具体结果的配置。
  • 尝试添加$groupComponent->setMainresult(true);配置,该配置不仅没有提升查询速度,返回的电话号码计数结果始终为0。

需要可在Solarium侧或Solr侧优化该统计流程、提升执行效率的可行方案。

解决方案

方案1:使用Cardinality基数聚合(Solr 8.0+ 首选,性能最优)

你之前用的普通分面、stats、分组查询性能差的核心原因,是这些方案要么需要遍历全量匹配值做精确全量计算,要么会加载不必要的文档、分组元数据。Solr 8.0版本后原生支持的基数聚合专门面向大规模数据集唯一值计数场景,底层用HyperLogLog++算法实现,可在可控误差范围内实现毫秒级统计,全程不需要加载具体文档内容。

前置配置

首先确保phone字段配置符合聚合要求:字段类型设为string(不要用分词类型存电话号码),开启docValues,关闭不必要的存储属性:

<field name="phone" type="string" indexed="true" stored="false" docValues="true"/>

如果之前没开docValues,修改schema后需要全量重建索引才能生效。

Solarium侧调用代码

不需要使用Group组件,直接调用JSON聚合接口即可:

// 核心配置:设置rows=0,完全不返回匹配文档,砍掉无用数据传输和加载开销
$select->setRows(0);
$agg = $select->createAggregation()
    ->setKey('unique_phone_count')
    ->setType('cardinality')
    ->setField('phone')
    // 精度阈值越高误差越小,性能损耗略有上升,设为10000时统计误差低于0.5%,完全满足企业业务统计要求
    ->setOption('precisionThreshold', 10000);

$result = $this->client->execute($select);
$uniquePhoneCount = $result->getAggregation()['unique_phone_count'];

如果需要100%精确计数,把precisionThreshold设置为大于业务可能的最大匹配量即可(比如设为10000000),性能依然比分组查询高5~20倍。

方案2:低版本Solr兼容方案(Solr 7及以下)

如果使用的Solr版本不支持基数聚合,不要用Group组件做统计,改用优化配置的分面查询,性能比当前分组方案高3~10倍:

$select->setRows(0);
// 关闭所有无关查询组件,减少额外计算
$select->getHighlighting()->setEnabled(false);
$select->getSpellcheck()->setEnabled(false);

$facetSet = $select->getFacetSet();
$facetSet->createFacetField('phone')
    ->setField('phone')
    ->setMinCount(1)
    // 用enum分面算法,避免全量值遍历
    ->setMethod('enum')
    // 开启exists参数,仅统计存在值的数量,不返回具体分面值列表
    ->setExists(true);

当前写法的问题说明

  • Group组件的setLimit(0)作用是每个分组内返回0条文档,不是不生成分组列表,Solr依然会遍历所有匹配的phone值构建全部分组元数据,这部分计算开销完全没有省掉,这是你当前查询慢的核心原因
  • setMainresult(true)返回的是未分组的主结果集匹配数,和分组唯一值计数没有关系,返回0属于配置逻辑错误,不是功能问题
  • 所有统计类查询必须加$select->setRows(0),禁止返回任何匹配文档内容,这一步至少能减少30%以上的无用IO和内存占用。

额外优化点

  • 所有用于过滤、聚合的字段都要开启docValues,避免filter query、聚合操作走索引全表扫描
  • 仅用于统计的字段统一设置stored=false,不需要存储原始值,能大幅降低索引体积,提升查询速度
  • 统计查询不要关联高亮、拼写检查、联想推荐等无关组件,减少不必要的性能损耗

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 04:24:15