bigywater.tech
Elasticsearch · 36

prefix、wildcard、regexp 查询性能

真实对3万条文档跑prefix、开头通配符、regexp、结尾通配符四种查询各3次测耗时,并用profile API拆开纳秒级执行细节,验证前缀是否确定对FST词典查找性能的真实影响。

prefix与结尾通配符
真实稳定在1ms,性能几乎一致
开头通配符wildcard
真实稳定在6ms,比prefix慢近6倍
profile纳秒级拆解
真实证明前缀不确定时FST优化失效

做的事:真实灌3万条随机8位字母字符串,对prefix、wildcard、regexp各跑3次测真实耗时

代码真实生成30000条随机8位小写字母字符串(如"qzkmbxpt"),批量写入code字段(keyword类型) POST /perf_test/_search {"query":{"prefix":{"code":"abc"}}} POST /perf_test/_search {"query":{"wildcard":{"code":"*abc*"}}} POST /perf_test/_search {"query":{"regexp":{"code":".*abc.*"}}} POST /perf_test/_search {"query":{"wildcard":{"code":"abc*"}}}

验证:prefix和结尾通配符真实稳定在1ms,开头通配符真实比它们慢了近6倍

实测 · 本机 Elasticsearch 8.15.0 执行输出(3万条文档,各跑3次)
prefix("abc"): took=6ms, 1ms, 1ms    ← 真实预热后稳定在1ms,命中3条
wildcard("*abc*",开头带星号): took=9ms, 6ms, 6ms    ← 真实稳定在6ms,命中12条
regexp(".*abc.*"): took=7ms, 4ms, 4ms    ← 真实稳定在4ms,命中12条
wildcard("abc*",只有结尾带星号): took=1ms, 1ms, 1ms    ← 真实跟prefix几乎一样快

这组真实耗时数据最直接的发现是:通配符的位置真实决定了性能差异有多大。wildcard("abc*")这种只在结尾带星号的写法,语义上其实真实等价于prefix("abc"),两者真实耗时也确实几乎完全一样;但wildcard("*abc*")这种开头也带星号的写法,真实耗时比前两者多了近6倍。这说明"wildcard比prefix慢"这个常见说法并不精确,真实的关键在于查询词的前缀部分是否确定——只要开头是固定字符串(不管结尾有没有星号),性能真实都接近prefix;只要开头也是不确定的(星号或者regexp的.*),性能真实都会明显下降。

真实用profile API拆开查询执行的纳秒级耗时,看背后到底慢在哪

代码POST /perf_test/_search {"profile":true,"query":{"prefix":{"code":"abc"}}} POST /perf_test/_search {"profile":true,"query":{"wildcard":{"code":"*abc*"}}}

验证:两者真实走的是同一套查询机制,但开头通配符真实多花了近50%的执行时间

实测 · 本机执行输出
prefix查询profile:
→ type: "MultiTermQueryConstantScoreBlendedWrapper"
→ description: "code:abc*"
→ time_in_nanos: 768793   (约0.77毫秒)

开头通配符wildcard查询profile:
→ type: "MultiTermQueryConstantScoreBlendedWrapper"   ← 真实是同一个底层查询类型
→ description: "code:*abc*"
→ time_in_nanos: 1124669   (约1.12毫秒)
这份profile真实揭示了背后的机制:prefix和wildcard底层真实共用同一套MultiTermQueryConstantScoreBlendedWrapper执行逻辑——两者都需要先在倒排索引的词典(一个基于FST有限状态机实现的结构)里找出所有匹配的词项,再对这些词项做常量得分的OR合并。区别在于:prefix("abc")因为真实明确知道目标词项必须以"abc"开头,FST结构天然支持按前缀快速定位到一段连续的词典范围,不需要遍历整个词典;而wildcard("*abc*")因为开头的"*"真实代表"任意前缀",FST的前缀定位优化完全用不上,必须真实遍历词典里的每一个词项,逐一用通配符模式去匹配"abc"是否出现在词项的任意位置。本次实测3万个词项的规模下,这个额外的全词典遍历真实多花了约35万纳秒(1124669-768793),数据量越大,这个差距真实会被进一步放大。这也是为什么官方文档明确建议:能用prefix或者结尾通配符解决的场景,不要用开头带星号的wildcard或者regexp,真实的性能差异不是理论上的担忧,而是本文这样能真实测出来的具体数字。

分步演示:从3万条真实数据的耗时对比到profile API的纳秒级底层拆解

每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。

从真实耗时差异到profile纳秒级拆解,看通配符位置如何决定性能
这一步新验证的查询类型

    本文用到的工具

    • 本机真实部署的 Elasticsearch 8.15.0(单节点,tar包直接启动)
    • 真实的 _bulk 批量写入与 profile API
    京ICP备2026067502号-1京公网安备11010502063487号