暴暴鱼 發表於 2025-11-13 09:33:19

Redis 数值范围查询(Numeric Range Queries)的实现

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">一、基本语法</a></li><ul class="second_class_ul"><li><a href="#_lab2_0_0">1. 普通范围查询</a></li><li><a href="#_lab2_0_1">2. 排除边界</a></li></ul><li><a href="#_label1">二、FILTER 与执行计划差异</a></li><ul class="second_class_ul"></ul><li><a href="#_label2">三、排序与分页</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_2">1. SORTBY</a></li><li><a href="#_lab2_2_3">2. LIMIT</a></li></ul><li><a href="#_label3">四、典型示例</a></li><ul class="second_class_ul"><li><a href="#_lab2_3_4">示例1:500 &le; price &le; 1000</a></li><li><a href="#_lab2_3_5">示例2:price &gt; 1000</a></li><li><a href="#_lab2_3_6">示例3:price &le; 2000,返回最便宜的 5 辆</a></li><li><a href="#_lab2_3_7">示例4:使用 FILTER(等价于示例1)</a></li></ul><li><a href="#_label4">五、多语言客户端示例</a></li><ul class="second_class_ul"><li><a href="#_lab2_4_8">Python(redis-py + redisearch-py)</a></li><li><a href="#_lab2_4_9">Go(go-redis + redisearch)</a></li></ul><li><a href="#_label5">六、最佳实践</a></li><ul class="second_class_ul"></ul><li><a href="#_label6">七、常见误区</a></li><ul class="second_class_ul"></ul><li><a href="#_label7">八、总结</a></li><ul class="second_class_ul"></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>一、基本语法</h2>
<p class="maodian"><a name="_lab2_0_0"></a></p><h3>1. 普通范围查询</h3>
<p>使用查询字符串语法,将字段名及上下界写在一对方括号内:</p>
<div class="jb51code"><pre class="brush:sql;">FT.SEARCH idx "@field:"
</pre></div>
<ul><li><code>start</code> 和 <code>end</code> 默认均<strong>包含</strong>(inclusive)。</li><li>例如,检索价格在 500 到 1000 区间的商品:</li></ul>
<div class="jb51code"><pre class="brush:sql;">FT.SEARCH idx:bicycle "@price:"
</pre></div>
<p class="maodian"><a name="_lab2_0_1"></a></p><h3>2. 排除边界</h3>
<p>若要<strong>排除</strong>上(或下)界,在该值前加左括号 <code>(</code>:</p>
<div class="jb51code"><pre class="brush:sql;">FT.SEARCH idx "@price:[(1000 +inf]"
</pre></div>
<ul><li>上例匹配 <code>price &gt; 1000</code>。</li><li><code>-inf</code> / <code>+inf</code> 可用于定义&ldquo;下不封顶&rdquo;或&ldquo;上不封顶&rdquo;的开/闭区间。</li></ul>
<p class="maodian"><a name="_label1"></a></p><h2>二、FILTER 与执行计划差异</h2>
<p>除了在查询字符串中直接指定范围,还可借助 <code>FILTER</code> 子句:</p>
<div class="jb51code"><pre class="brush:sql;">FT.SEARCH idx "*" FILTER price 500 1000
</pre></div>
<ul><li><code>FILTER</code> 会在查询解析(如全文 <code>*</code>)完成后再执行数值过滤。</li><li>当索引包含数值字段且仅依赖数值过滤时,若直接在查询字符串中指定范围,RediSearch 能利用索引跳过不匹配文档,性能通常更佳;</li><li>若查询中包含复杂子句或聚合,<code>FILTER</code> 提供了灵活的后置过滤方案。</li></ul>
<p class="maodian"><a name="_label2"></a></p><h2>三、排序与分页</h2>
<p class="maodian"><a name="_lab2_2_2"></a></p><h3>1. SORTBY</h3>
<p>默认情况下,结果按文档出现顺序返回。可用 <code>SORTBY</code> 按数值字段排序:</p>
<div class="jb51code"><pre class="brush:sql;">FT.SEARCH idx:bicycle "@price:[-inf 2000]"
SORTBY price ASC
LIMIT 0 5
</pre></div>
<ul><li><code>ASC</code> / <code>DESC</code> 指定升序或降序。</li><li>未显式排序时,可视为按文档 ID 或添加顺序返回。</li></ul>
<p class="maodian"><a name="_lab2_2_3"></a></p><h3>2. LIMIT</h3>
<ul><li><code>LIMIT offset count</code> 控制返回窗口,实现分页或&ldquo;滚动&rdquo;访问大结果集;</li><li>Redis 默认只返回前 10 条,若需完整结果,务必配合 <code>LIMIT</code>。</li></ul>
<p class="maodian"><a name="_label3"></a></p><h2>四、典型示例</h2>
<p>假设已创建如下索引:</p>
<div class="jb51code"><pre class="brush:sql;">FT.CREATE idx:bicycle ON JSON PREFIX 1 bicycle:
SCHEMA $.price AS price NUMERIC SORTABLE
         $.brand AS brand TEXT SORTABLE
         $.model AS model TEXT SORTABLE
</pre></div>
<p>并已插入若干文档。</p>
<p class="maodian"><a name="_lab2_3_4"></a></p><h3>示例1:500 &le; price &le; 1000</h3>
<div class="jb51code"><pre class="brush:sql;">FT.SEARCH idx:bicycle "@price:"
</pre></div>
<p class="maodian"><a name="_lab2_3_5"></a></p><h3>示例2:price &gt; 1000</h3>
<div class="jb51code"><pre class="brush:sql;">FT.SEARCH idx:bicycle "@price:[(1000 +inf]"
</pre></div>
<p class="maodian"><a name="_lab2_3_6"></a></p><h3>示例3:price &le; 2000,返回最便宜的 5 辆</h3>
<div class="jb51code"><pre class="brush:sql;">FT.SEARCH idx:bicycle "@price:[-inf 2000]"
SORTBY price ASC
LIMIT 0 5
</pre></div>
<p class="maodian"><a name="_lab2_3_7"></a></p><h3>示例4:使用 FILTER(等价于示例1)</h3>
<div class="jb51code"><pre class="brush:sql;">FT.SEARCH idx:bicycle "*" FILTER price 500 1000
</pre></div>
<p class="maodian"><a name="_label4"></a></p><h2>五、多语言客户端示例</h2>
<p>以下以 Python 和 Go 为例,展示如何在客户端执行数值范围查询。</p>
<p class="maodian"><a name="_lab2_4_8"></a></p><h3>Python(redis-py + redisearch-py)</h3>
<div class="jb51code"><pre class="brush:py;">from redis import Redis
from redis.commands.search.field import NumericField, TextField
from redis.commands.search.query import Query

r = Redis()
# 假设已创建索引

q = Query("@price:").sort_by("price", asc=True).paging(0, 10)
res = r.ft("idx:bicycle").search(q)
print(res.total, res.docs)
</pre></div>
<p class="maodian"><a name="_lab2_4_9"></a></p><h3>Go(go-redis + redisearch)</h3>
<div class="jb51code"><pre class="brush:go;">import (
    "fmt"
    "github.com/RediSearch/redisearch-go/redisearch"
)

client := redisearch.NewClient("localhost:6379", "idx:bicycle")

// 构造查询:500 &lt;= price &lt;= 1000,按 price 升序,返回前 10 条
q := redisearch.NewQuery("@price:").
    SortBy("price", true).
    Limit(0, 10)

docs, total, err := client.Search(q)
if err != nil {
    panic(err)
}
fmt.Printf("匹配 %d 条,结果:%v\n", total, docs)
</pre></div>
<p class="maodian"><a name="_label5"></a></p><h2>六、最佳实践</h2>
<ol><li><p><strong>字段声明时加 </strong><code>SORTABLE</code><br />使 <code>SORTBY</code> 与范围过滤都能利用压缩索引,高效跳过不匹配文档。</p></li><li><p><strong>尽量在查询字符串中指定范围</strong><br />直接使用 <code>@field:</code>,避免 <code>FILTER</code> 的后置过滤带来的额外扫描。</p></li><li><p><strong>分页与并发拉取</strong><br />对于百万级以上结果集,避免一次性拉取所有结果,用 <code>LIMIT</code> 分页,并发执行多次查询或使用游标。</p></li><li><p><strong>监控查询性能</strong><br />通过 <code>FT.INFO idx</code> 查看索引 cardinality、统计信息;用 <code>LATENCY</code> 命令监控查询延迟分布。</p></li><li><p><strong>结合其他索引类型</strong><br />可将数值过滤与全文(TEXT)、标签(TAG)、地理(GEO)等条件组合,使用布尔子句优化多维度查询。</p></li></ol>
<p class="maodian"><a name="_label6"></a></p><h2>七、常见误区</h2>
<ul><li><strong>误用通配符 </strong><code>*</code><strong> 加 FILTER</strong><br /><code>FT.SEARCH idx &quot;*&quot; FILTER price x y</code> 等价于全表扫描再过滤,性能逊于直接 <code>@price:</code>。</li><li><strong>忽略</strong> <code>SORTABLE</code><br />未加 <code>SORTABLE</code> 的数值字段无法在查询时高效排序或范围扫描。</li><li><strong>忘记 LIMIT</strong><br />默认只返回前 10 条,若需遍历后续结果,务必使用 <code>LIMIT offset count</code>。</li></ul>
<p class="maodian"><a name="_label7"></a></p><h2>八、总结</h2>
<p>Redis 数值范围查询提供了简洁灵活的语法和高效的执行性能。通过在索引设计阶段添加 <code>NUMERIC SORTABLE</code>,在查询时优先使用 <code>@field:</code>,并结合 <code>SORTBY</code> 和 <code>LIMIT</code>,即可在大规模数据集上快速完成价格区间、阈值筛选等场景下的检索需求。配合监控与索引优化,你将获得更稳定、更低延迟的数值范围查询体验。</p>
頁: [1]
查看完整版本: Redis 数值范围查询(Numeric Range Queries)的实现