<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>飘逸峰</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://blog.hanqunfeng.com/</id>
  <link href="https://blog.hanqunfeng.com/" rel="alternate"/>
  <link href="https://blog.hanqunfeng.com/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, 飘逸峰</rights>
  <subtitle>Spring--Java程序员的春天</subtitle>
  <title>飘逸峰的博客</title>
  <updated>2026-01-16T09:28:51.229Z</updated>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <category term="redis cluster" scheme="https://blog.hanqunfeng.com/tags/redis-cluster/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍如何通过 OpenResty 实现 Nginx + Lua 访问 Redis</li><li class="lvl-2">本文基于<code>redis-7.4.7</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2">OpenResty官网：<a href="https://openresty.org/">https://openresty.org/</a></li><li class="lvl-2"><a href="https://www.runoob.com/lua/lua-tutorial.html">Lua语法参考</a></li></ul><span id="more"></span><h2 id="OpenResty-简介">OpenResty 简介</h2><ul class="lvl-0"><li class="lvl-2"><p>OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台，其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。</p></li><li class="lvl-2"><p>OpenResty® 通过汇聚各种设计精良的 Nginx 模块（主要由 OpenResty 团队自主开发），从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样，Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块，快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。</p></li><li class="lvl-2"><p>OpenResty® 的目标是让你的Web服务直接跑在 Nginx 服务内部，充分利用 Nginx 的非阻塞 I/O 模型，不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。</p></li><li class="lvl-2"><p>一句话：OpenResty 就是加载了 Lua 模块的 Nginx。</p></li></ul><h2 id="OpenResty-安装">OpenResty 安装</h2><h3 id="MacOS">MacOS</h3><ul class="lvl-0"><li class="lvl-2"><p><a href="https://openresty.org/cn/download.html">官网参考</a></p></li><li class="lvl-2"><p>MacOS 版本：<code>15.7.3</code></p></li><li class="lvl-2"><p>通过 Homebrew 安装</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">brew tap openresty/brew</span><br><span class="line">brew install openresty</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>安装时报如下错误，提示找不到<code>GeoIP</code></p></li></ul><blockquote><p>GeoIP 是一个 IP 地址 → 地理位置映射库和模块，核心作用是：根据客户端 IP，解析出国家、城市、经纬度、运营商等地理信息。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">checking <span class="keyword">for</span> GeoIP library ... not found</span><br><span class="line">checking <span class="keyword">for</span> GeoIP library <span class="keyword">in</span> /usr/local/ ... not found</span><br><span class="line">checking <span class="keyword">for</span> GeoIP library <span class="keyword">in</span> /usr/pkg/ ... not found</span><br><span class="line">checking <span class="keyword">for</span> GeoIP library <span class="keyword">in</span> /opt/local/ ... not found</span><br><span class="line">checking <span class="keyword">for</span> GeoIP library <span class="keyword">in</span> /opt/homebrew/ ... not found</span><br><span class="line"></span><br><span class="line">./configure: error: the GeoIP module requires the GeoIP library.</span><br><span class="line">You can either <span class="keyword">do</span> not <span class="built_in">enable</span> the module or install the library.</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>解决办法：这是因为 Homebrew 官方仓库已经不再支持 GeoIP，而是使用 GeoIP2 替代，但 Openresty 暂不支持 GeoIP2，所以这里只能选择不启用 GeoIP 模块。</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 编辑 openresty 的安装脚本</span></span><br><span class="line">brew edit openresty/brew/openresty</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将下面的行注释掉后保存并退出</span></span><br><span class="line"><span class="comment"># args &lt;&lt; &quot;--with-http_geoip_module&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 重新安装</span></span><br><span class="line">brew reinstall openresty</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>OpenResty 命令说明</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">openresty -h</span><br><span class="line">nginx version: openresty/1.29.2.1</span><br><span class="line">Usage: nginx [-?hvVtTq] [-s signal] [-p prefix]</span><br><span class="line">             [-e filename] [-c filename] [-g directives]</span><br><span class="line"></span><br><span class="line">Options:</span><br><span class="line">  -?,-h         : this <span class="built_in">help</span></span><br><span class="line">  -v            : show version and <span class="built_in">exit</span></span><br><span class="line">  -V            : show version and configure options <span class="keyword">then</span> <span class="built_in">exit</span></span><br><span class="line">  -t            : <span class="built_in">test</span> configuration and <span class="built_in">exit</span></span><br><span class="line">  -T            : <span class="built_in">test</span> configuration, dump it and <span class="built_in">exit</span></span><br><span class="line">  -q            : suppress non-error messages during configuration testing</span><br><span class="line">  -s signal     : send signal to a master process: stop, quit, reopen, reload</span><br><span class="line">  -p prefix     : <span class="built_in">set</span> prefix path (default: /usr/local/Cellar/openresty/1.29.2.1_1/nginx/)</span><br><span class="line">  -e filename   : <span class="built_in">set</span> error <span class="built_in">log</span> file (default: /usr/local/var/log/nginx/error.log)</span><br><span class="line">  -c filename   : <span class="built_in">set</span> configuration file (default: /usr/local/etc/openresty/nginx.conf)</span><br><span class="line">  -g directives : <span class="built_in">set</span> global directives out of configuration file</span><br></pre></td></tr></table></figure><table><thead><tr><th>参数</th><th>完整写法</th><th>中文含义</th><th>典型使用场景</th><th>示例</th></tr></thead><tbody><tr><td><code>-h</code> / <code>-?</code></td><td><code>openresty -h</code></td><td>显示帮助信息并退出</td><td>快速查看可用参数</td><td><code>openresty -h</code></td></tr><tr><td><code>-v</code></td><td><code>openresty -v</code></td><td>显示版本号</td><td>确认运行版本</td><td><code>openresty -v</code></td></tr><tr><td><code>-V</code></td><td><code>openresty -V</code></td><td>显示版本号 + 编译参数</td><td>排查模块、编译选项、依赖</td><td><code>openresty -V</code></td></tr><tr><td><code>-t</code></td><td><code>openresty -t</code></td><td>校验配置文件合法性并退出</td><td>修改配置后验证语法</td><td><code>openresty -t</code></td></tr><tr><td><code>-T</code></td><td><code>openresty -T</code></td><td>校验配置并打印完整配置内容</td><td>排查 include 文件、调试配置加载顺序</td><td><code>openresty -T</code></td></tr><tr><td><code>-q</code></td><td><code>openresty -t -q</code></td><td>测试配置时只输出错误信息</td><td>CI / 自动化脚本</td><td><code>openresty -t -q</code></td></tr><tr><td><code>-s</code></td><td><code>openresty -s reload</code></td><td>向 master 进程发送控制信号</td><td>服务管理（重载、停止等）</td><td><code>openresty -s reload</code></td></tr><tr><td></td><td><code>stop</code></td><td>立即停止服务（强制）</td><td>紧急停服</td><td><code>openresty -s stop</code></td></tr><tr><td></td><td><code>quit</code></td><td>优雅停止服务（处理完请求后退出）</td><td>平滑下线</td><td><code>openresty -s quit</code></td></tr><tr><td></td><td><code>reload</code></td><td>平滑重载配置</td><td>发布配置变更</td><td><code>openresty -s reload</code></td></tr><tr><td></td><td><code>reopen</code></td><td>重新打开日志文件</td><td>日志切割后使用</td><td><code>openresty -s reopen</code></td></tr><tr><td><code>-p</code></td><td><code>openresty -p /path</code></td><td>指定运行前缀目录（prefix）</td><td>多实例部署、定制目录结构</td><td><code>openresty -p /opt/openresty</code></td></tr><tr><td><code>-e</code></td><td><code>openresty -e file</code></td><td>指定错误日志路径</td><td>临时调试错误日志</td><td><code>openresty -e /tmp/error.log</code></td></tr><tr><td><code>-c</code></td><td><code>openresty -c file</code></td><td>指定配置文件路径</td><td>使用非默认配置启动</td><td><code>openresty -c ./nginx.conf</code></td></tr><tr><td><code>-g</code></td><td><code>openresty -g &quot;daemon off;&quot;</code></td><td>设置全局指令（覆盖配置文件）</td><td>容器化 / 临时调试</td><td><code>openresty -g &quot;daemon off;&quot;</code></td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>安装后的目录</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装目录</span></span><br><span class="line">/usr/local/opt/openresty</span><br><span class="line"><span class="comment"># 配置文件目录</span></span><br><span class="line">/usr/local/etc/openresty/</span><br></pre></td></tr></table></figure><h3 id="Linux">Linux</h3><ul class="lvl-0"><li class="lvl-2"><p><a href="https://openresty.org/cn/download.html">官网参考</a></p></li><li class="lvl-2"><p>这里以 <code>Amazon Linux 2023(内核 6.1)</code> 系统为例，从<a href="https://openresty.org/cn/linux-packages.html#amazon-linux">这里</a>找到对应的安装方法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> yum install -y yum-utils</span><br><span class="line"><span class="built_in">sudo</span> yum-config-manager --add-repo https://openresty.org/package/amazon/openresty.repo</span><br><span class="line"><span class="built_in">sudo</span> yum install -y openresty</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>安装后的目录</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装目录</span></span><br><span class="line">/usr/local/openresty</span><br><span class="line"><span class="comment"># 配置文件目录</span></span><br><span class="line">/usr/local/openresty/nginx/conf</span><br></pre></td></tr></table></figure><h2 id="一个简单的示例">一个简单的示例</h2><ul class="lvl-0"><li class="lvl-2"><p><code>vim test-nginx.conf</code></p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">worker_processes  1;</span><br><span class="line">events &#123;</span><br><span class="line">    worker_connections 1024;</span><br><span class="line">&#125;</span><br><span class="line">http &#123;</span><br><span class="line">    server &#123;</span><br><span class="line">        listen 8080;</span><br><span class="line">        location / &#123;</span><br><span class="line">            default_type text/html;</span><br><span class="line">            <span class="comment"># 这是 OpenResty 提供的 Lua 指令: 在 HTTP 请求的 Content 阶段 执行 Lua 代码，用于生成响应内容。</span></span><br><span class="line">            content_by_lua_block &#123;</span><br><span class="line">                -- ngx: OpenResty 提供的全局对象，封装了 Nginx API，可用于：写响应、读请求、操作 header、访问共享内存、控制状态码</span><br><span class="line">                -- ngx.say(...): 向 HTTP 响应体写入内容，自动追加换行符 \n，可多次调用</span><br><span class="line">                -- 等价于：ngx.print(<span class="string">&quot;&lt;p&gt;hello, world&lt;/p&gt;\n&quot;</span>)</span><br><span class="line">                ngx.say(<span class="string">&quot;&lt;p&gt;hello, world&lt;/p&gt;&quot;</span>)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>实际响应效果，浏览器收到：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">HTTP/1.1 200 OK</span><br><span class="line">Content-Type: text/html</span><br><span class="line"></span><br><span class="line">&lt;p&gt;hello, world&lt;/p&gt;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>启动</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openresty -p `<span class="built_in">pwd</span>` -c ./test-nginx.conf</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>访问</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">curl http://127.0.0.1:8080</span><br><span class="line"><span class="comment"># 结果</span></span><br><span class="line">&lt;p&gt;hello, world&lt;/p&gt;</span><br></pre></td></tr></table></figure><h2 id="Nginx-请求处理阶段划分-与-Lua-指令">Nginx 请求处理阶段划分 与 Lua 指令</h2><ul class="lvl-0"><li class="lvl-2"><p>在 OpenResty 中，一个 HTTP 请求大致经历以下核心阶段：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rewrite  →  access  →  content  →  <span class="built_in">log</span></span><br></pre></td></tr></table></figure><table><thead><tr><th>阶段</th><th>主要职责</th><th>典型指令</th></tr></thead><tbody><tr><td>rewrite</td><td>URL 重写、变量计算、跳转</td><td>rewrite、set、rewrite_by_lua*</td></tr><tr><td>access</td><td>访问控制、鉴权、限流</td><td>allow/deny、auth_request、access_by_lua*</td></tr><tr><td>content</td><td>生成响应内容</td><td>proxy_pass、root、fastcgi_pass、content_by_lua*</td></tr><tr><td>log</td><td>日志记录、统计</td><td>access_log、log_by_lua*</td></tr></tbody></table><blockquote><p>带 <code>*_by_lua</code> 的指令是 OpenResty 在对应阶段注入 Lua 执行逻辑。</p></blockquote><ul class="lvl-0"><li class="lvl-2"><p>Lua 指令与 Nginx 阶段关系对照表</p></li></ul><table><thead><tr><th>Nginx 阶段</th><th>Lua 指令</th><th>是否可读请求</th><th>是否可写响应</th><th>是否推荐输出内容</th><th>典型业务</th></tr></thead><tbody><tr><td>rewrite</td><td>rewrite_by_lua*</td><td>✅</td><td>⚠️（不建议）</td><td>❌</td><td>URL 重写、变量</td></tr><tr><td>access</td><td>access_by_lua*</td><td>✅</td><td>⚠️（仅拒绝时）</td><td>❌</td><td>鉴权、限流</td></tr><tr><td>content</td><td>content_by_lua*</td><td>✅</td><td>✅</td><td>✅</td><td>动态服务</td></tr><tr><td>log</td><td>log_by_lua*</td><td>⚠️</td><td>❌</td><td>❌</td><td>日志、统计</td></tr></tbody></table><blockquote><p><code>*_by_lua* 指令</code> 支持三种形式：</p></blockquote><table><thead><tr><th>写法形式</th><th>语法示例</th><th>含义说明</th><th>Lua 代码来源</th><th>是否支持多行</th><th>是否支持复杂逻辑</th><th>热更新友好度</th><th>推荐使用场景</th><th>注意事项</th></tr></thead><tbody><tr><td><code>_by_lua_block &#123; ... &#125;</code></td><td><code>content_by_lua_block &#123; ngx.say(&quot;hello&quot;) &#125; </code></td><td>在 Nginx 配置文件中直接以内嵌代码块方式书写 Lua</td><td>nginx.conf 内嵌</td><td>✅ 支持</td><td>⚠️ 一般</td><td>⚠️ 中等（需 reload）</td><td>简单逻辑、Demo、调试</td><td>配置文件可读性下降</td></tr><tr><td><code>_by_lua_file /path/a.lua</code></td><td><code>access_by_lua_file lua/auth.lua; </code></td><td>从外部 Lua 文件加载并执行</td><td>独立 Lua 脚本文件</td><td>✅ 支持</td><td>✅ 强</td><td>✅ 高（代码可版本管理）</td><td>生产环境、复杂业务</td><td>路径必须正确</td></tr><tr><td><code>_by_lua &quot;inline lua&quot;</code></td><td><code>content_by_lua &quot;ngx.say('ok')&quot;; </code></td><td>将 Lua 代码作为字符串参数传入</td><td>Nginx 指令字符串</td><td>❌ 不友好</td><td>❌ 弱</td><td>❌ 差</td><td>临时测试、单行逻辑</td><td>转义复杂，难维护</td></tr></tbody></table><blockquote><p>工程实践推荐等级</p></blockquote><table><thead><tr><th>使用方式</th><th>推荐等级</th><th>理由</th></tr></thead><tbody><tr><td><code>_by_lua_file</code></td><td>⭐⭐⭐⭐⭐</td><td>可维护、可测试、可版本管理</td></tr><tr><td><code>_by_lua_block</code></td><td>⭐⭐⭐</td><td>适合简单逻辑</td></tr><tr><td><code>_by_lua &quot;...&quot;</code></td><td>⭐</td><td>仅适合临时验证</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">server &#123;</span><br><span class="line">    listen 8080;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 方式1.content_by_lua_block ：lua 内嵌代码块</span></span><br><span class="line">    location /hello-lua &#123;</span><br><span class="line">        default_type <span class="string">&#x27;text/plain&#x27;</span>;</span><br><span class="line">        content_by_lua_block &#123;</span><br><span class="line">        ngx.say(<span class="string">&quot;Hello World! Lua &amp; Nginx .&quot;</span>)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 方式2.content_by_lua_file ：lua 脚本文件路径</span></span><br><span class="line">    location /hello-lua-file &#123;</span><br><span class="line">        default_type <span class="string">&#x27;text/html&#x27;</span>;</span><br><span class="line">        content_by_lua_file ./lua/hello.lua;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 方式3.access_by_lua 在请求访问阶段处理用于访问控制 ：lua 字符串</span></span><br><span class="line">    location /hello-lua-access &#123;</span><br><span class="line">        default_type <span class="string">&#x27;text/html&#x27;</span>;</span><br><span class="line">        access_by_lua <span class="string">&#x27;</span></span><br><span class="line"><span class="string">        local message = &quot;403 - Hello World! Lua &amp; Nginx  access_by_lua&quot;</span></span><br><span class="line"><span class="string">        ngx.say(message)</span></span><br><span class="line"><span class="string">        &#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 方式4.content_by_lua 在内容处理阶段接受请求并输出响应：lua 字符串</span></span><br><span class="line">    location /hello-lua-content &#123;</span><br><span class="line">        default_type <span class="string">&#x27;text/html&#x27;</span>;</span><br><span class="line">        content_by_lua <span class="string">&quot;ngx.print(&#x27;Hello World!&#x27;)&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="如何获取客户端请求数据？">如何获取客户端请求数据？</h2><ul class="lvl-0"><li class="lvl-2"><p>OpenResty 把 HTTP 请求映射为 Lua API，主要分为 5 类</p></li></ul><h3 id="✅-1-获取-URL-Query-参数（GET）">✅ 1. 获取 URL / Query 参数（GET）</h3><ul class="lvl-0"><li class="lvl-2"><p>示例请求</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GET /api?user=tom&amp;age=18</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>Lua 获取方式</p></li></ul><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> args = ngx.req.get_uri_args()</span><br><span class="line"><span class="comment">-- args 是一个 table，获取不到参数返回 nil</span></span><br><span class="line"><span class="keyword">local</span> user = args[<span class="string">&quot;user&quot;</span>]</span><br><span class="line"><span class="keyword">local</span> age = args[<span class="string">&quot;age&quot;</span>]</span><br></pre></td></tr></table></figure><h3 id="✅-2-获取-POST-表单参数（application-x-www-form-urlencoded）">✅ 2. 获取 POST 表单参数（application/x-www-form-urlencoded）</h3><ul class="lvl-0"><li class="lvl-2"><p>示例请求</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 示例请求</span></span><br><span class="line">POST /api</span><br><span class="line">Content-Type: application/x-www-form-urlencoded</span><br><span class="line"></span><br><span class="line">user=tom&amp;age=18</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>Lua 获取方式</p></li></ul><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 必须先读取 Body</span></span><br><span class="line">ngx.req.read_body()</span><br><span class="line"><span class="keyword">local</span> args = ngx.req.get_post_args()</span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> user = args[<span class="string">&quot;user&quot;</span>]</span><br><span class="line"><span class="keyword">local</span> age = args[<span class="string">&quot;age&quot;</span>]</span><br></pre></td></tr></table></figure><h3 id="✅-3-获取-JSON-Body（application-json）">✅ 3. 获取 JSON Body（application/json）</h3><ul class="lvl-0"><li class="lvl-2"><p>示例请求</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 示例请求</span></span><br><span class="line">POST /api</span><br><span class="line">Content-Type: application/json</span><br><span class="line"></span><br><span class="line">&#123;<span class="string">&quot;user&quot;</span>:<span class="string">&quot;tom&quot;</span>,<span class="string">&quot;age&quot;</span>:18&#125;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>Lua 获取方式</p></li></ul><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 必须先读取 Body</span></span><br><span class="line">ngx.req.read_body()</span><br><span class="line"><span class="keyword">local</span> body = ngx.req.get_body_data()</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 加载 JSON 库</span></span><br><span class="line"><span class="keyword">local</span> cjson = <span class="built_in">require</span> <span class="string">&quot;cjson.safe&quot;</span></span><br><span class="line"><span class="comment">-- 解析为 JSON 对象</span></span><br><span class="line"><span class="keyword">local</span> data, err = cjson.decode(body)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> data <span class="keyword">then</span></span><br><span class="line">   ngx.<span class="built_in">log</span>(ngx.WARN, <span class="string">&quot;json decode failed: &quot;</span>, err)</span><br><span class="line">   <span class="keyword">return</span> ngx.<span class="built_in">exit</span>(<span class="number">400</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> user = data.user</span><br><span class="line"><span class="keyword">local</span> age = data.age</span><br></pre></td></tr></table></figure><h3 id="✅-4-获取-HTTP-Headers">✅ 4. 获取 HTTP Headers</h3><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 获取所有 Header</span></span><br><span class="line"><span class="keyword">local</span> headers = ngx.req.get_headers()</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> k, v <span class="keyword">in</span> <span class="built_in">pairs</span>(headers) <span class="keyword">do</span></span><br><span class="line">    ngx.say(k, <span class="string">&quot; = &quot;</span>, v)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 获取单个 Header</span></span><br><span class="line"><span class="keyword">local</span> ua = ngx.var.http_user_agent</span><br><span class="line"><span class="keyword">local</span> token = ngx.var.http_authorization</span><br></pre></td></tr></table></figure><h3 id="✅-5-获取请求方法、URI、路径等">✅ 5. 获取请求方法、URI、路径等</h3><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 比如请求：curl http://127.0.0.1:8080/api\?name\=zhangsan\&amp;age\=20</span></span><br><span class="line"><span class="comment">-- 请求方法，如 GET，POST</span></span><br><span class="line"><span class="keyword">local</span> method = ngx.req.get_method() <span class="comment">-- GET</span></span><br><span class="line"><span class="comment">-- 请求 URI</span></span><br><span class="line"><span class="keyword">local</span> uri = ngx.var.uri <span class="comment">-- /api</span></span><br><span class="line"><span class="comment">-- QueryString</span></span><br><span class="line"><span class="keyword">local</span> args = ngx.var.args <span class="comment">-- name=zhangsan&amp;age=20</span></span><br><span class="line"><span class="comment">-- 域名或IP</span></span><br><span class="line"><span class="keyword">local</span> host = ngx.var.host <span class="comment">-- 127.0.0.1</span></span><br><span class="line"><span class="comment">-- 端口</span></span><br><span class="line"><span class="keyword">local</span> port = ngx.var.server_port <span class="comment">-- 8080</span></span><br><span class="line"><span class="comment">-- 请求协议</span></span><br><span class="line"><span class="keyword">local</span> scheme = ngx.var.scheme <span class="comment">-- http</span></span><br></pre></td></tr></table></figure><h3 id="总结">总结</h3><table><thead><tr><th>数据类型</th><th>Lua API</th></tr></thead><tbody><tr><td>客户端 IP</td><td><code>ngx.var.remote_addr</code></td></tr><tr><td>请求方法</td><td><code>ngx.req.get_method()</code></td></tr><tr><td>完整 URI</td><td><code>ngx.var.request_uri</code></td></tr><tr><td>Path</td><td><code>ngx.var.uri</code></td></tr><tr><td>QueryString</td><td><code>ngx.var.args</code></td></tr><tr><td>GET 参数</td><td><code>ngx.req.get_uri_args()</code></td></tr><tr><td>POST 表单</td><td><code>ngx.req.get_post_args()</code></td></tr><tr><td>Raw Body</td><td><code>ngx.req.get_body_data()</code></td></tr><tr><td>JSON Body</td><td><code>cjson.decode()</code></td></tr><tr><td>Headers</td><td><code>ngx.req.get_headers()</code></td></tr><tr><td>单个 Header</td><td><code>ngx.var.http_xxx</code></td></tr><tr><td>Cookie</td><td><code>ngx.var.http_cookie</code></td></tr></tbody></table><h2 id="Nginx-Lua-Redis-限流完整示例">Nginx + Lua + Redis 限流完整示例</h2><h3 id="限流设计说明">限流设计说明</h3><ul class="lvl-0"><li class="lvl-2"><p>限流规则</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">维度：客户端 IP</span><br><span class="line">窗口：60 秒</span><br><span class="line">阈值：10 次</span><br><span class="line">算法：固定窗口计数器(0秒-60秒的每个整分钟内)，实现复杂度极低，性能极高，但有边界突刺问题，可以使用滑动窗口算法(第一次访问后的最近 60 秒内)</span><br><span class="line">存储：Redis</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>Redis Key 设计</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">rate:&#123;client_ip&#125;:&#123;minute_timestamp&#125;</span><br><span class="line"><span class="comment"># TTL：70 秒（防止残留）</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p><code>vim redis-nginx.conf</code></p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 指定 Nginx Worker 进程数量，生产环境：等于 CPU 核心数，建议配置为 auto，自动适配</span></span><br><span class="line">worker_processes auto;</span><br><span class="line"><span class="comment"># 指定错误日志路径，所有错误都会写入该日志文件，包括 Lua ngx.log(ngx.ERR, ...)</span></span><br><span class="line">error_log logs/error.log;</span><br><span class="line"><span class="comment"># 定义事件模型参数</span></span><br><span class="line">events &#123;</span><br><span class="line">    <span class="comment"># 单个 Worker 最大并发连接数</span></span><br><span class="line">    worker_connections  1024;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">http &#123;</span><br><span class="line">    <span class="comment"># Lua 模块加载路径</span></span><br><span class="line">    <span class="comment"># 语法规则</span></span><br><span class="line">    <span class="comment">#  ?.lua 表示模块文件名占位符。</span></span><br><span class="line">    <span class="comment">#  ;; 表示 保留默认路径，否则会覆盖系统默认路径。</span></span><br><span class="line">    lua_package_path <span class="string">&quot;/usr/local/openresty/lualib/?.lua;;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># ----------------------------</span></span><br><span class="line">    <span class="comment"># Redis 原子限流 Lua 脚本</span></span><br><span class="line">    <span class="comment"># ----------------------------</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建一块 共享内存区，名称：redis_scripts，大小：1MB（Worker 之间共享内存）</span></span><br><span class="line">    <span class="comment"># 常用于：缓存 Lua 脚本、Token、计数器、配置信息</span></span><br><span class="line">    lua_shared_dict redis_scripts 1m;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Worker 初始化阶段加载 Lua，在 每个 Worker 启动时执行一次</span></span><br><span class="line">    <span class="comment"># 适合：加载配置、初始化缓存、预加载脚本、启动定时器</span></span><br><span class="line">    init_worker_by_lua_block &#123;</span><br><span class="line">        -- 将 Lua 脚本加载到 Nginx Worker 内存</span><br><span class="line">        <span class="built_in">local</span> script = [[</span><br><span class="line">            -- 对指定 Key 进行自增</span><br><span class="line">            <span class="built_in">local</span> cnt = redis.call(<span class="string">&quot;INCR&quot;</span>, KEYS[1])</span><br><span class="line">            -- 如果是第一次创建 Key</span><br><span class="line">            <span class="keyword">if</span> cnt == 1 <span class="keyword">then</span></span><br><span class="line">                -- 设置 Key 过期时间(秒)</span><br><span class="line">                redis.call(<span class="string">&quot;EXPIRE&quot;</span>, KEYS[1], ARGV[1])</span><br><span class="line">            end</span><br><span class="line">            -- 返回当前计数</span><br><span class="line">            <span class="built_in">return</span> cnt</span><br><span class="line">        ]]</span><br><span class="line">        -- 将 Lua 脚本存入共享内存，避免每次请求拼接 Lua 脚本字符串，提升性能</span><br><span class="line">        <span class="built_in">local</span> dict = ngx.shared.redis_scripts</span><br><span class="line">        -- Key：rate_limit_lua，Value：Lua 脚本</span><br><span class="line">        dict:<span class="built_in">set</span>(<span class="string">&quot;rate_limit_lua&quot;</span>, script)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    server &#123;</span><br><span class="line">        listen 8080;</span><br><span class="line"></span><br><span class="line">        location /api &#123;</span><br><span class="line">            <span class="comment"># 在 content 阶段执行 Lua，完全由 Lua 生成响应内容</span></span><br><span class="line">            content_by_lua_block &#123;</span><br><span class="line">                -- 加载 OpenResty 官方 Redis 客户端</span><br><span class="line">                <span class="built_in">local</span> redis = require <span class="string">&quot;resty.redis&quot;</span></span><br><span class="line">                -- 创建 Redis 对象</span><br><span class="line">                <span class="built_in">local</span> red = redis:new()</span><br><span class="line">                -- Redis 连接信息</span><br><span class="line">                <span class="built_in">local</span> redis_ip = <span class="string">&quot;127.0.0.1&quot;</span></span><br><span class="line">                <span class="built_in">local</span> redis_port = 6379</span><br><span class="line">                <span class="built_in">local</span> redis_timeout = 500</span><br><span class="line">                <span class="built_in">local</span> redis_user = <span class="string">&quot;admin&quot;</span></span><br><span class="line">                <span class="built_in">local</span> redis_pass = <span class="string">&quot;123456&quot;</span></span><br><span class="line"></span><br><span class="line">                -- 关闭redis连接的工具方法，其实是放入连接池</span><br><span class="line">                <span class="built_in">local</span> <span class="keyword">function</span> close_redis(red)</span><br><span class="line">                    <span class="built_in">local</span> pool_max_idle_time = 10000 -- 连接的空闲时间，单位是毫秒</span><br><span class="line">                    <span class="built_in">local</span> pool_size = 100 --连接池大小</span><br><span class="line">                    -- 将连接放回连接池，后续请求可复用</span><br><span class="line">                    <span class="built_in">local</span> ok, err = red:set_keepalive(pool_max_idle_time, pool_size)</span><br><span class="line">                    <span class="keyword">if</span> not ok <span class="keyword">then</span></span><br><span class="line">                        -- 失败时记录错误日志</span><br><span class="line">                        ngx.log(ngx.ERR, <span class="string">&quot;放入redis连接池失败: &quot;</span>, err)</span><br><span class="line">                    end</span><br><span class="line">                end</span><br><span class="line"></span><br><span class="line">                -- 超时（毫秒）</span><br><span class="line">                red:set_timeout(redis_timeout)</span><br><span class="line"></span><br><span class="line">                -- 建立 Redis 连接，若连接池有空闲连接会复用</span><br><span class="line">                <span class="built_in">local</span> ok, err = red:connect(redis_ip, redis_port)</span><br><span class="line">                <span class="keyword">if</span> not ok <span class="keyword">then</span></span><br><span class="line">                    ngx.log(ngx.ERR, <span class="string">&quot;redis connect failed: &quot;</span>, err)</span><br><span class="line">                    -- 连接失败返回 500 错误码</span><br><span class="line">                    <span class="built_in">return</span> ngx.exit(500)</span><br><span class="line">                end</span><br><span class="line"></span><br><span class="line">                -- ACL 认证（仅新连接）</span><br><span class="line">                -- 判断是否是新连接，避免重复认证浪费性能</span><br><span class="line">                <span class="keyword">if</span> red:get_reused_times() == 0 <span class="keyword">then</span></span><br><span class="line">                    <span class="built_in">local</span> ok, err = red:auth(redis_user, redis_pass)</span><br><span class="line">                    <span class="keyword">if</span> not ok <span class="keyword">then</span></span><br><span class="line">                        ngx.log(ngx.ERR, <span class="string">&quot;redis auth failed: &quot;</span>, err)</span><br><span class="line">                        -- 认证失败返回 500 错误码</span><br><span class="line">                        <span class="built_in">return</span> ngx.exit(500)</span><br><span class="line">                    end</span><br><span class="line">                end</span><br><span class="line"></span><br><span class="line">                -- 客户端 IP，这里 remote_addr 是nginx的内置变量</span><br><span class="line">                <span class="built_in">local</span> client_ip = ngx.var.remote_addr or <span class="string">&quot;unknown&quot;</span></span><br><span class="line"></span><br><span class="line">                -- 当前分钟窗口</span><br><span class="line">                <span class="built_in">local</span> now = ngx.time() -- 获取当前时间戳（秒）</span><br><span class="line">                <span class="built_in">local</span> minute = math.floor(now / 60) -- 转换为分钟窗口编号</span><br><span class="line"></span><br><span class="line">                -- Redis Key: 每个 IP 每分钟一个计数器</span><br><span class="line">                <span class="built_in">local</span> key = <span class="string">&quot;rate:&quot;</span> .. client_ip .. <span class="string">&quot;:&quot;</span> .. minute</span><br><span class="line"></span><br><span class="line">                -- 从共享字典读取 Lua 脚本</span><br><span class="line">                <span class="built_in">local</span> dict = ngx.shared.redis_scripts</span><br><span class="line">                <span class="built_in">local</span> script = dict:get(<span class="string">&quot;rate_limit_lua&quot;</span>)</span><br><span class="line"></span><br><span class="line">                -- 执行 Redis Lua（原子）</span><br><span class="line">                <span class="built_in">local</span> ttl = 70</span><br><span class="line">                <span class="built_in">local</span> cnt, err = red:<span class="built_in">eval</span>(script, 1, key, ttl)</span><br><span class="line">                <span class="keyword">if</span> not cnt <span class="keyword">then</span></span><br><span class="line">                    ngx.log(ngx.ERR, <span class="string">&quot;redis eval failed: &quot;</span>, err)</span><br><span class="line">                    <span class="built_in">return</span> ngx.exit(500)</span><br><span class="line">                end</span><br><span class="line"></span><br><span class="line">                -- 限流判断</span><br><span class="line">                <span class="built_in">local</span> <span class="built_in">limit</span> = 10</span><br><span class="line">                <span class="keyword">if</span> cnt &gt; <span class="built_in">limit</span> <span class="keyword">then</span></span><br><span class="line">                    ngx.status = 429</span><br><span class="line">                    ngx.say(<span class="string">&quot;Too Many Requests, limit=&quot;</span>, <span class="built_in">limit</span>)</span><br><span class="line">                    <span class="built_in">return</span> ngx.exit(429)  -- 返回 HTTP 429（Too Many Requests）。</span><br><span class="line">                end</span><br><span class="line"></span><br><span class="line">                -- 放回连接池</span><br><span class="line">                close_redis(red)</span><br><span class="line"></span><br><span class="line">                -- 正常返回</span><br><span class="line">                ngx.say(<span class="string">&quot;OK, request count=&quot;</span>, cnt)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>启动</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openresty -p `<span class="built_in">pwd</span>` -c ./redis-nginx.conf</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>访问</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> &#123;1..15&#125;; <span class="keyword">do</span></span><br><span class="line">  curl http://localhost:8080/api</span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="comment"># 结果</span></span><br><span class="line">OK, request count=1</span><br><span class="line">OK, request count=2</span><br><span class="line">OK, request count=3</span><br><span class="line">OK, request count=4</span><br><span class="line">OK, request count=5</span><br><span class="line">OK, request count=6</span><br><span class="line">OK, request count=7</span><br><span class="line">OK, request count=8</span><br><span class="line">OK, request count=9</span><br><span class="line">OK, request count=10</span><br><span class="line">Too Many Requests, <span class="built_in">limit</span>=10</span><br><span class="line">Too Many Requests, <span class="built_in">limit</span>=10</span><br><span class="line">Too Many Requests, <span class="built_in">limit</span>=10</span><br><span class="line">Too Many Requests, <span class="built_in">limit</span>=10</span><br><span class="line">Too Many Requests, <span class="built_in">limit</span>=10</span><br></pre></td></tr></table></figure><h3 id="生产环境推荐使用-content-by-lua-file">生产环境推荐使用 <code>content_by_lua_file</code></h3><ul class="lvl-0"><li class="lvl-2"><p>修改 <code>redis-nginx.conf</code></p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 指定 Nginx Worker 进程数量，生产环境：等于 CPU 核心数，建议配置为 auto，自动适配</span></span><br><span class="line">worker_processes auto;</span><br><span class="line"><span class="comment"># 指定错误日志路径，所有错误都会写入该日志文件，包括 Lua ngx.log(ngx.ERR, ...)</span></span><br><span class="line">error_log logs/error.log;</span><br><span class="line"><span class="comment"># 定义事件模型参数</span></span><br><span class="line">events &#123;</span><br><span class="line">    <span class="comment"># 单个 Worker 最大并发连接数</span></span><br><span class="line">    worker_connections  1024;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">http &#123;</span><br><span class="line">    <span class="comment"># Lua 模块加载路径</span></span><br><span class="line">    <span class="comment"># 语法规则</span></span><br><span class="line">    <span class="comment">#  ?.lua 表示模块文件名占位符。</span></span><br><span class="line">    <span class="comment">#  ;; 表示 保留默认路径，否则会覆盖系统默认路径。</span></span><br><span class="line">    lua_package_path <span class="string">&quot;/usr/local/openresty/lualib/?.lua;;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># ----------------------------</span></span><br><span class="line">    <span class="comment"># Redis 原子限流 Lua 脚本</span></span><br><span class="line">    <span class="comment"># ----------------------------</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建一块 共享内存区，名称：redis_scripts，大小：1MB（Worker 之间共享内存）</span></span><br><span class="line">    <span class="comment"># 常用于：缓存 Lua 脚本、Token、计数器、配置信息</span></span><br><span class="line">    lua_shared_dict redis_scripts 1m;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Worker 初始化阶段加载 Lua，在 每个 Worker 启动时执行一次</span></span><br><span class="line">    <span class="comment"># 适合：加载配置、初始化缓存、预加载脚本、启动定时器</span></span><br><span class="line">    init_worker_by_lua_block &#123;</span><br><span class="line">        -- 将 Lua 脚本加载到 Nginx Worker 内存</span><br><span class="line">        <span class="built_in">local</span> script = [[</span><br><span class="line">            -- 对指定 Key 进行自增</span><br><span class="line">            <span class="built_in">local</span> cnt = redis.call(<span class="string">&quot;INCR&quot;</span>, KEYS[1])</span><br><span class="line">            -- 如果是第一次创建 Key</span><br><span class="line">            <span class="keyword">if</span> cnt == 1 <span class="keyword">then</span></span><br><span class="line">                -- 设置 Key 过期时间(秒)</span><br><span class="line">                redis.call(<span class="string">&quot;EXPIRE&quot;</span>, KEYS[1], ARGV[1])</span><br><span class="line">            end</span><br><span class="line">            -- 返回当前计数</span><br><span class="line">            <span class="built_in">return</span> cnt</span><br><span class="line">        ]]</span><br><span class="line">        -- 将 Lua 脚本存入共享内存，避免每次请求拼接 Lua 脚本字符串，提升性能</span><br><span class="line">        <span class="built_in">local</span> dict = ngx.shared.redis_scripts</span><br><span class="line">        -- Key：rate_limit_lua，Value：Lua 脚本</span><br><span class="line">        dict:<span class="built_in">set</span>(<span class="string">&quot;rate_limit_lua&quot;</span>, script)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    server &#123;</span><br><span class="line">        listen 8080;</span><br><span class="line"></span><br><span class="line">        location /api &#123;</span><br><span class="line">            <span class="comment"># 关键修改点：改为加载 Lua 文件</span></span><br><span class="line">            content_by_lua_file /Users/hanqf/Desktop/openresty/lua/rate_limit.lua;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p><code>rate_limit.lua</code></p></li></ul><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 加载 OpenResty 官方 Redis 客户端</span></span><br><span class="line"><span class="keyword">local</span> redis = <span class="built_in">require</span> <span class="string">&quot;resty.redis&quot;</span></span><br><span class="line"><span class="comment">-- 创建 Redis 对象</span></span><br><span class="line"><span class="keyword">local</span> red = redis:new()</span><br><span class="line"><span class="comment">-- Redis 连接信息</span></span><br><span class="line"><span class="keyword">local</span> redis_ip = <span class="string">&quot;127.0.0.1&quot;</span></span><br><span class="line"><span class="keyword">local</span> redis_port = <span class="number">6379</span></span><br><span class="line"><span class="keyword">local</span> redis_timeout = <span class="number">500</span></span><br><span class="line"><span class="keyword">local</span> redis_user = <span class="string">&quot;admin&quot;</span></span><br><span class="line"><span class="keyword">local</span> redis_pass = <span class="string">&quot;123456&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 关闭redis连接的工具方法，其实是放入连接池</span></span><br><span class="line"><span class="keyword">local</span> <span class="function"><span class="keyword">function</span> <span class="title">close_redis</span><span class="params">(red)</span></span></span><br><span class="line">    <span class="keyword">local</span> pool_max_idle_time = <span class="number">10000</span> <span class="comment">-- 连接的空闲时间，单位是毫秒</span></span><br><span class="line">    <span class="keyword">local</span> pool_size = <span class="number">100</span> <span class="comment">--连接池大小</span></span><br><span class="line">    <span class="comment">-- 将连接放回连接池，后续请求可复用</span></span><br><span class="line">    <span class="keyword">local</span> ok, err = red:set_keepalive(pool_max_idle_time, pool_size)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ok <span class="keyword">then</span></span><br><span class="line">        <span class="comment">-- 失败时记录错误日志</span></span><br><span class="line">        ngx.<span class="built_in">log</span>(ngx.ERR, <span class="string">&quot;放入redis连接池失败: &quot;</span>, err)</span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 超时（毫秒）</span></span><br><span class="line">red:set_timeout(redis_timeout)</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 建立 Redis 连接，若连接池有空闲连接会复用</span></span><br><span class="line"><span class="keyword">local</span> ok, err = red:connect(redis_ip, redis_port)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> ok <span class="keyword">then</span></span><br><span class="line">    ngx.<span class="built_in">log</span>(ngx.ERR, <span class="string">&quot;redis connect failed: &quot;</span>, err)</span><br><span class="line">    <span class="comment">-- 连接失败返回 500 错误码</span></span><br><span class="line">    <span class="keyword">return</span> ngx.<span class="built_in">exit</span>(<span class="number">500</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- ACL 认证（仅新连接）</span></span><br><span class="line"><span class="comment">-- 判断是否是新连接，避免重复认证浪费性能</span></span><br><span class="line"><span class="keyword">if</span> red:get_reused_times() == <span class="number">0</span> <span class="keyword">then</span></span><br><span class="line">    <span class="keyword">local</span> ok, err = red:auth(redis_user, redis_pass)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ok <span class="keyword">then</span></span><br><span class="line">        ngx.<span class="built_in">log</span>(ngx.ERR, <span class="string">&quot;redis auth failed: &quot;</span>, err)</span><br><span class="line">        <span class="comment">-- 认证失败返回 500 错误码</span></span><br><span class="line">        <span class="keyword">return</span> ngx.<span class="built_in">exit</span>(<span class="number">500</span>)</span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 客户端 IP</span></span><br><span class="line"><span class="keyword">local</span> client_ip = ngx.var.remote_addr <span class="keyword">or</span> <span class="string">&quot;unknown&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 当前分钟窗口</span></span><br><span class="line"><span class="keyword">local</span> now = ngx.<span class="built_in">time</span>() <span class="comment">-- 获取当前时间戳（秒）</span></span><br><span class="line"><span class="keyword">local</span> minute = <span class="built_in">math</span>.<span class="built_in">floor</span>(now / <span class="number">60</span>) <span class="comment">-- 转换为分钟窗口编号</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- Redis Key: 每个 IP 每分钟一个计数器</span></span><br><span class="line"><span class="keyword">local</span> key = <span class="string">&quot;rate:&quot;</span> .. client_ip .. <span class="string">&quot;:&quot;</span> .. minute</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 从共享字典读取 Lua 脚本</span></span><br><span class="line"><span class="keyword">local</span> dict = ngx.shared.redis_scripts</span><br><span class="line"><span class="keyword">local</span> script = dict:get(<span class="string">&quot;rate_limit_lua&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 执行 Redis Lua（原子）</span></span><br><span class="line"><span class="keyword">local</span> ttl = <span class="number">70</span></span><br><span class="line"><span class="keyword">local</span> cnt, err = red:eval(script, <span class="number">1</span>, key, ttl)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> cnt <span class="keyword">then</span></span><br><span class="line">    ngx.<span class="built_in">log</span>(ngx.ERR, <span class="string">&quot;redis eval failed: &quot;</span>, err)</span><br><span class="line">    <span class="keyword">return</span> ngx.<span class="built_in">exit</span>(<span class="number">500</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 限流判断</span></span><br><span class="line"><span class="keyword">local</span> limit = <span class="number">10</span></span><br><span class="line"><span class="keyword">if</span> cnt &gt; limit <span class="keyword">then</span></span><br><span class="line">    ngx.<span class="built_in">status</span> = <span class="number">429</span></span><br><span class="line">    ngx.say(<span class="string">&quot;Too Many Requests, limit=&quot;</span>, limit)</span><br><span class="line">    <span class="keyword">return</span> ngx.<span class="built_in">exit</span>(<span class="number">429</span>)  <span class="comment">-- 返回 HTTP 429（Too Many Requests）。</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 放回连接池</span></span><br><span class="line">close_redis(red)</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 正常返回</span></span><br><span class="line">ngx.say(<span class="string">&quot;OK, request count=&quot;</span>, cnt)</span><br></pre></td></tr></table></figure><h3 id="改用-access-by-lua-file">改用 <code>access_by_lua_file</code></h3><ul class="lvl-0"><li class="lvl-2"><p>前面的配置所有的响应都是由 lua 处理的，但是实际上，用户的请求被限流器放行后应该将请求下发到后端真实的服务，而不是通过 Lua 返回一个状态码或字符串。</p></li><li class="lvl-2"><p>生产级网关设计的标准做法: ✅ 凡是“鉴权/限流/风控/灰度/路由决策”逻辑，都应该放在 access 阶段，而不是 content 阶段。</p></li><li class="lvl-2"><p>修改 <code>redis-nginx.conf</code></p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 指定 Nginx Worker 进程数量，生产环境：等于 CPU 核心数，建议配置为 auto，自动适配</span></span><br><span class="line">worker_processes auto;</span><br><span class="line"><span class="comment"># 指定错误日志路径，所有错误都会写入该日志文件，包括 Lua ngx.log(ngx.ERR, ...)</span></span><br><span class="line">error_log logs/error.log;</span><br><span class="line"><span class="comment"># 定义事件模型参数</span></span><br><span class="line">events &#123;</span><br><span class="line">    <span class="comment"># 单个 Worker 最大并发连接数</span></span><br><span class="line">    worker_connections  1024;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">http &#123;</span><br><span class="line">    <span class="comment"># Lua 模块加载路径</span></span><br><span class="line">    <span class="comment"># 语法规则</span></span><br><span class="line">    <span class="comment">#  ?.lua 表示模块文件名占位符。</span></span><br><span class="line">    <span class="comment">#  ;; 表示 保留默认路径，否则会覆盖系统默认路径。</span></span><br><span class="line">    lua_package_path <span class="string">&quot;/usr/local/openresty/lualib/?.lua;;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># ----------------------------</span></span><br><span class="line">    <span class="comment"># Redis 原子限流 Lua 脚本</span></span><br><span class="line">    <span class="comment"># ----------------------------</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建一块 共享内存区，名称：redis_scripts，大小：1MB（Worker 之间共享内存）</span></span><br><span class="line">    <span class="comment"># 常用于：缓存 Lua 脚本、Token、计数器、配置信息</span></span><br><span class="line">    lua_shared_dict redis_scripts 1m;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Worker 初始化阶段加载 Lua，在 每个 Worker 启动时执行一次</span></span><br><span class="line">    <span class="comment"># 适合：加载配置、初始化缓存、预加载脚本、启动定时器</span></span><br><span class="line">    init_worker_by_lua_block &#123;</span><br><span class="line">        -- 将 Lua 脚本加载到 Nginx Worker 内存</span><br><span class="line">        <span class="built_in">local</span> script = [[</span><br><span class="line">            -- 对指定 Key 进行自增</span><br><span class="line">            <span class="built_in">local</span> cnt = redis.call(<span class="string">&quot;INCR&quot;</span>, KEYS[1])</span><br><span class="line">            -- 如果是第一次创建 Key</span><br><span class="line">            <span class="keyword">if</span> cnt == 1 <span class="keyword">then</span></span><br><span class="line">                -- 设置 Key 过期时间(秒)</span><br><span class="line">                redis.call(<span class="string">&quot;EXPIRE&quot;</span>, KEYS[1], ARGV[1])</span><br><span class="line">            end</span><br><span class="line">            -- 返回当前计数</span><br><span class="line">            <span class="built_in">return</span> cnt</span><br><span class="line">        ]]</span><br><span class="line">        -- 将 Lua 脚本存入共享内存，避免每次请求拼接 Lua 脚本字符串，提升性能</span><br><span class="line">        <span class="built_in">local</span> dict = ngx.shared.redis_scripts</span><br><span class="line">        -- Key：rate_limit_lua，Value：Lua 脚本</span><br><span class="line">        dict:<span class="built_in">set</span>(<span class="string">&quot;rate_limit_lua&quot;</span>, script)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 后端服务</span></span><br><span class="line">    upstream backend &#123;</span><br><span class="line">        server 127.0.0.1:9000;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    server &#123;</span><br><span class="line">        listen 8080;</span><br><span class="line"></span><br><span class="line">        location /api &#123;</span><br><span class="line">             <span class="comment"># 限流在 access 阶段执行</span></span><br><span class="line">            access_by_lua_file /Users/hanqf/Desktop/openresty/lua/rate_limit.lua;</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 通过后转发给后端服务</span></span><br><span class="line">            proxy_pass http://backend;</span><br><span class="line"></span><br><span class="line">            proxy_set_header Host <span class="variable">$host</span>;</span><br><span class="line">            proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line">            proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>✅ rate_limit.lua 修改</p></li></ul><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 将最后的</span></span><br><span class="line"><span class="comment">-- ngx.say(&quot;OK, request count=&quot;, cnt)</span></span><br><span class="line"><span class="comment">-- 替换为</span></span><br><span class="line"><span class="keyword">return</span> <span class="comment">-- 放行：什么都不做</span></span><br></pre></td></tr></table></figure><h2 id="后记">后记</h2><h3 id="应该如何编写滑动窗口限流脚本呢？">应该如何编写滑动窗口限流脚本呢？</h3><ul class="lvl-0"><li class="lvl-2"><p>目标：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">限制：最近 60 秒内 ≤ 10 次请求</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>核心思想：</p><ul class="lvl-2"><li class="lvl-5">每次请求记录当前时间戳</li><li class="lvl-5">删除窗口外的旧记录</li><li class="lvl-5">统计窗口内请求数量</li><li class="lvl-5">超过阈值拒绝</li></ul></li><li class="lvl-2"><p>Redis 数据结构使用：<code>ZSET</code></p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># KEY:rate:IP，SCORE:时间戳，MEMBER:任意唯一值，可以依旧使用时间戳，或者 时间戳_随机数，防止重复</span></span><br><span class="line">ZADD rate:192.168.1.10 1620000000 1620000000</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>具体的lua实现方法在 <a href="/2026/01/11/redis7-command-05-script-function/" title="Redis 命令详解：Scripting &#x2F; Functions 命令">Redis 命令详解：Scripting &#x2F; Functions 命令</a> 中有详细介绍。</p></li></ul>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/15/redis8-OpenResty-nginx/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/15/redis8-OpenResty-nginx/"/>
    <published>2026-01-15T13:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍如何通过 OpenResty 实现 Nginx + Lua 访问 Redis</li>
<li class="lvl-2">本文基于<code>redis-7.4.7</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2">OpenResty官网：<a href="https://openresty.org/">https://openresty.org/</a></li>
<li class="lvl-2"><a href="https://www.runoob.com/lua/lua-tutorial.html">Lua语法参考</a></li>
</ul>]]>
    </summary>
    <title>OpenResty -- Nginx + Lua 访问 Redis</title>
    <updated>2026-01-16T09:28:51.229Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 Redis8 新增的数据类型 Vector Set</li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li></ul><span id="more"></span><h2 id="Vector-Set-命令说明">Vector Set 命令说明</h2><h3 id="一、基础管理类">一、基础管理类</h3><table><thead><tr><th>命令</th><th>作用</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>VADD</strong></td><td>向向量集合中添加一个向量元素</td><td><code>VADD key VALUES num vector element</code></td><td><code>key</code>：向量集合名<br><code>vector</code>：向量数据（浮点数组）<br><code>element</code>：元素标识</td><td><code>VADD user:embeddings VALUES 3 0.1 0.2 0.3 u1</code></td></tr><tr><td><strong>VREM</strong></td><td>从向量集合中删除一个或多个元素</td><td><code>VREM key element</code></td><td><code>key</code>：向量集合<br><code>element</code>：元素标识</td><td><code>VREM user:embeddings u1</code></td></tr><tr><td><strong>VCARD</strong></td><td>返回向量集合中的元素数量</td><td><code>VCARD key</code></td><td><code>key</code>：向量集合</td><td><code>VCARD user:embeddings</code></td></tr><tr><td><strong>VDIM</strong></td><td>获取向量集合的维度</td><td><code>VDIM key</code></td><td><code>key</code>：向量集合</td><td><code>VDIM user:embeddings</code></td></tr><tr><td><strong>VINFO</strong></td><td>查看向量集合的元信息</td><td><code>VINFO key</code></td><td><code>key</code>：向量集合</td><td><code>VINFO user:embeddings</code></td></tr></tbody></table><h4 id="VADD-命令参数详解">VADD 命令参数详解</h4><ul class="lvl-0"><li class="lvl-2"><p>语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">VADD key</span><br><span class="line">     [REDUCE dim]</span><br><span class="line">     (FP32 | VALUES num) vector</span><br><span class="line">     element</span><br><span class="line">     [CAS]</span><br><span class="line">     [NOQUANT | Q8 | BIN]</span><br><span class="line">     [EF build-exploration-factor]</span><br><span class="line">     [SETATTR attributes]</span><br><span class="line">     [M numlinks]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>必需参数</p></li></ul><table><thead><tr><th>参数</th><th>参数类型</th><th>作用</th><th>语法 / 格式</th><th>说明</th></tr></thead><tbody><tr><td><code>key</code></td><td>String</td><td>向量集合名称</td><td><code>VADD key ...</code></td><td>Redis 中用于存储向量集合的键名。如果不存在会自动创建集合。</td></tr><tr><td><code>FP32 vector</code></td><td>Binary</td><td>直接传入二进制浮点向量</td><td><code>FP32 &lt;binary&gt;</code></td><td>以 <strong>小端序</strong>编码的 32 位浮点数组，适合客户端直接传二进制向量数据（高性能、低序列化开销）。</td></tr><tr><td><code>VALUES num vector</code></td><td>Numeric List</td><td>以数值方式传入向量</td><td><code>VALUES &lt;num&gt; &lt;v1&gt; &lt;v2&gt; ... &lt;vN&gt;</code></td><td><code>num</code> 为向量维度，后面必须跟随 <code>num</code> 个浮点数。适合在 CLI 或调试场景使用。</td></tr><tr><td><code>element</code></td><td>String</td><td>向量元素 ID</td><td><code>&lt;element&gt;</code></td><td>向量在集合中的唯一标识符，可理解为向量的主键或业务 ID。</td></tr></tbody></table><blockquote><p>重点说明：客户端实现时推荐直接传 FP32 二进制向量，避免浮点字符串解析开销。</p></blockquote><ul class="lvl-0"><li class="lvl-2"><p>可选参数</p></li></ul><table><thead><tr><th>参数</th><th>参数类型</th><th>作用</th><th>语法约束</th><th>说明</th></tr></thead><tbody><tr><td><code>REDUCE dim</code></td><td>Integer</td><td>向量降维</td><td><strong>必须紧跟在 key 后面</strong></td><td>使用随机投影算法将原始向量降维到 <code>dim</code> 维，降低存储与计算成本。<br>如果原始向量维度 &gt; dim → 是“降维”<br>如果原始向量维度 &lt; dim → 是“升维 / 填充映射”</td></tr><tr><td><code>CAS</code></td><td>Flag</td><td>异步构建索引</td><td>可放在 element 后</td><td>采用 Check-And-Set 风格：主线程快速返回，后台异步完成候选集构建，提高写入吞吐。</td></tr><tr><td><code>NOQUANT</code></td><td>Flag</td><td>禁用量化</td><td>仅首次创建集合时生效</td><td>不使用默认 int8 量化，保留原始浮点精度，内存占用更高。</td></tr><tr><td><code>Q8</code></td><td>Flag</td><td>启用 int8 量化（默认）</td><td>仅首次创建集合时生效</td><td>使用有符号 8 位量化，在精度、性能和内存之间取得平衡。</td></tr><tr><td><code>BIN</code></td><td>Flag</td><td>二进制量化</td><td>仅首次创建集合时生效</td><td>内存占用最小、速度最快，但相似度召回率可能下降。</td></tr><tr><td><code>EF build-exploration-factor</code></td><td>Integer</td><td>构图探索因子</td><td>可放在 element 后</td><td>HNSW 构图阶段的搜索宽度参数，默认约 200，值越大构图质量越高，但写入成本增加。</td></tr><tr><td><code>SETATTR attributes</code></td><td>JSON</td><td>设置向量属性</td><td>可放在 element 后</td><td>给元素绑定 JSON 属性，等价于调用 <code>VSETATTR</code>。</td></tr><tr><td><code>M numlinks</code></td><td>Integer</td><td>HNSW 最大连接数</td><td>可放在 element 后</td><td>每个节点允许的最大邻居数，默认 16。值越大索引更稠密，查询质量更高，但内存和写入成本上升。</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例：使用 VALUES</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">VADD my_vectors VALUES 3 0.1 1.2 0.5 image123</span><br><span class="line"><span class="comment"># 解释：</span></span><br><span class="line"><span class="comment">#    将向量 [0.1, 1.2, 0.5] 添加到键 my_vectors 对应的向量结构中。</span></span><br><span class="line"><span class="comment">#    元素名为 image123。</span></span><br><span class="line"><span class="comment">#    如果该键不存在，则创建新向量集。</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例：使用降维和量化选项</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">VADD my_vectors REDUCE 50 VALUES 5 0.02 0.89 0.77 0.56 0.33 pic_001 Q8 EF 300 M 32</span><br><span class="line"><span class="comment"># 解释：</span></span><br><span class="line"><span class="comment">#    对向量 REDUCE 到 50 维，这里原始数据维度小，实际上是升维</span></span><br><span class="line"><span class="comment">#    向量为 5 维浮点数值。</span></span><br><span class="line"><span class="comment">#    使用默认的带符号 8 位量化（Q8）。</span></span><br><span class="line"><span class="comment">#    设置 HNSW 图探索因子为 300。</span></span><br><span class="line"><span class="comment">#    每节点连接数上限为 32。</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取向量维度</span></span><br><span class="line">127.0.0.1:6379&gt; VDIM my_vectors</span><br><span class="line">(<span class="built_in">integer</span>) 50</span><br><span class="line"><span class="comment"># 获取向量信息，输出内容见下表中的说明</span></span><br><span class="line">127.0.0.1:6379&gt; VINFO my_vectors</span><br><span class="line"> 1) quant-type</span><br><span class="line"> 2) int8</span><br><span class="line"> 3) hnsw-m</span><br><span class="line"> 4) (<span class="built_in">integer</span>) 32</span><br><span class="line"> 5) vector-dim</span><br><span class="line"> 6) (<span class="built_in">integer</span>) 50</span><br><span class="line"> 7) projection-input-dim</span><br><span class="line"> 8) (<span class="built_in">integer</span>) 5</span><br><span class="line"> 9) size</span><br><span class="line">10) (<span class="built_in">integer</span>) 1</span><br><span class="line">11) max-level</span><br><span class="line">12) (<span class="built_in">integer</span>) 0</span><br><span class="line">13) attributes-count</span><br><span class="line">14) (<span class="built_in">integer</span>) 0</span><br><span class="line">15) vset-uid</span><br><span class="line">16) (<span class="built_in">integer</span>) 0</span><br><span class="line">17) hnsw-max-node-uid</span><br><span class="line">18) (<span class="built_in">integer</span>) 1</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>VINFO 输出字段说明表</p></li></ul><table><thead><tr><th>字段名</th><th>当前值</th><th>类型</th><th>含义说明</th><th>工程解读 / 使用价值</th></tr></thead><tbody><tr><td><code>quant-type</code></td><td><code>int8</code></td><td>String</td><td>向量量化方式</td><td>当前使用 <strong>Q8（8位有符号整数量化）</strong>，在精度、性能、内存之间平衡。</td></tr><tr><td><code>hnsw-m</code></td><td><code>32</code></td><td>Integer</td><td>HNSW 每节点最大连接数（M 参数）</td><td>图结构较稠密，召回率高，但内存占用和写入成本上升。</td></tr><tr><td><code>vector-dim</code></td><td><code>50</code></td><td>Integer</td><td><strong>索引内部真实使用的向量维度</strong></td><td>REDUCE 后的目标维度，VSIM / VRANGE 查询均基于此维度计算。</td></tr><tr><td><code>projection-input-dim</code></td><td><code>5</code></td><td>Integer</td><td>投影前输入向量的原始维度</td><td>说明你输入的是 <strong>5维向量</strong>，然后被映射到 50 维。</td></tr><tr><td><code>size</code></td><td><code>1</code></td><td>Integer</td><td>当前向量元素数量</td><td>集合中仅有 1 条向量数据（item42）。</td></tr><tr><td><code>max-level</code></td><td><code>0</code></td><td>Integer</td><td>HNSW 当前最大层级</td><td>数据量太小，仅构建了 0 层（尚未形成多层索引结构）。</td></tr><tr><td><code>attributes-count</code></td><td><code>0</code></td><td>Integer</td><td>当前已存储的属性对象数量</td><td>说明 SETATTR 没有成功写入或尚未设置属性。</td></tr><tr><td><code>vset-uid</code></td><td><code>0</code></td><td>Integer</td><td>向量集合内部唯一 ID</td><td>内部调试字段，对业务无直接影响。</td></tr><tr><td><code>hnsw-max-node-uid</code></td><td><code>1</code></td><td>Integer</td><td>HNSW 当前最大节点 ID</td><td>当前仅存在 1 个节点。</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例：添加带属性的向量</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">VADD my_vectors VALUES 4 0.15 0.26 0.47 0.88 item42 SETATTR <span class="string">&quot;&#123;\&quot;type\&quot;:\&quot;product\&quot;,\&quot;price\&quot;:29.99&#125;&quot;</span></span><br><span class="line"><span class="comment"># 解释：</span></span><br><span class="line">   <span class="comment"># 插入 4 维向量并给 item42 设置 JSON 属性 type 和 price。</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取向量属性</span></span><br><span class="line">127.0.0.1:6379&gt; VGETATTR my_vectors item42</span><br><span class="line"><span class="string">&quot;&#123;\&quot;type\&quot;:\&quot;product\&quot;,\&quot;price\&quot;:29.99&#125;&quot;</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例：插入多条数据</p></li></ul><blockquote><p>只有<code>维度</code>、<code>quant-type</code> 和 <code>hnsw-m</code>都相同的数据才会被加入同一个 VectorSet<br>VectorSet 在第一次创建时就固定了相关的属性</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; VADD my_vectors VALUES 4 0.15 0.26 0.47 0.88 item42 SETATTR <span class="string">&quot;&#123;\&quot;type\&quot;:\&quot;product\&quot;,\&quot;price\&quot;:29.99&#125;&quot;</span></span><br><span class="line">(<span class="built_in">integer</span>) 1</span><br><span class="line">127.0.0.1:6379&gt; VADD my_vectors VALUES 4 0.25 0.20 0.57 0.98 item43</span><br><span class="line">127.0.0.1:6379&gt; VADD my_vectors VALUES 4 0.25 0.20 0.57 0.98 item44 Q8 EF 300 M 16</span><br><span class="line">(<span class="built_in">integer</span>) 1</span><br><span class="line">127.0.0.1:6379&gt; VADD my_vectors VALUES 4 0.25 0.20 0.57 0.98 item44 Q8 EF 300 M 16</span><br><span class="line">(<span class="built_in">integer</span>) 0  <span class="comment"># 添加重复元素标识，返回 0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取向量数量</span></span><br><span class="line">127.0.0.1:6379&gt; VCARD my_vectors</span><br><span class="line">(<span class="built_in">integer</span>) 3</span><br><span class="line"><span class="comment"># 获取向量信息</span></span><br><span class="line">127.0.0.1:6379&gt; VINFO my_vectors</span><br><span class="line"> 1) quant-type</span><br><span class="line"> 2) int8</span><br><span class="line"> 3) hnsw-m</span><br><span class="line"> 4) (<span class="built_in">integer</span>) 16</span><br><span class="line"> 5) vector-dim</span><br><span class="line"> 6) (<span class="built_in">integer</span>) 4</span><br><span class="line"> 7) projection-input-dim</span><br><span class="line"> 8) (<span class="built_in">integer</span>) 0</span><br><span class="line"> 9) size</span><br><span class="line">10) (<span class="built_in">integer</span>) 2</span><br><span class="line">11) max-level</span><br><span class="line">12) (<span class="built_in">integer</span>) 0</span><br><span class="line">13) attributes-count</span><br><span class="line">14) (<span class="built_in">integer</span>) 2</span><br><span class="line">15) vset-uid</span><br><span class="line">16) (<span class="built_in">integer</span>) 2</span><br><span class="line">17) hnsw-max-node-uid</span><br><span class="line">18) (<span class="built_in">integer</span>) 2</span><br></pre></td></tr></table></figure><h3 id="二、向量元素访问与判断">二、向量元素访问与判断</h3><table><thead><tr><th>命令</th><th>作用</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>VISMEMBER</strong></td><td>判断元素是否存在于向量集合中</td><td><code>VISMEMBER key element</code></td><td><code>key</code>：向量集合<br><code>element</code>：元素标识</td><td><code>VISMEMBER user:embeddings u1</code></td></tr><tr><td><strong>VRANDMEMBER</strong></td><td>随机返回一个或多个向量元素</td><td><code>VRANDMEMBER key [count]</code></td><td><code>count</code>：返回数量，默认为1（可选）</td><td><code>VRANDMEMBER user:embeddings 2</code></td></tr><tr><td><strong>VRANGE</strong></td><td>返回向量集合中指定范围的元素（按内部顺序）</td><td><code>VRANGE key start stop [count]</code></td><td><code>start</code> / <code>stop</code>：说明见下表</td><td><code>VRANGE user:embeddings - + -1</code></td></tr></tbody></table><h4 id="VRANGE">VRANGE</h4><ul class="lvl-0"><li class="lvl-2"><p>参数说明</p></li></ul><table><thead><tr><th>参数</th><th>类型</th><th>是否必须</th><th>描述</th></tr></thead><tbody><tr><td><code>key</code></td><td>key</td><td>是</td><td>向量集合的键名</td></tr><tr><td><code>start</code></td><td>string</td><td>是</td><td>范围起始元素（字典序）<br>可用：<code>[</code> 前缀表示包含，<code>(</code> 前缀表示排除，<code>-</code> 表示最小元素</td></tr><tr><td><code>end</code></td><td>string</td><td>是</td><td>范围结束元素（字典序）<br>可用：<code>[</code> 前缀表示包含，<code>(</code> 前缀表示排除，<code>+</code> 表示最大元素</td></tr><tr><td><code>count</code></td><td>integer</td><td>否</td><td>最多返回元素数量<br>若为负数，则返回范围内所有匹配元素（注意可能阻塞）</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>参数举例说明</p></li></ul><table><thead><tr><th>参数值</th><th>说明</th></tr></thead><tbody><tr><td><code>-</code></td><td>最小元素（等同 open-ended 从头开始）</td></tr><tr><td><code>+</code></td><td>最大元素（等同 open-ended 到尾结束）</td></tr><tr><td><code>[Redis</code></td><td>从字典序 ≥ <code>&quot;Redis&quot;</code> 的元素开始（包含 <code>&quot;Redis&quot;</code>）</td></tr><tr><td><code>(a7</code></td><td>从字典序 &gt; <code>&quot;a7&quot;</code> 的元素开始（不包含 <code>&quot;a7&quot;</code>）</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例 1：返回从 “Redis”（包含）开始的前 10 个元素</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">VRANGE mykey [Redis + 10</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 2：分段迭代所有元素</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">VRANGE mykey - + 10</span><br><span class="line"><span class="comment"># 输出：从最小到最大按每次 10 个元素迭代遍历（客户端可用结果接续下一段）。</span></span><br><span class="line"><span class="comment"># 比如输出最后一个元素是 &quot;Redis&quot;，则下一次迭代从 &quot;Redis&quot; 后面开始。</span></span><br><span class="line">VRANGE mykey (Redis + 10</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 3：返回指定范围所有元素（无数量上限）</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">VRANGE mykey - + -1</span><br><span class="line"><span class="comment"># 输出：返回整个集合所有元素。注意：若集合很大可能阻塞 Redis。</span></span><br></pre></td></tr></table></figure><h3 id="三、向量属性（Metadata-Attributes）管理">三、向量属性（Metadata / Attributes）管理</h3><ul class="lvl-0"><li class="lvl-2"><p>用于给向量元素绑定结构化元数据（如标签、业务字段）</p></li></ul><table><thead><tr><th>命令</th><th>作用</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>VSETATTR</strong></td><td>为向量元素设置属性</td><td><code>VSETATTR key element &quot;{ JSON obj }&quot;</code></td><td>JSON obj</td><td>设置元数据：<code>VSETATTR key element &quot;{\&quot;type\&quot;: \&quot;fruit\&quot;, \&quot;color\&quot;: \&quot;red\&quot;}&quot;</code> <br> 清除元数据：<code>VSETATTR key element &quot;&quot;</code></td></tr><tr><td><strong>VGETATTR</strong></td><td>获取向量元素的属性</td><td><code>VGETATTR key element</code></td><td></td><td><code>VGETATTR user:embeddings u1</code></td></tr></tbody></table><h3 id="四、向量相似度与检索类（核心）">四、向量相似度与检索类（核心）</h3><table><thead><tr><th>命令</th><th>作用</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>VSIM</strong></td><td>基于向量相似度进行近邻搜索（KNN）</td><td>详见下面的说明</td><td></td><td><code>VSIM word_embeddings ELE apple</code></td></tr><tr><td><strong>VLINKS</strong></td><td>查看向量之间的近邻链接关系（图结构）</td><td><code>VLINKS key element [WITHSCORES]</code></td><td><code>element</code>：向量元素 ID</td><td><code>VLINKS user:embeddings u1</code></td></tr></tbody></table><h4 id="VSIM">VSIM</h4><ul class="lvl-0"><li class="lvl-2"><p>VSIM 用于在 向量集合（vector set） 中执行 相似度搜索，返回与指定参考向量或已存在元素向量 最相似 的元素列表。可以进行近似（默认 HNSW 索引）或精确（TRUTH）查询。</p></li><li class="lvl-2"><p>语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">VSIM key (ELE | FP32 | VALUES num) (vector | element)</span><br><span class="line">   [WITHSCORES] [WITHATTRIBS] [COUNT num]</span><br><span class="line">   [EPSILON delta] [EF search-exploration-factor] [FILTER expression] [FILTER-EF max-filtering-effort]</span><br><span class="line">   [TRUTH] [NOTHREAD]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>参数说明</p></li></ul><table><thead><tr><th>参数 / 选项</th><th>类型</th><th>是否必需</th><th>描述</th></tr></thead><tbody><tr><td><code>key</code></td><td>key</td><td>是</td><td>向量集合的键名</td></tr><tr><td><code>ELE</code></td><td>literal with element name</td><td>是（三者不能同时出现）</td><td>使用集合中已有的元素名称作为查询向量</td></tr><tr><td><code>FP32</code></td><td>literal</td><td>是（三者不能同时出现）</td><td>使用二进制 float32 格式提供查询向量</td></tr><tr><td><code>VALUES num</code></td><td>literal with integer</td><td>是（三者不能同时出现）</td><td>使用后续 <code>num</code> 个字符串 float 值提供查询向量</td></tr><tr><td><code>vector</code> / <code>element</code></td><td>vector values or element name</td><td>是</td><td>查询向量本身（或元素名）</td></tr><tr><td><code>WITHSCORES</code></td><td>flag</td><td>否</td><td>返回每个匹配项的相似度分数</td></tr><tr><td><code>WITHATTRIBS</code></td><td>flag</td><td>否</td><td>返回每个匹配项关联的 JSON 属性（如有）</td></tr><tr><td><code>COUNT num</code></td><td>integer</td><td>否</td><td>限制返回的相似项数量</td></tr><tr><td><code>EPSILON delta</code></td><td>float</td><td>否</td><td>过滤出距离不大于 delta 的结果（score ≥ 1−delta）</td></tr><tr><td><code>EF search-exploration-factor</code></td><td>integer</td><td>否</td><td>调整 HNSW 搜索探索因子（值越高搜索更深、更准确但更慢）。</td></tr><tr><td><code>FILTER expression</code></td><td>string</td><td>否</td><td>对属性进行过滤表达式约束（仅返回满足的元素）</td></tr><tr><td><code>FILTER-EF max-filtering-effort</code></td><td>integer</td><td>否</td><td>限制 FILTER 表达式的评估尝试次数</td></tr><tr><td><code>TRUTH</code></td><td>flag</td><td>否</td><td>强制精确线性扫描（不使用图索引）</td></tr><tr><td><code>NOTHREAD</code></td><td>flag</td><td>否</td><td>在主线程执行搜索而非后台线程</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>参数/选项说明细节</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">输入向量方式</span><br><span class="line">   ELE elementName：以集合中已有元素的向量作为查询参考。</span><br><span class="line">   FP32：提供 little-endian 编码的二进制浮点向量。</span><br><span class="line">   VALUES num …：直接以 <span class="built_in">float</span> 字符串序列作为查询向量，其后跟 num 个浮点值。</span><br><span class="line"></span><br><span class="line">输出控制</span><br><span class="line">   WITHSCORES：输出时每个结果后附带 similarity 分数（1 = 完全相同, 0 = 完全不同）。</span><br><span class="line">   WITHATTRIBS：若集合元素有 JSON 属性，则返回对应属性值。</span><br><span class="line">   COUNT num：指定最多返回匹配数量。</span><br><span class="line"></span><br><span class="line">搜索行为控制</span><br><span class="line">   EPSILON delta：仅返回与查询向量 distance &lt;= delta 的元素。</span><br><span class="line">   EF search-exploration-factor：HNSW 图搜索的探索因子（值越高搜索更深、更准确但更慢）。</span><br><span class="line">   FILTER expression：基于元素属性进行过滤，例如按年份、分类等。</span><br><span class="line">   FILTER-EF max-filtering-effort：限制 FILTER 表达式的评估尝试次数。</span><br><span class="line">   TRUTH：执行精确线性扫描而非近似图索引。</span><br><span class="line">   NOTHREAD：禁用后台线程，在主线程执行搜索（可能阻塞）。</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 1 — 基于现有元素查询（取最相似前 10 个）</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">VSIM my_vectors ELE apple WITHSCORES COUNT 10</span><br><span class="line"><span class="comment"># 返回元素 &quot;apple&quot; 最相似的前 10 个元素及其相似度分数。&quot;apple&quot; 必须在集合中存在。</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 2 — 基于明确定义的向量查询</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">VSIM my_vectors VALUES 3 0.12 0.34 0.56 WITHSCORES COUNT 5</span><br><span class="line"><span class="comment"># 使用向量 [0.12, 0.34, 0.56] 作为查询参考，返回 最相似 的前 5 个结果及分数。</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 3 — 精确线性扫描用于基准或严格匹配</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">VSIM my_vectors ELE targetElement TRUTH WITHSCORES</span><br><span class="line"><span class="comment"># 绕过 HNSW 索引，执行全量扫描以获取精确的相似度结果。</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 4 — 使用属性过滤</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 假设集合元素有 JSON 属性如 &#123; &quot;price&quot;: 20.99 &#125;</span></span><br><span class="line">VSIM my_vectors VALUES 4 0.15 0.26 0.47 0.88 FILTER <span class="string">&#x27;.price &gt;= 20&#x27;</span> COUNT 10</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 5 — 使用 EPSILON 控制相似度范围</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">VSIM my_vectors ELE queryElem EPSILON 0.2 COUNT 50</span><br><span class="line"><span class="comment"># 仅返回相似度 ≥ 0.8（即距离 ≤ 0.2）且最多 50 个的匹配项。</span></span><br></pre></td></tr></table></figure><h3 id="五、向量嵌入（Embedding）相关">五、向量嵌入（Embedding）相关</h3><table><thead><tr><th>命令</th><th>作用</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>VEMB</strong></td><td>从 向量集合（vector set） 中检索给定元素的向量（embedding）</td><td><code>VEMB key element [RAW]</code></td><td><code>key</code>: 向量集合的键名（即 vector set 名称）。<br><code>element</code>：目标元素名称，其向量将被检索。<br> <code>RAW</code>: 如果指定，返回 原始内部表示（量化信息 + 载体数据），而不是简单的反归一化向量。</td><td><code>VEMB my_vectors item42</code></td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例 1 — 获取向量（embedding）</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 添加元素</span></span><br><span class="line">VADD my_vectors VALUES 4 0.15 0.26 0.47 0.88 item42</span><br><span class="line"><span class="comment"># 查询元素的向量</span></span><br><span class="line">VEMB my_vectors item42</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) <span class="string">&quot;0.15244093537330627&quot;</span></span><br><span class="line">2) <span class="string">&quot;0.2633070647716522&quot;</span></span><br><span class="line">3) <span class="string">&quot;0.47118112444877625&quot;</span></span><br><span class="line">4) <span class="string">&quot;0.8799999952316284&quot;</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 2 — 使用 RAW 选项</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">VEMB my_vectors item42 RAW</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) int8              <span class="comment"># quantization type，字符串：如 fp32、bin、q8</span></span><br><span class="line">2) <span class="string">&quot;\x16&amp;D\x7f&quot;</span>      <span class="comment"># raw data blob ，二进制向量原始数据</span></span><br><span class="line">3) <span class="string">&quot;1.041825294494629&quot;</span>  <span class="comment"># L2 norm，归一化前向量的 L2 范数</span></span><br><span class="line">4) <span class="string">&quot;0.844671368598938&quot;</span>  <span class="comment"># quantization range (仅 q8 时)，量化范围，用于恢复真实值</span></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/14/redis8-datatype-vector/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/14/redis8-datatype-vector/"/>
    <published>2026-01-14T13:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 Redis8 新增的数据类型 Vector Set</li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
</ul>]]>
    </summary>
    <title>Redis8 新增的数据类型 -- Vector Set</title>
    <updated>2026-01-14T10:07:56.048Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 Redis 扩展模块 – RediSearch 操作 向量检索 的方法(<code>非 Redis8 中新增的向量功能</code>)</li><li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li><li class="lvl-2">RediSearch 的安装参见 <a href="/2025/12/26/redis7-module-RediSearch/" title="Redis 扩展模块 -- RediSearch 的安装方法">Redis 扩展模块 -- RediSearch 的安装方法</a></li></ul><span id="more"></span><h2 id="向量是什么？">向量是什么？</h2><ul class="lvl-0"><li class="lvl-2"><p>在现代人工智能系统中，文本、图片、音频、视频等非结构化数据并不能直接被计算机理解和比较。</p></li><li class="lvl-2"><p>向量（Vector）是将这些复杂信息转换为数学可计算形式的核心载体，也是大模型检索、推荐、搜索、RAG、语义理解等能力的基础。</p></li></ul><h3 id="数学意义上的向量">数学意义上的向量</h3><ul class="lvl-0"><li class="lvl-2"><p>在数学中，向量是一个有序的数值集合，例如：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[0.12, -0.87, 1.45, 0.003, ...]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>每一个数字称为一个维度（dimension），整体可以看作是在高维空间中的一个点。</p></li><li class="lvl-2"><p>如果一个向量有 1024 个数字，则它位于一个 1024 维空间中。</p></li></ul><h3 id="向量在-AI-中的含义">向量在 AI 中的含义</h3><ul class="lvl-0"><li class="lvl-2"><p>在 AI 领域，向量并不是随意的数字，而是 对某个对象语义特征的数值化表达，例如：</p></li></ul><table><thead><tr><th>对象</th><th>向量表达含义</th></tr></thead><tbody><tr><td>一句话</td><td>语义含义、主题、情感、上下文关系</td></tr><tr><td>一张图片</td><td>纹理、颜色、形状、语义对象</td></tr><tr><td>一段音频</td><td>音色、节奏、频谱特征</td></tr><tr><td>一个用户</td><td>兴趣偏好、行为模式</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>模型通过学习大量数据，将“相似的语义”映射到“空间中距离接近的向量”。</p></li></ul><h3 id="向量为什么可以比较相似度？">向量为什么可以比较相似度？</h3><ul class="lvl-0"><li class="lvl-2"><p>向量之间可以通过距离函数进行比较，常见距离指标：</p></li></ul><table><thead><tr><th>指标</th><th>含义</th><th>适用场景</th></tr></thead><tbody><tr><td>Cosine Similarity</td><td>余弦相似度</td><td>文本语义、Embedding</td></tr><tr><td>Euclidean Distance</td><td>欧式距离</td><td>图像、空间特征</td></tr><tr><td>Dot Product</td><td>点积</td><td>推荐系统</td></tr><tr><td>L2 / L1</td><td>范数距离</td><td>通用计算</td></tr></tbody></table><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">向量A ≈ 向量B  → 表示语义相似</span><br><span class="line">向量A 距离 向量C 很远 → 表示语义差异大</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>向量相似度计算代码示例</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.langchain4j.embeding;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.example.langchain4j.ModelUtil;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.community.model.dashscope.QwenEmbeddingModel;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.data.embedding.Embedding;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.model.embedding.EmbeddingModel;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.model.output.Response;</span><br><span class="line"><span class="keyword">import</span> org.apache.lucene.util.VectorUtil;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 向量demo</span></span><br><span class="line"><span class="comment"> * Created by hanqf on 2026/1/13 09:51.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VectorDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">EmbeddingModel</span> <span class="variable">embeddingModel</span> <span class="operator">=</span> QwenEmbeddingModel.builder()</span><br><span class="line">                .apiKey(ModelUtil.DASHSCOPE_API_KEY)</span><br><span class="line">                .modelName(<span class="string">&quot;text-embedding-v3&quot;</span>)</span><br><span class="line">                .build();</span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">question</span> <span class="operator">=</span> <span class="string">&quot;Redis 7 如何开启向量检索功能？&quot;</span>;</span><br><span class="line"></span><br><span class="line">        String[] texts = &#123;</span><br><span class="line">                <span class="string">&quot;Redis 需要安装 RediSearch 模块才能支持向量索引和相似度搜索。&quot;</span>,</span><br><span class="line">                <span class="string">&quot;Redis 7.4 可以通过 MODULE LOAD 加载向量模块。&quot;</span>,</span><br><span class="line">                <span class="string">&quot;MySQL 如何建立索引提升查询性能？&quot;</span>,</span><br><span class="line">                <span class="string">&quot;Spring Boot 如何配置 RedisTemplate 连接集群？&quot;</span></span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        Response&lt;Embedding&gt; embed = embeddingModel.embed(question);</span><br><span class="line">        <span class="type">float</span>[] q_vector = embed.content().vector();</span><br><span class="line">        System.out.println(embed.content().vectorAsList());</span><br><span class="line">        <span class="comment">// 获取向量的维度</span></span><br><span class="line">        System.out.println(embed.content().vector().length);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (String s : texts) &#123;</span><br><span class="line">            Response&lt;Embedding&gt; embedded = embeddingModel.embed(s);</span><br><span class="line">            <span class="type">float</span>[] vector = embedded.content().vector();</span><br><span class="line">            <span class="comment">// 计算余弦相似度, 范围[0,1],越大表示越相似</span></span><br><span class="line">            <span class="keyword">final</span> <span class="type">float</span> <span class="variable">cosine</span> <span class="operator">=</span> VectorUtil.cosine(q_vector, vector);</span><br><span class="line">            System.out.println(cosine);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="向量模型（Embedding-Model）">向量模型（Embedding Model）</h2><ul class="lvl-0"><li class="lvl-2"><p>向量模型非常多，常用的向量模型可以参考<a href="https://docs.langchain4j.dev/category/embedding-models">向量模型</a></p></li><li class="lvl-2"><p>向量模型，也称为 Embedding 模型，是一种：把原始数据映射为固定长度向量的深度学习模型。</p></li><li class="lvl-2"><p>输入可以是：文本、图片、音频、视频、多模态组合</p></li><li class="lvl-2"><p>输出是：<code>float[] embedding</code></p></li></ul><h3 id="向量维度的意义">向量维度的意义</h3><ul class="lvl-0"><li class="lvl-2"><p>常见维度范围：</p></li></ul><table><thead><tr><th>模型类型</th><th>向量维度</th></tr></thead><tbody><tr><td>Mini Embedding</td><td>256 ~ 384</td></tr><tr><td>通用文本模型</td><td>512 ~ 1024</td></tr><tr><td>高精度模型</td><td>1536 ~ 4096</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>维度越高：表达能力越强、存储和计算成本越高、检索延迟越大</p></li></ul><h3 id="向量模型的典型应用">向量模型的典型应用</h3><table><thead><tr><th>场景</th><th>作用</th></tr></thead><tbody><tr><td>语义搜索</td><td>搜索语义相关内容</td></tr><tr><td>RAG</td><td>文档召回</td></tr><tr><td>推荐系统</td><td>用户兴趣匹配</td></tr><tr><td>聚类分析</td><td>自动分类</td></tr><tr><td>去重</td><td>内容相似性检测</td></tr><tr><td>多模态检索</td><td>文搜图 / 图搜文</td></tr></tbody></table><h2 id="向量数据库">向量数据库</h2><h3 id="为什么需要向量数据库">为什么需要向量数据库?</h3><ul class="lvl-0"><li class="lvl-2"><p>普通数据库擅长：等值查询、范围查询、排序、聚合，但对：“在百万向量中找最相似的10个” 效率极低。</p></li><li class="lvl-2"><p>向量数据库专门解决：高维向量的相似度检索问题（ANN Search）。</p></li><li class="lvl-2"><p>向量数据库的能力</p></li></ul><table><thead><tr><th>能力</th><th>说明</th></tr></thead><tbody><tr><td>向量存储</td><td>高维 float 向量</td></tr><tr><td>相似度索引</td><td>HNSW、IVF、PQ</td></tr><tr><td>KNN 查询</td><td>TopK 最近邻</td></tr><tr><td>混合检索</td><td>向量 + 结构化过滤</td></tr><tr><td>持久化</td><td>磁盘存储</td></tr><tr><td>分布式</td><td>横向扩展</td></tr><tr><td>实时写入</td><td>在线更新</td></tr></tbody></table><h3 id="常见向量索引算法">常见向量索引算法</h3><table><thead><tr><th>算法</th><th>全称</th><th>核心原理</th><th>查询性能</th><th>检索精度</th><th>内存占用</th><th>构建成本</th><th>适用场景</th><th>典型系统</th></tr></thead><tbody><tr><td><strong>HNSW</strong></td><td>Hierarchical Navigable Small World</td><td>分层小世界图结构，通过多层图实现快速导航</td><td>极快（毫秒级）</td><td>极高（接近精确搜索）</td><td>较高</td><td>中等</td><td>实时检索、高 QPS、低延迟系统</td><td>Redis, Milvus, Faiss</td></tr><tr><td><strong>IVF</strong></td><td>Inverted File Index</td><td>先聚类分桶，再在桶内进行向量搜索</td><td>快</td><td>中等~高（可调）</td><td>中等~低</td><td>较高（需要训练聚类）</td><td>大规模离线检索、成本敏感场景</td><td>Faiss, Milvus</td></tr><tr><td><strong>PQ</strong></td><td>Product Quantization</td><td>向量分块压缩，用码本近似原始向量</td><td>快</td><td>较低（有量化误差）</td><td>极低</td><td>高（训练码本）</td><td>海量数据、内存受限系统</td><td>Faiss, Milvus</td></tr></tbody></table><h3 id="主流向量数据库">主流向量数据库</h3><ul class="lvl-0"><li class="lvl-2"><p>向量数据库非常多，常用的向量数据库可以参考<a href="https://docs.langchain4j.dev/category/embedding-stores">向量数据库</a></p></li><li class="lvl-2"><p>这里只列出一些主流的向量数据库</p></li></ul><table><thead><tr><th>产品</th><th>特点</th></tr></thead><tbody><tr><td>Milvus</td><td>专业向量数据库</td></tr><tr><td>Qdrant</td><td>Rust 高性能</td></tr><tr><td>Weaviate</td><td>Graph + Vector</td></tr><tr><td>Elasticsearch</td><td>向量扩展</td></tr><tr><td>Redis</td><td>内存级向量</td></tr><tr><td>PGVector</td><td>PostgreSQL 插件</td></tr><tr><td>Faiss</td><td>本地库</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>向量数据库在 RAG 架构中的位置</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">用户问题</span><br><span class="line">   ↓</span><br><span class="line">Embedding 模型 → 向量</span><br><span class="line">   ↓</span><br><span class="line">向量数据库 KNN 查询</span><br><span class="line">   ↓</span><br><span class="line">召回相关文档片段</span><br><span class="line">   ↓</span><br><span class="line">LLM 生成答案</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>向量数据库的能力决定了如下指标：</p></li></ul><table><thead><tr><th>指标</th><th>含义</th></tr></thead><tbody><tr><td>检索精度</td><td>检索结果质量，召回准确率</td></tr><tr><td>检索延迟</td><td>检索耗时</td></tr><tr><td>存储空间</td><td>索引大小</td></tr><tr><td>索引构建时间</td><td>索引构建耗时</td></tr><tr><td>系统吞吐能力</td><td>系统处理能力</td></tr></tbody></table><h2 id="RediSearch-创建向量索引">RediSearch 创建向量索引</h2><ul class="lvl-0"><li class="lvl-2"><p>前提是安装了 RedisJSON 和 RediSearch 模块，具体安装方法参见 <a href="/2025/12/24/redis7-module-RedisJSON/" title="Redis 扩展模块 -- RedisJSON 的安装方法">Redis 扩展模块 -- RedisJSON 的安装方法</a> 和 <a href="/2025/12/26/redis7-module-RediSearch/" title="Redis 扩展模块 -- RediSearch 的安装方法">Redis 扩展模块 -- RediSearch 的安装方法</a></p></li><li class="lvl-2"><p>在 Redis7 中，向量数据存储在 JSON 数据类型中，然后通过 RediSearch 对其创建索引，实现向量的搜索功能。</p></li><li class="lvl-2"><p>示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建 JSON 数据 示例</span></span><br><span class="line">JSON.SET vector:1 $ <span class="string">&#x27;&#123;&quot;vector&quot;:[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0], &quot;text&quot;:&quot;This is a test vector&quot;&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line">JSON.SET vector:2 $ <span class="string">&#x27;&#123;&quot;vector&quot;:[0.2,0.1,0.4,0.3,0.6,0.5,0.8,0.7,1.0,0.9], &quot;text&quot;:&quot;Another vector&quot;&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建索引示例</span></span><br><span class="line">FT.CREATE vector_index ON JSON</span><br><span class="line">PREFIX 1 vector:</span><br><span class="line">SCHEMA</span><br><span class="line">  $.text AS text TEXT</span><br><span class="line">  $.vector AS vector VECTOR</span><br><span class="line">    HNSW 10</span><br><span class="line">    TYPE FLOAT32</span><br><span class="line">    DIM 10</span><br><span class="line">    DISTANCE_METRIC COSINE</span><br><span class="line">    M 16</span><br><span class="line">    EF_CONSTRUCTION 200</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>创建索引的参数说明</p></li></ul><table><thead><tr><th>参数</th><th>含义</th></tr></thead><tbody><tr><td>VECTOR</td><td>向量字段</td></tr><tr><td>HNSW</td><td>一种索引算法，可以把查询复杂度降低到近似 O(log N)，牺牲极少精度换取极高性能。</td></tr><tr><td>10</td><td>表示后面紧跟的 10 个参数是用于创建向量索引的参数</td></tr><tr><td>TYPE FLOAT32</td><td>向量数据类型</td></tr><tr><td>DIM 10</td><td>向量维度，一般由向量模型的输出维度决定，如：1024</td></tr><tr><td>COSINE</td><td>相似度度量</td></tr><tr><td>M 16</td><td>每个节点在 HNSW 图中允许建立的最大邻居连接数 <br>每个向量最多和多少个其它向量建立边关系<br>在插入一个新向量时：<br>算法会搜索一批候选邻居,<br>从候选集合中选择最多 M 个最合适的邻居建立双向连接</td></tr><tr><td>EF_CONSTRUCTION 200</td><td>建索引时搜索宽度<br>在构建索引（插入向量）时，用于搜索候选邻居的动态候选队列大小</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p><code>M</code>的大小影响的维度</p></li></ul><table><thead><tr><th>维度</th><th>M 增大</th><th>M 减小</th></tr></thead><tbody><tr><td>查询召回率</td><td>↑ 提升</td><td>↓ 降低</td></tr><tr><td>查询延迟</td><td>↑ 略增</td><td>↓ 更快</td></tr><tr><td>内存占用</td><td>↑ 明显增加</td><td>↓ 减少</td></tr><tr><td>构建时间</td><td>↑ 增加</td><td>↓ 更快</td></tr><tr><td>图稳定性</td><td>↑ 更稳健</td><td>↓ 容易断裂</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p><code>M</code>常见取值经验</p></li></ul><table><thead><tr><th>场景</th><th>推荐 M</th></tr></thead><tbody><tr><td>小规模数据（&lt;100万）</td><td>16 ~ 32</td></tr><tr><td>高召回要求（搜索、推荐）</td><td>32 ~ 64</td></tr><tr><td>内存敏感</td><td>8 ~ 16</td></tr><tr><td>向量维度很高（&gt;1024）</td><td>24 ~ 48</td></tr></tbody></table><blockquote><p>Redis 官方示例一般使用 M = 16，属于平衡型配置。</p></blockquote><ul class="lvl-0"><li class="lvl-2"><p><code>EF_CONSTRUCTION</code> 影响维度</p></li></ul><table><thead><tr><th>维度</th><th>EF_CONSTRUCTION 增大</th><th>EF_CONSTRUCTION 减小</th></tr></thead><tbody><tr><td>索引质量</td><td>↑ 提升</td><td>↓ 下降</td></tr><tr><td>构建时间</td><td>↑ 明显变慢</td><td>↓ 更快</td></tr><tr><td>构建 CPU</td><td>↑ 占用增加</td><td>↓ 减少</td></tr><tr><td>查询性能</td><td>↑ 更稳定</td><td>↓ 容易退化</td></tr><tr><td>内存</td><td>≈ 基本不变</td><td>≈</td></tr></tbody></table><blockquote><p>⚠️ 注意：EF_CONSTRUCTION 只影响建索引阶段，不影响查询阶段的实时性能。</p></blockquote><ul class="lvl-0"><li class="lvl-2"><p><code>EF_CONSTRUCTION</code> 常见取值经验</p></li></ul><blockquote><p>经验公式：<code>EF_CONSTRUCTION ≥ 2 × M</code></p></blockquote><table><thead><tr><th>M</th><th>推荐 EF_CONSTRUCTION</th></tr></thead><tbody><tr><td>16</td><td>100 ~ 200</td></tr><tr><td>32</td><td>200 ~ 400</td></tr><tr><td>48</td><td>300 ~ 600</td></tr></tbody></table><h2 id="搜索向量">搜索向量</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 搜索示例: 🔍 查询一个向量，找 Top 2 个最相似向量，并返回 text 和相似度距离 score。</span></span><br><span class="line">FT.SEARCH vector_index</span><br><span class="line"><span class="string">&quot;*=&gt;[KNN 2 @vector <span class="variable">$vec</span> AS score]&quot;</span></span><br><span class="line">PARAMS 2 vec <span class="string">&quot;\xCD\xCC\xCC\x3D\xCD\xCC\x4C\x3E\x9A\x99\x99\x3E\xCD\xCC\xCC\x3E\x00\x00\x00\x3F\x9A\x99\x19\x3F\x33\x33\x33\x3F\xCD\xCC\x4C\x3F\x66\x66\x66\x3F\x00\x00\x80\x3F&quot;</span></span><br><span class="line">SORTBY score</span><br><span class="line">RETURN 2 text score</span><br><span class="line">DIALECT 2</span><br><span class="line"><span class="comment">## 返回结果</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 2</span><br><span class="line">2) <span class="string">&quot;vector:1&quot;</span></span><br><span class="line">3) 1) <span class="string">&quot;score&quot;</span></span><br><span class="line">   2) <span class="string">&quot;0&quot;</span></span><br><span class="line">   3) <span class="string">&quot;text&quot;</span></span><br><span class="line">   4) <span class="string">&quot;This is a test vector&quot;</span></span><br><span class="line">4) <span class="string">&quot;vector:2&quot;</span></span><br><span class="line">5) 1) <span class="string">&quot;score&quot;</span></span><br><span class="line">   2) <span class="string">&quot;0.0129868984222&quot;</span></span><br><span class="line">   3) <span class="string">&quot;text&quot;</span></span><br><span class="line">   4) <span class="string">&quot;Another vector&quot;</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>查询表达式: <code>*=&gt;[KNN 2 @vector $vec AS score]</code></p></li></ul><table><thead><tr><th>参数</th><th>含义</th></tr></thead><tbody><tr><td><code>*=&gt;</code></td><td>这是 RediSearch 向量检索语法，这里表示匹配所有文档。<br>向量搜索通常不需要文本过滤，所以直接使用 *。</td></tr><tr><td><code>KNN</code></td><td>执行最近邻搜索（K-Nearest Neighbors）</td></tr><tr><td><code>2</code></td><td>返回最相近的 <strong>2 条记录</strong></td></tr><tr><td><code>@vector</code></td><td>索引中的向量字段名</td></tr><tr><td><code>$vec</code></td><td>查询向量参数（来自 PARAMS）</td></tr><tr><td><code>AS score</code></td><td>将“距离结果”保存到字段名 score</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>PARAMS 参数绑定</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 语法</span></span><br><span class="line">PARAMS &lt;N&gt; &lt;key1&gt; &lt;value1&gt; ... &lt;keyN&gt; &lt;valueN&gt;</span><br><span class="line"><span class="comment"># 本示例中表示：定义一个参数，参数名：vec，参数值：一个 float32 二进制向量</span></span><br><span class="line">PARAMS 2 vec <span class="string">&quot;&lt;binary vector&gt;&quot;</span></span><br><span class="line"><span class="comment"># 注意这里要将 float 数组转换为FLOAT32二进制数据，不能直接传 [0.1,0.2,0.3]</span></span><br></pre></td></tr></table></figure><blockquote><p>float数组转换为FLOAT32二进制数据的方法</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.langchain4j;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.nio.ByteBuffer;</span><br><span class="line"><span class="keyword">import</span> java.nio.ByteOrder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FloatToByte</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">float</span>[] vector = <span class="keyword">new</span> <span class="title class_">float</span>[]&#123;<span class="number">0.1f</span>, <span class="number">0.2f</span>, <span class="number">0.3f</span>, <span class="number">0.4f</span>, <span class="number">0.5f</span>, <span class="number">0.6f</span>, <span class="number">0.7f</span>, <span class="number">0.8f</span>, <span class="number">0.9f</span>, <span class="number">1.0f</span>&#125;;</span><br><span class="line">        <span class="type">byte</span>[] bytes = toFloat32Bytes(vector);</span><br><span class="line">        System.out.println(toRedisHex(bytes));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">byte</span>[] toFloat32Bytes(<span class="type">float</span>[] vector) &#123;</span><br><span class="line">        <span class="type">ByteBuffer</span> <span class="variable">buffer</span> <span class="operator">=</span> ByteBuffer</span><br><span class="line">                .allocate(vector.length * <span class="number">4</span>)</span><br><span class="line">                .order(ByteOrder.LITTLE_ENDIAN); <span class="comment">// Redis 要求小端序</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">float</span> v : vector) &#123;</span><br><span class="line">            buffer.putFloat(v);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> buffer.array();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">toRedisHex</span><span class="params">(<span class="type">byte</span>[] bytes)</span> &#123;</span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">sb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>(bytes.length * <span class="number">4</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">byte</span> b : bytes) &#123;</span><br><span class="line">            sb.append(<span class="string">&quot;\\x&quot;</span>);</span><br><span class="line">            sb.append(String.format(<span class="string">&quot;%02X&quot;</span>, b &amp; <span class="number">0xFF</span>));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sb.toString();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>SORTBY score 排序规则</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 按 score 字段排序（默认升序）。</span></span><br><span class="line">SORTBY score</span><br><span class="line"><span class="comment">## 注意，COSINE 度量在 Redis 中返回的是“距离”，不是“相似度”</span></span><br><span class="line">score = 距离，距离越小 → 越相似 → 排在前面</span><br><span class="line">如果希望返回的相似度得分，则 (1 - score)</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>RETURN 2 text score: 返回字段控制</p></li><li class="lvl-2"><p>DIALECT 2: Dialect 版本，向量搜索语法：<code>=&gt;[KNN ...]</code> 必须使用 Dialect ≥ 2，否则会报语法错误。</p></li></ul><h2 id="使用-Redis-向量搜索的示例-基于-LangChain4j">使用 Redis 向量搜索的示例(基于 <code>LangChain4j</code>)</h2><ul class="lvl-0"><li class="lvl-2"><p>示例代码：<a href="https://github.com/hanqunfeng/springbootchapter/tree/master/springboot3-demo/redis-demo/langchain4j-ai-demo">GitHub</a></p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.langchain4j.embeding;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.example.langchain4j.ModelUtil;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.community.model.dashscope.QwenEmbeddingModel;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.community.store.embedding.redis.RedisEmbeddingStore;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.data.embedding.Embedding;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.data.segment.TextSegment;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.model.embedding.EmbeddingModel;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.model.output.Response;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.store.embedding.EmbeddingMatch;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.store.embedding.EmbeddingSearchRequest;</span><br><span class="line"><span class="keyword">import</span> dev.langchain4j.store.embedding.EmbeddingStore;</span><br><span class="line"><span class="keyword">import</span> redis.clients.jedis.DefaultJedisClientConfig;</span><br><span class="line"><span class="keyword">import</span> redis.clients.jedis.HostAndPort;</span><br><span class="line"><span class="keyword">import</span> redis.clients.jedis.UnifiedJedis;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Files;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Paths;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisVectorDemo</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// EmbeddingModel</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">EmbeddingModel</span> <span class="variable">embeddingModel</span> <span class="operator">=</span> QwenEmbeddingModel.builder()</span><br><span class="line">            .apiKey(ModelUtil.DASHSCOPE_API_KEY)</span><br><span class="line">            .modelName(<span class="string">&quot;text-embedding-v3&quot;</span>)</span><br><span class="line">            .build();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Redis连接信息</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">DefaultJedisClientConfig</span> <span class="variable">jedisClientConfig</span> <span class="operator">=</span> DefaultJedisClientConfig.builder()</span><br><span class="line">            .user(<span class="string">&quot;admin&quot;</span>)</span><br><span class="line">            .password(<span class="string">&quot;123456&quot;</span>)</span><br><span class="line">            .build();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">HostAndPort</span> <span class="variable">hostAndPort</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HostAndPort</span>(<span class="string">&quot;127.0.0.1&quot;</span>, <span class="number">6379</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// EmbeddingStore 这里是Redis向量数据库</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> EmbeddingStore&lt;TextSegment&gt; embeddingStore = RedisEmbeddingStore.builder()</span><br><span class="line">            .unifiedJedis(<span class="keyword">new</span> <span class="title class_">UnifiedJedis</span>(hostAndPort, jedisClientConfig))</span><br><span class="line">            .dimension(<span class="number">1024</span>) <span class="comment">// 模型返回的维度</span></span><br><span class="line">            .indexName(<span class="string">&quot;redis-vector-index&quot;</span>) <span class="comment">// 索引名称，默认 embedding-index</span></span><br><span class="line">            .prefix(<span class="string">&quot;redis-vector-embedding:&quot;</span>) <span class="comment">// 索引前缀，默认 embedding:</span></span><br><span class="line">            .build();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 读取 rag.txt 并插入到 Redis</span></span><br><span class="line">        insertDocumentsToRedis();</span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">question</span> <span class="operator">=</span> <span class="string">&quot;Redis 7 如何开启向量检索功能？&quot;</span>;</span><br><span class="line">        search(question);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 搜索向量</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> question</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">search</span><span class="params">(String question)</span>&#123;</span><br><span class="line">        <span class="type">TextSegment</span> <span class="variable">textSegment</span> <span class="operator">=</span> TextSegment.from(question);</span><br><span class="line">        Response&lt;Embedding&gt; embed = embeddingModel.embed(textSegment);</span><br><span class="line">        <span class="keyword">final</span> <span class="type">Embedding</span> <span class="variable">queryEmbedding</span> <span class="operator">=</span> embed.content();</span><br><span class="line"></span><br><span class="line">        <span class="type">EmbeddingSearchRequest</span> <span class="variable">embeddingSearchRequest</span> <span class="operator">=</span> EmbeddingSearchRequest.builder()</span><br><span class="line">                .queryEmbedding(queryEmbedding)</span><br><span class="line">                .maxResults(<span class="number">3</span>)</span><br><span class="line">                .build();</span><br><span class="line">        List&lt;EmbeddingMatch&lt;TextSegment&gt;&gt; matches = embeddingStore.search(embeddingSearchRequest).matches();</span><br><span class="line">        <span class="keyword">for</span> (EmbeddingMatch&lt;TextSegment&gt; match : matches) &#123;</span><br><span class="line">            System.out.println(match.embedded().text());</span><br><span class="line">            System.out.println(match.score());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 插入文档到 Redis，初始化数据</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">insertDocumentsToRedis</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">content</span> <span class="operator">=</span> Files.readString(Paths.get(<span class="string">&quot;rag.txt&quot;</span>));</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 简单按段落分割，也可以使用专门的文本分割工具</span></span><br><span class="line">            String[] paragraphs = content.split(<span class="string">&quot;\n\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">for</span> (String paragraph : paragraphs) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!paragraph.trim().isEmpty()) &#123;</span><br><span class="line">                    <span class="type">TextSegment</span> <span class="variable">textSegment</span> <span class="operator">=</span> TextSegment.from(paragraph.trim());</span><br><span class="line">                    Response&lt;Embedding&gt; embeddingResponse = embeddingModel.embed(textSegment);</span><br><span class="line">                    <span class="type">Embedding</span> <span class="variable">embedding</span> <span class="operator">=</span> embeddingResponse.content();</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 将向量和文本段添加到 Redis</span></span><br><span class="line">                    embeddingStore.add(embedding, textSegment);</span><br><span class="line">                    System.out.println(<span class="string">&quot;已添加段落到向量库: &quot;</span> + textSegment.text().substring(<span class="number">0</span>, Math.min(<span class="number">50</span>, textSegment.text().length())) + <span class="string">&quot;...&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;所有文档已成功插入到 Redis 向量库中&quot;</span>);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            System.err.println(<span class="string">&quot;读取文件失败: &quot;</span> + e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>rag.txt 内容</p></li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">问题：Redis 7 如何开启向量检索功能？</span><br><span class="line">答案：需要安装并加载 RediSearch 模块，然后创建包含 VECTOR 字段的索引即可支持向量相似度搜索。</span><br><span class="line"></span><br><span class="line">问题：Redis 中 STRING 和 HASH 的主要区别是什么？</span><br><span class="line">答案：STRING 适合存储单值或序列化对象，HASH 适合存储结构化字段，支持单字段读写和节省内存。</span><br><span class="line"></span><br><span class="line">问题：Spring Boot 如何配置 RedisTemplate 连接 Redis 集群？</span><br><span class="line">答案：需要配置 cluster.nodes 节点列表，并使用 Lettuce 客户端自动发现和维护集群拓扑。</span><br></pre></td></tr></table></figure><h2 id="后记">后记</h2><ul class="lvl-0"><li class="lvl-2"><p>相同的功能使用 <code>SpringAI</code> 也实现了一版，具体代码参看示例代码：<a href="https://github.com/hanqunfeng/springbootchapter/tree/master/springboot3-demo/redis-demo/springai-redis-demo">GitHub</a></p></li></ul>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/13/redis7-module-RediSearch-vector/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/13/redis7-module-RediSearch-vector/"/>
    <published>2026-01-13T13:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 Redis 扩展模块 – RediSearch 操作 向量检索 的方法(<code>非 Redis8 中新增的向量功能</code>)</li>
<li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li>
<li class="lvl-2">RediSearch 的安装参见 <a href="/2025/12/26/redis7-module-RediSearch/" title="Redis 扩展模块 -- RediSearch 的安装方法">Redis 扩展模块 -- RediSearch 的安装方法</a></li>
</ul>]]>
    </summary>
    <title>RediSearch 开发实战 之 向量检索</title>
    <updated>2026-01-14T07:58:32.098Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 SpringBoot 集成 Redis 的方法</li><li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li></ul><span id="more"></span><h2 id="引入依赖">引入依赖</h2><ul class="lvl-0"><li class="lvl-2"><p>在<code>pom.xml</code>中引入依赖</p></li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencyManagement</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-dependencies<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.5.8<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">type</span>&gt;</span>pom<span class="tag">&lt;/<span class="name">type</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>import<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencyManagement</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">   <span class="comment">&lt;!-- redis 连接池 --&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.commons<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>commons-pool2<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="配置-application-yml">配置 <code>application.yml</code></h2><h3 id="单机模式">单机模式</h3><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">data:</span></span><br><span class="line">    <span class="attr">redis:</span></span><br><span class="line">      <span class="attr">host:</span> <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span></span><br><span class="line">      <span class="attr">port:</span> <span class="number">6379</span></span><br><span class="line">      <span class="attr">database:</span> <span class="number">0</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">admin</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">redis123</span></span><br><span class="line">      <span class="attr">timeout:</span> <span class="string">5s</span></span><br><span class="line">      <span class="attr">connectTimeout:</span> <span class="string">3s</span></span><br><span class="line">      <span class="attr">clientName:</span> <span class="string">demo-service</span></span><br><span class="line">      <span class="attr">lettuce:</span></span><br><span class="line">        <span class="attr">shutdown-timeout:</span> <span class="string">100ms</span></span><br><span class="line">        <span class="attr">pool:</span></span><br><span class="line">          <span class="attr">max-active:</span> <span class="number">64</span></span><br><span class="line">          <span class="attr">max-idle:</span> <span class="number">32</span></span><br><span class="line">          <span class="attr">min-idle:</span> <span class="number">16</span></span><br><span class="line">          <span class="attr">max-wait:</span> <span class="string">2s</span></span><br><span class="line">          <span class="attr">time-between-eviction-runs:</span> <span class="string">30s</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>基础连接配置项</p></li></ul><table><thead><tr><th>配置项</th><th>示例值</th><th>含义</th><th>默认值</th><th>生产建议</th></tr></thead><tbody><tr><td><code>spring.data.redis.host</code></td><td><code>localhost</code></td><td>Redis 服务地址（IP / 域名）</td><td><code>localhost</code></td><td>生产使用内网 IP / 域名</td></tr><tr><td><code>spring.data.redis.port</code></td><td><code>6379</code></td><td>Redis 服务端口</td><td><code>6379</code></td><td>一般无需修改</td></tr><tr><td><code>spring.data.redis.username</code></td><td><code>admin</code></td><td>Redis ACL 用户名（Redis 6+）</td><td><code>(空)</code></td><td>未启用 ACL 可不配置</td></tr><tr><td><code>spring.data.redis.password</code></td><td><code>123456</code></td><td>Redis 访问密码</td><td><code>(空)</code></td><td>生产必须配置</td></tr><tr><td><code>spring.data.redis.database</code></td><td><code>0</code></td><td>逻辑数据库索引（0–15）</td><td><code>0</code></td><td>Cluster 模式无效</td></tr><tr><td><code>spring.data.redis.timeout</code></td><td><code>3s</code></td><td>Redis 命令执行超时</td><td><code>60s</code>（依版本）</td><td>建议 2–5s</td></tr><tr><td><code>spring.data.redis.connect-timeout</code></td><td><code>3s</code></td><td>TCP 建连超时</td><td>OS 默认</td><td>建议 1–5s</td></tr><tr><td><code>spring.data.redis.client-name</code></td><td><code>my-redis-client</code></td><td>客户端标识，用于运维定位</td><td><code>(空)</code></td><td>强烈建议配置</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>连接池配置（Lettuce Pool）</p></li></ul><table><thead><tr><th>配置项</th><th>示例值</th><th>含义</th><th>默认值</th><th>生产建议</th></tr></thead><tbody><tr><td><code>spring.data.redis.lettuce.pool.max-active</code></td><td><code>8</code></td><td>连接池最大连接数（使用中 + 空闲）</td><td><code>8</code></td><td>CPU × 2~4 或压测评估</td></tr><tr><td><code>spring.data.redis.lettuce.pool.max-wait</code></td><td><code>2s</code></td><td>连接耗尽时等待时间</td><td><code>-1</code>（无限等待）</td><td>必须设置，1–3s</td></tr><tr><td><code>spring.data.redis.lettuce.pool.max-idle</code></td><td><code>8</code></td><td>最大空闲连接数</td><td><code>8</code></td><td>max-active × 30%~50%</td></tr><tr><td><code>spring.data.redis.lettuce.pool.min-idle</code></td><td><code>0</code></td><td>最小空闲连接数</td><td><code>0</code></td><td>≥ max-active × 25%</td></tr><tr><td><code>spring.data.redis.lettuce.pool.time-between-eviction-runs</code></td><td><code>60s</code></td><td>空闲连接检测周期</td><td><code>-1</code>（不启用）</td><td>30–60s</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>Lettuce 客户端运行参数</p></li></ul><table><thead><tr><th>配置项</th><th>示例值</th><th>含义</th><th>默认值</th><th>生产建议</th></tr></thead><tbody><tr><td><code>spring.data.redis.lettuce.shutdown-timeout</code></td><td><code>100ms</code></td><td>应用关闭时等待连接释放时间</td><td><code>100ms</code></td><td>一般无需修改</td></tr></tbody></table><h3 id="sentinel-模式">sentinel 模式</h3><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">data:</span></span><br><span class="line">    <span class="attr">redis:</span></span><br><span class="line">      <span class="attr">database:</span> <span class="number">0</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">admin</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">redis123</span></span><br><span class="line">      <span class="attr">timeout:</span> <span class="string">5s</span></span><br><span class="line">      <span class="attr">connectTimeout:</span> <span class="string">3s</span></span><br><span class="line">      <span class="attr">clientName:</span> <span class="string">demo-service</span></span><br><span class="line">      <span class="attr">sentinel:</span></span><br><span class="line">        <span class="attr">master:</span> <span class="string">mymaster</span></span><br><span class="line">        <span class="attr">nodes:</span></span><br><span class="line">          <span class="bullet">-</span> <span class="number">10.0</span><span class="number">.0</span><span class="number">.10</span><span class="string">:26379</span></span><br><span class="line">          <span class="bullet">-</span> <span class="number">10.0</span><span class="number">.0</span><span class="number">.11</span><span class="string">:26379</span></span><br><span class="line">          <span class="bullet">-</span> <span class="number">10.0</span><span class="number">.0</span><span class="number">.12</span><span class="string">:26379</span></span><br><span class="line">      <span class="attr">lettuce:</span></span><br><span class="line">        <span class="attr">shutdown-timeout:</span> <span class="string">100ms</span></span><br><span class="line">        <span class="attr">pool:</span></span><br><span class="line">          <span class="attr">max-active:</span> <span class="number">64</span></span><br><span class="line">          <span class="attr">max-idle:</span> <span class="number">32</span></span><br><span class="line">          <span class="attr">min-idle:</span> <span class="number">16</span></span><br><span class="line">          <span class="attr">max-wait:</span> <span class="string">2s</span></span><br><span class="line">          <span class="attr">time-between-eviction-runs:</span> <span class="string">30s</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>哨兵模式配置项</p></li></ul><table><thead><tr><th>配置项</th><th>示例值</th><th>含义</th><th>是否必填</th><th>说明</th></tr></thead><tbody><tr><td><code>spring.data.redis.sentinel.master</code></td><td><code>mymaster</code></td><td>Sentinel 监控的 Master 名称</td><td>✅</td><td>必须与 Sentinel <code>monitor</code> 名称完全一致</td></tr><tr><td><code>spring.data.redis.sentinel.nodes</code></td><td><code>10.0.0.10:26379,10.0.0.11:26379,10.0.0.12:26379</code></td><td>Sentinel 节点列表</td><td>✅</td><td>至少配置 2–3 个 Sentinel，提升可用性</td></tr><tr><td><code>spring.data.redis.sentinel.username</code></td><td><code>(空)</code></td><td>Sentinel 的认证用户名</td><td></td><td>如果启用 ACL，则必填</td></tr><tr><td><code>spring.data.redis.sentinel.password</code></td><td><code>(空)</code></td><td>Sentinel 的认证密码</td><td></td><td>如果启用 ACL，则必填</td></tr></tbody></table><h3 id="cluster-模式">cluster 模式</h3><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">data:</span></span><br><span class="line">    <span class="attr">redis:</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">admin</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">redis123</span></span><br><span class="line">      <span class="attr">timeout:</span> <span class="string">5s</span></span><br><span class="line">      <span class="attr">connectTimeout:</span> <span class="string">3s</span></span><br><span class="line">      <span class="attr">clientName:</span> <span class="string">order-service</span></span><br><span class="line">      <span class="attr">cluster:</span></span><br><span class="line">        <span class="attr">nodes:</span></span><br><span class="line">          <span class="bullet">-</span> <span class="number">192.168</span><span class="number">.1</span><span class="number">.10</span><span class="string">:6379</span></span><br><span class="line">          <span class="bullet">-</span> <span class="number">192.168</span><span class="number">.1</span><span class="number">.11</span><span class="string">:6379</span></span><br><span class="line">          <span class="bullet">-</span> <span class="number">192.168</span><span class="number">.1</span><span class="number">.12</span><span class="string">:6379</span></span><br><span class="line">      <span class="attr">lettuce:</span></span><br><span class="line">        <span class="attr">shutdown-timeout:</span> <span class="string">100ms</span></span><br><span class="line">        <span class="attr">cluster:</span></span><br><span class="line">          <span class="attr">refresh:</span></span><br><span class="line">            <span class="attr">adaptive:</span> <span class="literal">true</span></span><br><span class="line">            <span class="attr">period:</span> <span class="string">10s</span></span><br><span class="line">        <span class="attr">pool:</span></span><br><span class="line">          <span class="attr">max-active:</span> <span class="number">64</span></span><br><span class="line">          <span class="attr">max-idle:</span> <span class="number">32</span></span><br><span class="line">          <span class="attr">min-idle:</span> <span class="number">16</span></span><br><span class="line">          <span class="attr">max-wait:</span> <span class="string">2s</span></span><br><span class="line">          <span class="attr">time-between-eviction-runs:</span> <span class="string">30s</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>注意</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">集群模式下，不能配置 `spring.data.redis.database`，因为集群只能使用默认数据库索引 0</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>集群模式配置项</p></li></ul><table><thead><tr><th>配置项</th><th>示例值</th><th>含义</th><th>是否必填</th><th>默认值</th><th>生产建议</th></tr></thead><tbody><tr><td><code>spring.data.redis.cluster.nodes</code></td><td><code>192.168.1.10:6379,...</code></td><td>Redis Cluster 节点地址列表</td><td>✅</td><td><code>(空)</code></td><td>至少配置 3 个节点</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>Cluster 拓扑自动刷新配置，用于解决集群拓扑变化时 Client 无法自动感知</p></li></ul><table><thead><tr><th>配置项</th><th>示例值</th><th>含义</th><th>默认值</th><th>是否推荐</th></tr></thead><tbody><tr><td><code>adaptive</code></td><td><code>true</code></td><td>开启 <strong>事件驱动拓扑刷新</strong></td><td><code>false</code></td><td>✅ 必须</td></tr><tr><td><code>period</code></td><td><code>10s</code></td><td>开启 <strong>定时拓扑刷新周期</strong></td><td>关闭</td><td>✅ 必须</td></tr></tbody></table><h2 id="SpringBoot-配置类">SpringBoot 配置类</h2><h3 id="封装-RedisTemplate">封装 RedisTemplate</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.config;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.fasterxml.jackson.annotation.JsonInclude;</span><br><span class="line"><span class="keyword">import</span> com.fasterxml.jackson.databind.DeserializationFeature;</span><br><span class="line"><span class="keyword">import</span> com.fasterxml.jackson.databind.ObjectMapper;</span><br><span class="line"><span class="keyword">import</span> com.fasterxml.jackson.databind.json.JsonMapper;</span><br><span class="line"><span class="keyword">import</span> com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.connection.RedisConnectionFactory;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.core.RedisTemplate;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.serializer.StringRedisSerializer;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> ObjectMapper <span class="title function_">redisObjectMapper</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> JsonMapper.builder()</span><br><span class="line">                .addModule(<span class="keyword">new</span> <span class="title class_">JavaTimeModule</span>())</span><br><span class="line">                .serializationInclusion(JsonInclude.Include.NON_NULL)</span><br><span class="line">                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, <span class="literal">false</span>)</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> RedisTemplate&lt;String, Object&gt; <span class="title function_">redisTemplate</span><span class="params">(</span></span><br><span class="line"><span class="params">            RedisConnectionFactory connectionFactory,</span></span><br><span class="line"><span class="params">            ObjectMapper objectMapper)</span> &#123;</span><br><span class="line"></span><br><span class="line">        RedisTemplate&lt;String, Object&gt; template = <span class="keyword">new</span> <span class="title class_">RedisTemplate</span>&lt;&gt;();</span><br><span class="line">        template.setConnectionFactory(connectionFactory);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// --- Key 序列化 ---</span></span><br><span class="line">        <span class="type">StringRedisSerializer</span> <span class="variable">stringSerializer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringRedisSerializer</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// --- Value 序列化 ---</span></span><br><span class="line">        <span class="comment">// 使用 Jackson JSON，避免 JDK 序列化性能与安全问题</span></span><br><span class="line">        <span class="type">GenericJackson2JsonRedisSerializer</span> <span class="variable">jsonSerializer</span> <span class="operator">=</span></span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">GenericJackson2JsonRedisSerializer</span>(objectMapper);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Key</span></span><br><span class="line">        template.setKeySerializer(stringSerializer);</span><br><span class="line">        template.setHashKeySerializer(stringSerializer);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Value</span></span><br><span class="line">        template.setValueSerializer(jsonSerializer);</span><br><span class="line">        template.setHashValueSerializer(jsonSerializer);</span><br><span class="line"></span><br><span class="line">        template.afterPropertiesSet();</span><br><span class="line">        <span class="keyword">return</span> template;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="开启注解式缓存">开启注解式缓存</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.config;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> lombok.Getter;</span><br><span class="line"><span class="keyword">import</span> lombok.Setter;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.AutoConfigureAfter;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.context.properties.ConfigurationProperties;</span><br><span class="line"><span class="keyword">import</span> org.springframework.cache.CacheManager;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.cache.RedisCacheConfiguration;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.cache.RedisCacheManager;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.cache.RedisCacheWriter;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.core.RedisTemplate;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.serializer.RedisSerializationContext;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.time.Duration;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@AutoConfigureAfter(value = RedisConfig.class)</span></span><br><span class="line"><span class="comment">//注入redis分组配置属性：ttlmap</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;caching&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisCachingConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 分组配置项</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Getter</span></span><br><span class="line">    <span class="meta">@Setter</span></span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, Long&gt; ttlmap;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> CacheManager <span class="title function_">cacheManager</span><span class="params">(RedisTemplate&lt;String, Object&gt; redisTemplate)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> RedisCacheManager</span><br><span class="line">                .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory()))</span><br><span class="line">                <span class="comment">//缺省配置</span></span><br><span class="line">                .cacheDefaults(redisCacheConfiguration(redisTemplate, <span class="number">3600L</span>))</span><br><span class="line">                <span class="comment">//分组配置，不需要分组配置可以去掉，不同的组配置不同的缓存过期时间，可以防止&quot;缓存雪崩&quot;</span></span><br><span class="line">                .withInitialCacheConfigurations(initialRedisCacheConfiguration(redisTemplate))</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 缺省缓存配置</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> RedisCacheConfiguration <span class="title function_">redisCacheConfiguration</span><span class="params">(RedisTemplate&lt;String, Object&gt; redisTemplate, Long ttl)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> RedisCacheConfiguration.defaultCacheConfig()</span><br><span class="line">                .entryTtl(Duration.ofSeconds(ttl)) <span class="comment">//设置过期，单位秒</span></span><br><span class="line">                <span class="comment">//.disableCachingNullValues() //不允许存储null值，默认可以存储null，缓存null可以防止&quot;缓存穿透&quot;</span></span><br><span class="line">                <span class="comment">//.disableKeyPrefix()  //设置key前面不带前缀，最好不要去掉前缀，否则执行删除缓存时会清空全部缓存</span></span><br><span class="line">                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getStringSerializer()))</span><br><span class="line">                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 针对不同的缓存组配置不同的设置</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, RedisCacheConfiguration&gt; <span class="title function_">initialRedisCacheConfiguration</span><span class="params">(RedisTemplate&lt;String, Object&gt; redisTemplate)</span> &#123;</span><br><span class="line">        Map&lt;String, RedisCacheConfiguration&gt; redisCacheConfigurationMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (Map.Entry&lt;String, Long&gt; entry : ttlmap.entrySet()) &#123;</span><br><span class="line">            redisCacheConfigurationMap.put(entry.getKey(), redisCacheConfiguration(redisTemplate, entry.getValue()));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> redisCacheConfigurationMap;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>ttlmap 配置项</p></li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">caching:</span></span><br><span class="line">  <span class="attr">ttlmap:</span></span><br><span class="line">    <span class="attr">commonCache:</span> <span class="number">3600</span></span><br><span class="line">    <span class="attr">loginCache:</span> <span class="number">7200</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>启动类上要加 @EnableCaching</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@EnableCaching</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DemoApplication</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(DemoApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>缓存注解使用示例</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="comment">// 缓存分组</span></span><br><span class="line"><span class="meta">@CacheConfig(cacheNames = &quot;commonCache&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SystemUserServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">ISystemUserService</span> &#123;</span><br><span class="line">   <span class="meta">@Autowired</span></span><br><span class="line">   SystemUserJpaRepository systemUserJpaRepository;</span><br><span class="line"></span><br><span class="line">   <span class="comment">//向组内添加缓存</span></span><br><span class="line">   <span class="meta">@Cacheable(key = &quot;&#x27;SystemUserServiceImpl.findAll&#x27;&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> List&lt;SystemUser&gt; <span class="title function_">findAll</span><span class="params">()</span> &#123;</span><br><span class="line">      List&lt;SystemUser&gt; systemUserList = systemUserJpaRepository.findAll();</span><br><span class="line">      <span class="keyword">return</span> systemUserList;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">//向组内添加缓存</span></span><br><span class="line">   <span class="meta">@Cacheable(key = &quot;&#x27;SystemUserServiceImpl.findById_&#x27;+ #userId&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> SystemUser <span class="title function_">findById</span><span class="params">(String userId)</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> systemUserJpaRepository.findById(userId);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">// 删除组内指定缓存</span></span><br><span class="line">   <span class="meta">@CacheEvict(key = &quot;&#x27;SystemUserServiceImpl.findById_&#x27;+ #userId&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteById</span><span class="params">(String userId)</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> systemUserJpaRepository.deleteById(userId);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">// 删除本组全部缓存</span></span><br><span class="line">   <span class="meta">@CacheEvict(allEntries = true, beforeInvocation = true)</span></span><br><span class="line">   <span class="keyword">public</span> SystemUser <span class="title function_">add</span><span class="params">(SystemUser user)</span> &#123;</span><br><span class="line">      systemUserJpaRepository.save(user);</span><br><span class="line">      <span class="keyword">return</span> user;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/12/redis-springboot/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/12/redis-springboot/"/>
    <published>2026-01-12T14:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 SpringBoot 集成 Redis 的方法</li>
<li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
</ul>]]>
    </summary>
    <title>SpringBoot 集成 Redis</title>
    <updated>2026-01-12T08:39:11.238Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文基于<code>redis-7.4.7</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li></ul><span id="more"></span><h2 id="Cluster-Management-简介">Cluster Management 简介</h2><ul class="lvl-0"><li class="lvl-2">Redis Cluster Commands 是 Redis 分布式集群的“控制面命令集”，用于管理节点、分片、迁移、故障转移和路由策略，而不是用于业务数据读写。</li></ul><h2 id="Cluster-Management-命令详解">Cluster Management 命令详解</h2><ul class="lvl-0"><li class="lvl-2"><p>通用参数说明</p></li></ul><table><thead><tr><th>字段</th><th>说明</th></tr></thead><tbody><tr><td><strong>slot</strong></td><td>Hash Slot 编号，范围：<code>0 – 16383</code></td></tr><tr><td><strong>nodeId</strong></td><td>Redis Cluster 节点唯一 ID（<code>CLUSTER NODES</code> 可查询）</td></tr></tbody></table><h3 id="一、路由与访问模式控制">一、路由与访问模式控制</h3><table><thead><tr><th>命令</th><th>语法</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td>READONLY</td><td><code>READONLY</code></td><td>允许客户端从副本读</td><td><code>READONLY</code></td></tr><tr><td>READWRITE</td><td><code>READWRITE</code></td><td>恢复只向主节点写</td><td><code>READWRITE</code></td></tr><tr><td>ASKING</td><td><code>ASKING</code></td><td>迁移过程中允许访问目标节点</td><td><code>ASKING</code></td></tr></tbody></table><h3 id="二、Slot-计算与查询">二、Slot 计算与查询</h3><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>用途</th></tr></thead><tbody><tr><td>CLUSTER KEYSLOT</td><td><code>CLUSTER KEYSLOT key</code></td><td>key</td><td>计算 key 所属 slot</td></tr><tr><td>CLUSTER COUNTKEYSINSLOT</td><td><code>CLUSTER COUNTKEYSINSLOT slot</code></td><td>slot: 槽位</td><td>统计 slot 中 key 数量</td></tr><tr><td>CLUSTER GETKEYSINSLOT</td><td><code>CLUSTER GETKEYSINSLOT slot count</code></td><td>slot, count: 返回数量</td><td>获取 slot 中的 key</td></tr></tbody></table><h3 id="三、Slot-分配与迁移（扩容核心）">三、Slot 分配与迁移（扩容核心）</h3><table><thead><tr><th>命令</th><th>语法</th><th>说明</th></tr></thead><tbody><tr><td>CLUSTER ADDSLOTS</td><td><code>CLUSTER ADDSLOTS slot [slot ...]</code></td><td>给节点分配 slot</td></tr><tr><td>CLUSTER ADDSLOTSRANGE</td><td><code>CLUSTER ADDSLOTSRANGE start end ...</code></td><td>批量分配 slot</td></tr><tr><td>CLUSTER DELSLOTS</td><td><code>CLUSTER DELSLOTS slot [slot ...]</code></td><td>删除 slot</td></tr><tr><td>CLUSTER DELSLOTSRANGE</td><td><code>CLUSTER DELSLOTSRANGE start end ...</code></td><td>批量删除 slot</td></tr><tr><td>CLUSTER FLUSHSLOTS</td><td><code>CLUSTER FLUSHSLOTS</code></td><td>清空节点 slot</td></tr><tr><td>CLUSTER SETSLOT</td><td><code>CLUSTER SETSLOT slot MIGRATING|IMPORTING|NODE nodeId|STABLE</code></td><td>设置 slot 状态</td></tr><tr><td>CLUSTER MIGRATION</td><td><code>CLUSTER MIGRATION</code></td><td>查询迁移状态</td></tr><tr><td>CLUSTER SLOT-STATS</td><td><code>CLUSTER SLOT-STATS</code></td><td>slot 统计</td></tr><tr><td>CLUSTER SLOTS</td><td><code>CLUSTER SLOTS</code></td><td>查看 slot 分布<br>已过时，推荐使用 <code>CLUSTER SHARDS</code></td></tr><tr><td>CLUSTER SHARDS</td><td><code>CLUSTER SHARDS</code></td><td>按 shard 展示</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>实际应用: 集群扩容、数据再平衡、故障恢复、热迁移</p></li><li class="lvl-2"><p>通常由 redis-cli 或自动化工具封装执行。</p></li></ul><h4 id="CLUSTER-SETSLOT">CLUSTER SETSLOT</h4><ul class="lvl-0"><li class="lvl-2"><p>参数说明表</p></li></ul><table><thead><tr><th>子命令</th><th>完整语法</th><th>参数含义</th><th>Slot 状态语义</th><th>客户端行为影响</th><th>典型使用阶段</th><th>示例</th></tr></thead><tbody><tr><td><strong>MIGRATING</strong></td><td><code>CLUSTER SETSLOT &lt;slot&gt; MIGRATING &lt;target-node-id&gt;</code></td><td>target-node-id：目标节点 ID（数据迁往的节点）</td><td>当前节点正在把该 slot 的数据迁出</td><td>- 若 key 仍在本节点 → 正常处理<br>- 若 key 已迁走 → 返回 <strong>ASK 重定向</strong></td><td>槽迁移开始阶段（源节点）</td><td><code>CLUSTER SETSLOT 100 MIGRATING e5f6g7...</code></td></tr><tr><td><strong>IMPORTING</strong></td><td><code>CLUSTER SETSLOT &lt;slot&gt; IMPORTING &lt;source-node-id&gt;</code></td><td>source-node-id：源节点 ID（数据来源）</td><td>当前节点准备接收该 slot 的数据</td><td>- 客户端必须先执行 <code>ASKING</code> 才允许访问<br>- 否则返回 MOVED</td><td>槽迁移开始阶段（目标节点）</td><td><code>CLUSTER SETSLOT 100 IMPORTING a1b2c3...</code></td></tr><tr><td><strong>NODE</strong></td><td><code>CLUSTER SETSLOT &lt;slot&gt; NODE &lt;node-id&gt;</code></td><td>node-id：该 slot 的最终归属节点 ID</td><td>明确该 slot 正式归属某节点</td><td>- 集群路由立即更新<br>- 不再返回 ASK</td><td>槽迁移完成阶段（所有节点）</td><td><code>CLUSTER SETSLOT 100 NODE e5f6g7...</code></td></tr><tr><td><strong>STABLE</strong></td><td><code>CLUSTER SETSLOT &lt;slot&gt; STABLE</code></td><td>无附加参数</td><td>清除 IMPORTING / MIGRATING 标记，恢复稳定状态</td><td>- 恢复正常路由<br>- 不改变 slot 所属节点</td><td>异常恢复 / 状态清理</td><td><code>CLUSTER SETSLOT 100 STABLE</code></td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>一个完整 Slot 迁移示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">假设：</span><br><span class="line">    Slot = 100</span><br><span class="line">    源节点 A = a1b2c3...</span><br><span class="line">    目标节点 B = e5f6g7...</span><br></pre></td></tr></table></figure><p>✅ Step 1：源节点标记迁出</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在节点 A 上执行</span></span><br><span class="line">CLUSTER SETSLOT 100 MIGRATING e5f6g7...</span><br><span class="line"><span class="comment"># 标记状态: Slot 100 正在从 A 迁往 B。</span></span><br></pre></td></tr></table></figure><p>✅ Step 2：目标节点标记导入</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在节点 B 上执行</span></span><br><span class="line">CLUSTER SETSLOT 100 IMPORTING a1b2c3...</span><br><span class="line"><span class="comment"># 标记状态：Slot 100 正在从 A 导入到 B</span></span><br></pre></td></tr></table></figure><p>✅ Step 3：迁移数据（CLUSTER GETKEYSINSLOT + MIGRATE），反复获取 key并迁移</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在节点 A 上执行，找到 Slot 100 的 key，反复执行，直到 返回 0 个 key</span></span><br><span class="line">CLUSTER GETKEYSINSLOT 100 1000</span><br><span class="line"></span><br><span class="line"><span class="comment"># 迁移数据，分批执行</span></span><br><span class="line">MIGRATE &lt;B-IP&gt; &lt;B-PORT&gt; <span class="string">&quot;&quot;</span> 0 5000 KEYS key1 key2 ... key1000</span><br><span class="line"><span class="comment"># 将数据从 A 节点迁移到 B 节点，可以多次执行</span></span><br></pre></td></tr></table></figure><p>✅ Step 4：设置最终归属</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在所有节点上执行</span></span><br><span class="line">CLUSTER SETSLOT 100 NODE e5f6g7...</span><br><span class="line"><span class="comment"># 标记状态：Slot 100 正式归属 B。</span></span><br></pre></td></tr></table></figure><p>✅ Step 5（可选）：异常清理</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 🔍 在哪个节点看到 slot 仍然处于 MIGRATING / IMPORTING，就在哪个节点执行 STABLE。可以通过命令 CLUSTER NODES 查看</span></span><br><span class="line"><span class="comment"># 正常情况下不需要执行，只要运行了 CLUSTER SETSLOT 100 NODE e5f6g7... 就会自动清除这些状态</span></span><br><span class="line"><span class="comment"># 只有在 迁移异常或中断 时才需要。</span></span><br><span class="line">CLUSTER SETSLOT 100 STABLE</span><br><span class="line"><span class="comment"># 用途：清除 MIGRATING / IMPORTING 状态</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>单独 MIGRATE 与 Cluster Slot MIGRATE 对比</p></li></ul><table><thead><tr><th>维度</th><th>MIGRATE 命令</th><th>Redis Cluster Slot 迁移</th></tr></thead><tbody><tr><td>迁移对象</td><td>单个或多个 <strong>Key</strong></td><td>一个或多个 <strong>Slot（包含成千上万 key）</strong></td></tr><tr><td>是否改变 slot 归属</td><td>❌ 不改变</td><td>✅ 会改变</td></tr><tr><td>客户端感知</td><td>客户端无感，但可能访问到旧节点失败</td><td>客户端自动重定向（MOVED / ASK）</td></tr><tr><td>自动路由支持</td><td>❌</td><td>✅</td></tr><tr><td>原子性粒度</td><td>单次 MIGRATE 是原子</td><td>Slot 迁移是分阶段的</td></tr><tr><td>支持在线迁移</td><td>⚠️ 可以，但业务需自行控制</td><td>✅ 天生支持在线迁移</td></tr><tr><td>失败恢复能力</td><td>❌ 需要人工兜底</td><td>✅ Cluster 协议自动修复</td></tr><tr><td>运维复杂度</td><td>低</td><td>高</td></tr><tr><td>自动化程度</td><td>低（需要脚本）</td><td>高（redis-cli --cluster、运维平台）</td></tr><tr><td>典型用途</td><td>数据搬运、修复、临时迁移</td><td>扩容、缩容、负载均衡</td></tr></tbody></table><h3 id="四、节点管理与拓扑">四、节点管理与拓扑</h3><table><thead><tr><th>命令</th><th>语法</th><th>说明</th></tr></thead><tbody><tr><td>CLUSTER MEET</td><td><code>CLUSTER MEET ip port</code></td><td>将指定的节点加入当前集群</td></tr><tr><td>CLUSTER FORGET</td><td><code>CLUSTER FORGET nodeId</code></td><td>从集群移除节点</td></tr><tr><td>CLUSTER NODES</td><td><code>CLUSTER NODES</code></td><td>查看节点列表</td></tr><tr><td>CLUSTER LINKS</td><td><code>CLUSTER LINKS</code></td><td>节点通信链路</td></tr><tr><td>CLUSTER MYID</td><td><code>CLUSTER MYID</code></td><td>当前节点 ID</td></tr><tr><td>CLUSTER MYSHARDID</td><td><code>CLUSTER MYSHARDID</code></td><td>当前 shard ID</td></tr><tr><td>CLUSTER REPLICAS</td><td><code>CLUSTER REPLICAS nodeId</code></td><td>查看指定节点的副本</td></tr><tr><td>CLUSTER SLAVES</td><td><code>CLUSTER SLAVES nodeId</code></td><td>旧命令（等价 replicas）</td></tr><tr><td>CLUSTER REPLICATE</td><td><code>CLUSTER REPLICATE nodeId</code></td><td>将指定的节点设置为当前节点的副本</td></tr></tbody></table><h3 id="五、故障转移与高可用">五、故障转移与高可用</h3><table><thead><tr><th>命令</th><th>语法</th><th>说明</th></tr></thead><tbody><tr><td>CLUSTER FAILOVER</td><td><code>CLUSTER FAILOVER [FORCE|TAKEOVER]</code></td><td>手动触发主从切换</td></tr><tr><td>CLUSTER COUNT-FAILURE-REPORTS</td><td><code>CLUSTER COUNT-FAILURE-REPORTS nodeId</code></td><td>故障投票统计</td></tr></tbody></table><h3 id="六、集群配置与内部控制">六、集群配置与内部控制</h3><table><thead><tr><th>命令</th><th>语法</th><th>说明</th></tr></thead><tbody><tr><td>CLUSTER RESET</td><td><code>CLUSTER RESET [HARD|SOFT]</code></td><td>重置节点</td></tr><tr><td>CLUSTER SAVECONFIG</td><td><code>CLUSTER SAVECONFIG</code></td><td>保存配置</td></tr><tr><td>CLUSTER SET-CONFIG-EPOCH</td><td><code>CLUSTER SET-CONFIG-EPOCH epoch</code></td><td>设置配置版本</td></tr><tr><td>CLUSTER BUMPEPOCH</td><td><code>CLUSTER BUMPEPOCH</code></td><td>自增 epoch</td></tr><tr><td>CLUSTER INFO</td><td><code>CLUSTER INFO</code></td><td>集群状态</td></tr><tr><td>CLUSTER COUNT-FAILURE-REPORTS</td><td><code>CLUSTER COUNT-FAILURE-REPORTS nodeId</code></td><td>故障统计</td></tr></tbody></table>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/12/redis7-command-06-cluster-management/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/12/redis7-command-06-cluster-management/"/>
    <published>2026-01-12T12:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文基于<code>redis-7.4.7</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
</ul>]]>
    </summary>
    <title>Redis 命令详解：Cluster Management 命令</title>
    <updated>2026-01-12T03:47:25.260Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文基于<code>redis-7.4.7</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2"><a href="https://www.runoob.com/lua/lua-tutorial.html">Lua语法参考</a></li></ul><span id="more"></span><h2 id="Scripting-Functions-简介">Scripting / Functions 简介</h2><ul class="lvl-0"><li class="lvl-2">Redis Scripting / Functions 是 Redis 提供的一套“服务器端可编程执行机制”，允许客户端把逻辑发送到 Redis 内部执行，从而：</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">将多条命令合并为一次原子执行</span><br><span class="line">减少网络往返（RTT）</span><br><span class="line">保证并发一致性</span><br><span class="line">支持逻辑复用与版本化管理</span><br></pre></td></tr></table></figure><h2 id="Scripting-Functions-命令详解">Scripting / Functions 命令详解</h2><h3 id="一、Lua-脚本执行与管理">一、Lua 脚本执行与管理</h3><h4 id="1-1-Sctipt-脚本执行-EVAL">1.1 Sctipt 脚本执行(EVAL)</h4><ul class="lvl-0"><li class="lvl-2">用于直接在 Redis 中执行 Lua 脚本。</li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th><th>说明</th></tr></thead><tbody><tr><td>EVAL</td><td><code>EVAL script numkeys key [key ...] arg [arg ...]</code></td><td>script：Lua 脚本<br>numkeys：key 数量</td><td><code>EVAL &quot;return redis.call('GET', KEYS[1])&quot; 1 k1</code></td><td>直接执行脚本</td></tr><tr><td>EVAL_RO</td><td><code>EVAL_RO script numkeys key [key ...] arg [arg ...]</code></td><td>只读执行</td><td><code>EVAL_RO &quot;return redis.call('GET', KEYS[1])&quot; 1 k1</code></td><td>副本安全</td></tr><tr><td>EVALSHA</td><td><code>EVALSHA sha1 numkeys ...</code></td><td>sha1：脚本摘要</td><td><code>EVALSHA abc123 1 k1</code></td><td>缓存执行</td></tr><tr><td>EVALSHA_RO</td><td><code>EVALSHA_RO sha1 numkeys ...</code></td><td>只读缓存执行</td><td>同上</td><td>副本安全</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>核心特性</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">原子执行（单线程）</span><br><span class="line">可以访问 KEYS / ARGV</span><br><span class="line">会阻塞事件循环（脚本过长有风险）</span><br><span class="line">支持脚本缓存</span><br></pre></td></tr></table></figure><h4 id="1-2-脚本运行管理（SCRIPT）">1.2 脚本运行管理（SCRIPT）</h4><ul class="lvl-0"><li class="lvl-2"><p>用于管理 Lua 脚本缓存和执行状态。</p></li><li class="lvl-2"><p>将脚本加载到缓存中，避免多次执行相同脚本时都要重新发送脚本到 Redis 服务器。</p></li><li class="lvl-2"><p>不会持久化，redis重启后失效</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th><th>说明</th></tr></thead><tbody><tr><td>SCRIPT LOAD</td><td><code>SCRIPT LOAD script</code></td><td>加载脚本并返回 SHA1</td><td><code>SCRIPT LOAD &quot;return 1&quot;</code></td><td>预热，不会持久化，redis重启后失效</td></tr><tr><td>SCRIPT EXISTS</td><td><code>SCRIPT EXISTS sha1 [sha1 ...]</code></td><td>判断是否已缓存</td><td><code>SCRIPT EXISTS abc123</code></td><td>校验</td></tr><tr><td>SCRIPT FLUSH</td><td><code>SCRIPT FLUSH [ASYNC]</code></td><td>清空脚本缓存</td><td><code>SCRIPT FLUSH</code></td><td>运维</td></tr><tr><td>SCRIPT KILL</td><td><code>SCRIPT KILL</code></td><td>终止正在执行脚本</td><td><code>SCRIPT KILL</code></td><td>紧急</td></tr><tr><td>SCRIPT DEBUG</td><td><code>SCRIPT DEBUG YES|SYNC|NO</code></td><td>脚本调试模式</td><td><code>SCRIPT DEBUG YES</code></td><td>调试</td></tr></tbody></table><h3 id="二、Redis-Functions（函数库与调用）">二、Redis Functions（函数库与调用）</h3><ul class="lvl-0"><li class="lvl-2"><p>Redis 7 引入，用于替代大规模 Lua 脚本管理。</p></li></ul><h4 id="2-1-函数调用">2.1 函数调用</h4><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th><th>说明</th></tr></thead><tbody><tr><td>FCALL</td><td><code>FCALL function numkeys key [key ...] arg [arg ...]</code></td><td>function：函数名</td><td><code>FCALL stock.decr 1 stock:1 5</code></td><td>调用函数</td></tr><tr><td>FCALL_RO</td><td><code>FCALL_RO function numkeys ...</code></td><td>只读调用</td><td><code>FCALL_RO metrics.get 1 k1</code></td><td>副本安全</td></tr></tbody></table><h4 id="2-2-函数库管理">2.2 函数库管理</h4><ul class="lvl-0"><li class="lvl-2"><p><code>FUNCTION LOAD</code> 加载的脚本会被持久化保存，重启redis依旧有效。</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th><th>说明</th></tr></thead><tbody><tr><td>FUNCTION LOAD</td><td><code>FUNCTION LOAD [REPLACE] payload</code></td><td>payload：函数源码</td><td><code>FUNCTION LOAD &lt;code&gt;</code></td><td>加载函数,会持久化</td></tr><tr><td>FUNCTION LIST</td><td><code>FUNCTION LIST [LIBRARY lib]</code></td><td>查看函数库</td><td><code>FUNCTION LIST</code></td><td>查询</td></tr><tr><td>FUNCTION DELETE</td><td><code>FUNCTION DELETE library</code></td><td>删除函数库</td><td><code>FUNCTION DELETE mylib</code></td><td>清理</td></tr><tr><td>FUNCTION DUMP</td><td><code>FUNCTION DUMP</code></td><td>导出函数库</td><td><code>FUNCTION DUMP</code></td><td>备份</td></tr><tr><td>FUNCTION RESTORE</td><td><code>FUNCTION RESTORE dump [REPLACE]</code></td><td>恢复函数库</td><td><code>FUNCTION RESTORE &lt;dump&gt;</code></td><td>恢复</td></tr><tr><td>FUNCTION FLUSH</td><td><code>FUNCTION FLUSH [ASYNC]</code></td><td>清空所有函数</td><td><code>FUNCTION FLUSH</code></td><td>高风险</td></tr><tr><td>FUNCTION KILL</td><td><code>FUNCTION KILL</code></td><td>终止正在运行的函数</td><td><code>FUNCTION KILL</code></td><td>紧急停止</td></tr><tr><td>FUNCTION STATS</td><td><code>FUNCTION STATS</code></td><td>运行统计</td><td><code>FUNCTION STATS</code></td><td>监控</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>Redis Functions 的优势</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">持久化（随 RDB / AOF 保存）</span><br><span class="line">支持版本化与部署</span><br><span class="line">支持多函数库</span><br><span class="line">更适合平台化治理</span><br></pre></td></tr></table></figure><h3 id="Function-源码格式">Function 源码格式</h3><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">#!lua name=ratelimit <span class="comment">-- 这是 Redis Function 专用头声明</span></span><br><span class="line">                     <span class="comment">-- #!lua: 声明这是一个 Lua Function Library，目前只支持 Lua</span></span><br><span class="line">                     <span class="comment">-- name: library，函数库名称，全局唯一</span></span><br><span class="line"></span><br><span class="line">redis.register_function(<span class="string">&#x27;allow&#x27;</span>, <span class="function"><span class="keyword">function</span><span class="params">(keys, args)</span></span> <span class="comment">-- 函数注册 API，</span></span><br><span class="line">                                                      <span class="comment">-- allow : 函数名称</span></span><br><span class="line">                                                      <span class="comment">-- keys: KEY 数组</span></span><br><span class="line">                                                      <span class="comment">-- args: ARGV 数组</span></span><br><span class="line">    xxx                 <span class="comment">-- 函数逻辑</span></span><br><span class="line">    <span class="keyword">return</span> xxx          <span class="comment">-- 函数返回值，只支持 数组/整数/字符串/表格&#123;a,b&#125;作为返回值</span></span><br><span class="line"><span class="keyword">end</span>)                    <span class="comment">-- 函数结束</span></span><br></pre></td></tr></table></figure><h2 id="示例">示例</h2><ul class="lvl-0"><li class="lvl-2"><ul class="lvl-2"><li class="lvl-4">场景：分布式滑动窗口限流器（企业级）</li></ul></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">业务背景</span><br><span class="line">    每个用户：每分钟最多 5 次请求</span><br><span class="line">    支持：高并发、原子统计、自动过期、可部署升级</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>数据模型设计</p></li></ul><table><thead><tr><th>Key</th><th>类型</th><th>示例</th></tr></thead><tbody><tr><td>rate:{userId}</td><td>ZSET</td><td>score=timestamp</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>初始化数据</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 假设 userId = 1001</span></span><br><span class="line">ZADD rate:1001 1704999990000 1704999990000</span><br><span class="line">ZADD rate:1001 1704999995000 1704999995000</span><br><span class="line">ZADD rate:1001 1704999998000 1704999998000</span><br><span class="line">ZADD rate:1001 1705000001000 1705000001000</span><br><span class="line">ZADD rate:1001 1705000002000 1705000002000</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置 key 自动过期，window = 60000 ms</span></span><br><span class="line">PEXPIRE rate:1001 60000</span><br></pre></td></tr></table></figure><h3 id="Script-使用示例">Script 使用示例</h3><ul class="lvl-0"><li class="lvl-2"><p>脚本</p></li></ul><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> rateKey = KEYS[<span class="number">1</span>]</span><br><span class="line"><span class="keyword">local</span> maxReq  = <span class="built_in">tonumber</span>(ARGV[<span class="number">1</span>])</span><br><span class="line"><span class="keyword">local</span> window  = <span class="built_in">tonumber</span>(ARGV[<span class="number">2</span>])</span><br><span class="line"><span class="keyword">local</span> now     = <span class="built_in">tonumber</span>(ARGV[<span class="number">3</span>])</span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> minTime = now - window</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 1. 清理过期请求</span></span><br><span class="line">redis.call(<span class="string">&quot;ZREMRANGEBYSCORE&quot;</span>, rateKey, <span class="number">0</span>, minTime)</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 2. 当前请求数</span></span><br><span class="line"><span class="keyword">local</span> count = <span class="built_in">tonumber</span>(redis.call(<span class="string">&quot;ZCARD&quot;</span>, rateKey))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> count &gt;= maxReq <span class="keyword">then</span></span><br><span class="line">    <span class="keyword">return</span> count</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 3. 记录请求</span></span><br><span class="line">redis.call(<span class="string">&quot;ZADD&quot;</span>, rateKey, now, now)</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 4. 设置自动过期</span></span><br><span class="line">redis.call(<span class="string">&quot;PEXPIRE&quot;</span>, rateKey, window)</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> count+<span class="number">1</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>调用脚本</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; EVAL <span class="string">&quot;local rateKey = KEYS[1] local maxReq  = tonumber(ARGV[1]) local window  = tonumber(ARGV[2]) local now     = tonumber(ARGV[3]) local minTime = now - window redis.call(\&quot;ZREMRANGEBYSCORE\&quot;, rateKey, 0, minTime) local count = tonumber(redis.call(\&quot;ZCARD\&quot;, rateKey)) if count &gt;= maxReq then return count end redis.call(\&quot;ZADD\&quot;, rateKey, now, now) redis.call(\&quot;PEXPIRE\&quot;, rateKey, window) return count+1&quot;</span> 1 rate:1001 5 60000 1705000003000</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>预热后调用</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; SCRIPT LOAD  <span class="string">&quot;local rateKey = KEYS[1] local maxReq  = tonumber(ARGV[1]) local window  = tonumber(ARGV[2]) local now     = tonumber(ARGV[3]) local minTime = now - window redis.call(\&quot;ZREMRANGEBYSCORE\&quot;, rateKey, 0, minTime) local count = tonumber(redis.call(\&quot;ZCARD\&quot;, rateKey)) if count &gt;= maxReq then return count end redis.call(\&quot;ZADD\&quot;, rateKey, now, now) redis.call(\&quot;PEXPIRE\&quot;, rateKey, window) return count+1&quot;</span></span><br><span class="line"><span class="comment">## 脚本 SHA</span></span><br><span class="line"><span class="string">&quot;69ca329ae4744a8b509f9daefea0ddf6415defae&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">## 调用</span></span><br><span class="line">EVALSHA <span class="string">&quot;69ca329ae4744a8b509f9daefea0ddf6415defae&quot;</span> 1 rate:1001 5 60000 1705000003000</span><br></pre></td></tr></table></figure><h4 id="SpringBoot-调用-Script">SpringBoot 调用 Script</h4><ul class="lvl-0"><li class="lvl-2"><p>直接运行脚本</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">lua</span> <span class="operator">=</span> <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">                脚本见上文</span></span><br><span class="line"><span class="string">            &quot;&quot;&quot;</span>;</span><br><span class="line"><span class="type">Long</span> <span class="variable">count</span> <span class="operator">=</span> redisTemplate.execute((RedisCallback&lt;Long&gt;) connection -&gt;</span><br><span class="line">        connection.scriptingCommands().eval(</span><br><span class="line">                lua.getBytes(),</span><br><span class="line">                ReturnType.INTEGER,</span><br><span class="line">                <span class="number">1</span>,</span><br><span class="line">                <span class="string">&quot;rate:1001&quot;</span>.getBytes(),</span><br><span class="line">                <span class="string">&quot;5&quot;</span>.getBytes(),</span><br><span class="line">                <span class="string">&quot;60000&quot;</span>.getBytes(),</span><br><span class="line">                <span class="string">&quot;1705000003000&quot;</span>.getBytes()</span><br><span class="line">        )</span><br><span class="line">);</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>预热脚本</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">lua</span> <span class="operator">=</span> <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">                脚本见上文</span></span><br><span class="line"><span class="string">            &quot;&quot;&quot;</span>;</span><br><span class="line"><span class="comment">// 预热脚本</span></span><br><span class="line"><span class="keyword">final</span> <span class="type">String</span> <span class="variable">sha</span> <span class="operator">=</span> redisTemplate.execute((RedisCallback&lt;String&gt;) connection -&gt;</span><br><span class="line">        connection.scriptingCommands().scriptLoad(lua.getBytes())</span><br><span class="line">);</span><br><span class="line"><span class="comment">// 执行脚本</span></span><br><span class="line"><span class="keyword">final</span> <span class="type">Long</span> <span class="variable">count</span> <span class="operator">=</span> redisTemplate.execute((RedisCallback&lt;Long&gt;) connection -&gt;</span><br><span class="line">        connection.scriptingCommands().evalSha(</span><br><span class="line">                sha.getBytes(),</span><br><span class="line">                ReturnType.INTEGER,</span><br><span class="line">                <span class="number">1</span>,</span><br><span class="line">                <span class="string">&quot;rate:1001&quot;</span>.getBytes(),</span><br><span class="line">                <span class="string">&quot;5&quot;</span>.getBytes(),</span><br><span class="line">                <span class="string">&quot;60000&quot;</span>.getBytes(),</span><br><span class="line">                <span class="string">&quot;1705000003000&quot;</span>.getBytes()</span><br><span class="line">        )</span><br><span class="line">);</span><br></pre></td></tr></table></figure><h3 id="Function-使用示例">Function 使用示例</h3><ul class="lvl-0"><li class="lvl-2"><p>Function 源码</p></li></ul><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">#!lua name=ratelimit</span><br><span class="line"></span><br><span class="line">redis.register_function(<span class="string">&#x27;allow&#x27;</span>, <span class="function"><span class="keyword">function</span><span class="params">(keys, args)</span></span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">local</span> rateKey = keys[<span class="number">1</span>]</span><br><span class="line">    <span class="keyword">local</span> maxReq  = <span class="built_in">tonumber</span>(args[<span class="number">1</span>])</span><br><span class="line">    <span class="keyword">local</span> window  = <span class="built_in">tonumber</span>(args[<span class="number">2</span>])</span><br><span class="line">    <span class="keyword">local</span> now     = <span class="built_in">tonumber</span>(args[<span class="number">3</span>])</span><br><span class="line"></span><br><span class="line">    <span class="keyword">local</span> minTime = now - window</span><br><span class="line"></span><br><span class="line">    <span class="comment">-- 1. 清理过期请求</span></span><br><span class="line">    redis.call(<span class="string">&quot;ZREMRANGEBYSCORE&quot;</span>, rateKey, <span class="number">0</span>, minTime)</span><br><span class="line"></span><br><span class="line">    <span class="comment">-- 2. 当前请求数</span></span><br><span class="line">    <span class="keyword">local</span> count = <span class="built_in">tonumber</span>(redis.call(<span class="string">&quot;ZCARD&quot;</span>, rateKey))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> count &gt;= maxReq <span class="keyword">then</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;notOk&#x27;</span></span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">-- 3. 记录请求</span></span><br><span class="line">    redis.call(<span class="string">&quot;ZADD&quot;</span>, rateKey, now, now)</span><br><span class="line"></span><br><span class="line">    <span class="comment">-- 4. 设置自动过期</span></span><br><span class="line">    redis.call(<span class="string">&quot;PEXPIRE&quot;</span>, rateKey, window)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;isOk&#x27;</span></span><br><span class="line"><span class="keyword">end</span>)</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>加载 Function</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 加载 Function</span></span><br><span class="line">127.0.0.1:6379&gt; FUNCTION LOAD <span class="string">&quot;#!lua name=ratelimit \n redis.register_function(&#x27;allow&#x27;, function(keys, args) local rateKey = keys[1] local maxReq  = tonumber(args[1]) local window  = tonumber(args[2]) local now     = tonumber(args[3]) local minTime = now - window redis.call(\&quot;ZREMRANGEBYSCORE\&quot;, rateKey, 0, minTime) local count = tonumber(redis.call(\&quot;ZCARD\&quot;, rateKey)) if count &gt;= maxReq then return count end redis.call(\&quot;ZADD\&quot;, rateKey, now, now) redis.call(\&quot;PEXPIRE\&quot;, rateKey, window) return count+1 end)&quot;</span></span><br><span class="line"><span class="comment">## 输出 library_name</span></span><br><span class="line"><span class="string">&quot;ratelimit&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 列出 Function</span></span><br><span class="line">127.0.0.1:6379&gt; FUNCTION LIST</span><br><span class="line">1) 1) <span class="string">&quot;library_name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;ratelimit&quot;</span></span><br><span class="line">   3) <span class="string">&quot;engine&quot;</span></span><br><span class="line">   4) <span class="string">&quot;LUA&quot;</span></span><br><span class="line">   5) <span class="string">&quot;functions&quot;</span></span><br><span class="line">   6) 1) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">         2) <span class="string">&quot;allow&quot;</span></span><br><span class="line">         3) <span class="string">&quot;description&quot;</span></span><br><span class="line">         4) (nil)</span><br><span class="line">         5) <span class="string">&quot;flags&quot;</span></span><br><span class="line">         6) (empty array)</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>调用 Function</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 调用 Function: FCALL function_name num_keys key [key ...] arg [arg ...]</span></span><br><span class="line">FCALL allow 1 rate:1001 5 60000 1705000000000</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>删除 Function</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">FUNCTION DELETE ratelimit</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line"><span class="string">&quot;OK&quot;</span></span><br></pre></td></tr></table></figure><h4 id="SpringBoot-调用-Function">SpringBoot 调用 Function</h4><ul class="lvl-0"><li class="lvl-2"><p>目前 SpringBoot 仅支持 Lua 脚本，不支持 Function，需要自己封装代码</p></li><li class="lvl-2"><p>Function 的返回值仅支持返回字符串，其它类型会抛异常，这是因为<code>Lettuce + RedisTemplate</code> 对 FCALL 的返回类型推断存在缺陷，但对 byte[]（字符串）是稳定可用的。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">lua</span> <span class="operator">=</span> <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">                脚本见上文</span></span><br><span class="line"><span class="string">            &quot;&quot;&quot;</span>;</span><br><span class="line"><span class="type">String</span> <span class="variable">lib_name</span> <span class="operator">=</span> <span class="string">&quot;ratelimit&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 删除 Function</span></span><br><span class="line">redisTemplate.execute((RedisCallback&lt;Object&gt;) connection -&gt;</span><br><span class="line">        connection.execute(<span class="string">&quot;FUNCTION&quot;</span>, <span class="string">&quot;DELETE&quot;</span>.getBytes(), lib_name.getBytes())</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 注册 Function</span></span><br><span class="line">redisTemplate.execute((RedisCallback&lt;Object&gt;) connection -&gt;</span><br><span class="line">        connection.execute(<span class="string">&quot;FUNCTION&quot;</span>, <span class="string">&quot;LOAD&quot;</span>.getBytes(), lua.getBytes())</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 调用 Function</span></span><br><span class="line"><span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> redisTemplate.execute((RedisCallback&lt;Object&gt;) connection -&gt;</span><br><span class="line">        connection.execute(<span class="string">&quot;FCALL&quot;</span>,</span><br><span class="line">                <span class="string">&quot;allow&quot;</span>.getBytes(),</span><br><span class="line">                <span class="string">&quot;1&quot;</span>.getBytes(),</span><br><span class="line">                <span class="string">&quot;rate:1001&quot;</span>.getBytes(),</span><br><span class="line">                <span class="string">&quot;5&quot;</span>.getBytes(),</span><br><span class="line">                <span class="string">&quot;60000&quot;</span>.getBytes(),</span><br><span class="line">                <span class="string">&quot;1705000000000&quot;</span>.getBytes())</span><br><span class="line">);</span><br><span class="line"><span class="comment">// 处理返回结果，RedisTemplate 对于 unknown 的 命令 统一返回 byte[]</span></span><br><span class="line"><span class="keyword">if</span> (result <span class="keyword">instanceof</span> <span class="type">byte</span>[] bytes) &#123;</span><br><span class="line">    System.out.println(<span class="keyword">new</span> <span class="title class_">String</span>(bytes, StandardCharsets.UTF_8));</span><br><span class="line">&#125;<span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;Unexpected FCALL return type: &quot;</span> + result.getClass());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/11/redis7-command-05-script-function/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/11/redis7-command-05-script-function/"/>
    <published>2026-01-11T15:40:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文基于<code>redis-7.4.7</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2"><a href="https://www.runoob.com/lua/lua-tutorial.html">Lua语法参考</a></li>
</ul>]]>
    </summary>
    <title>Redis 命令详解：Scripting / Functions 命令</title>
    <updated>2026-01-16T09:28:51.229Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文基于<code>redis-7.4.7</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li></ul><span id="more"></span><h2 id="Connection-Management-简介">Connection Management 简介</h2><ul class="lvl-0"><li class="lvl-2">Connection Management（连接管理） 是 Redis 用于管理客户端与服务器之间连接生命周期、连接状态、协议协商、身份认证、连接行为控制以及连接可观测性的一整套机制与命令集合。</li><li class="lvl-2">在 Redis 内部，每一个客户端连接都会被抽象为一个 client 结构体，包含：</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Socket 连接信息（IP、端口、FD）</span><br><span class="line">协议版本（RESP2 / RESP3）</span><br><span class="line">用户身份（ACL User）</span><br><span class="line">连接名称（Client Name）</span><br><span class="line">阻塞状态（Blocked / Unblocked）</span><br><span class="line">输入输出缓冲区</span><br><span class="line">命令统计信息</span><br><span class="line">Tracking / Caching 状态</span><br><span class="line">最近活动时间</span><br></pre></td></tr></table></figure><h2 id="Connection-Management-命令详解">Connection Management 命令详解</h2><h3 id="一、连接建立-协议协商-基础通信">一、连接建立 / 协议协商 / 基础通信</h3><ul class="lvl-0"><li class="lvl-2"><p>用于客户端与 Redis 建立连接、确认协议版本、心跳检测。</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th><th>典型用途</th></tr></thead><tbody><tr><td>HELLO</td><td><code>HELLO [protover] [AUTH user pass] [SETNAME name]</code></td><td>protover：协议版本（2/3）<br>AUTH：连接时认证<br>SETNAME：设置连接名</td><td><code>HELLO 3 AUTH default pwd SETNAME app-1</code></td><td>协议协商、一次性完成认证</td></tr><tr><td>AUTH</td><td><code>AUTH [username] password</code></td><td>用户名可选（ACL 模式）</td><td><code>AUTH app secret</code></td><td>登录认证</td></tr><tr><td>PING</td><td><code>PING [message]</code></td><td>message：可选回显内容</td><td><code>PING</code></td><td>心跳检测</td></tr><tr><td>ECHO</td><td><code>ECHO message</code></td><td>message：任意字符串</td><td><code>ECHO hello</code></td><td>连通性测试</td></tr><tr><td>QUIT</td><td><code>QUIT</code></td><td>无</td><td><code>QUIT</code></td><td>关闭连接</td></tr></tbody></table><h3 id="二、客户端身份与连接信息">二、客户端身份与连接信息</h3><ul class="lvl-0"><li class="lvl-2"><p>用于识别和查询客户端连接状态。</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th><th>典型用途</th></tr></thead><tbody><tr><td>CLIENT ID</td><td><code>CLIENT ID</code></td><td>返回当前连接 ID</td><td><code>CLIENT ID</code></td><td>连接唯一标识</td></tr><tr><td>CLIENT GETNAME</td><td><code>CLIENT GETNAME</code></td><td>获取连接名称</td><td><code>CLIENT GETNAME</code></td><td>连接识别</td></tr><tr><td>CLIENT SETNAME</td><td><code>CLIENT SETNAME name</code></td><td>设置连接名称</td><td><code>CLIENT SETNAME order-service</code></td><td>连接可观测性</td></tr><tr><td>CLIENT INFO</td><td><code>CLIENT INFO</code></td><td>返回当前客户端详细信息</td><td><code>CLIENT INFO</code></td><td>调试连接状态</td></tr><tr><td>CLIENT LIST</td><td><code>CLIENT LIST [TYPE type] [ID id]</code></td><td>列出所有客户端</td><td><code>CLIENT LIST TYPE normal</code></td><td>运维诊断</td></tr><tr><td>CLIENT GETREDIR</td><td><code>CLIENT GETREDIR</code></td><td>返回客户端重定向状态</td><td><code>CLIENT GETREDIR</code></td><td>集群调试</td></tr></tbody></table><h3 id="三、客户端连接控制-生命周期管理">三、客户端连接控制 / 生命周期管理</h3><ul class="lvl-0"><li class="lvl-2"><p>用于管理其他客户端连接。</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th><th>典型用途</th></tr></thead><tbody><tr><td>CLIENT KILL</td><td><code>CLIENT KILL &lt;filter&gt;</code></td><td>按条件关闭连接</td><td><code>CLIENT KILL TYPE normal</code></td><td>清理异常连接</td></tr><tr><td>CLIENT UNBLOCK</td><td><code>CLIENT UNBLOCK client-id [TIMEOUT|ERROR]</code></td><td>解除阻塞客户端</td><td><code>CLIENT UNBLOCK 1234</code></td><td>解死锁</td></tr><tr><td>CLIENT PAUSE</td><td><code>CLIENT PAUSE timeout [WRITE|ALL]</code></td><td>暂停客户端命令处理</td><td><code>CLIENT PAUSE 1000 WRITE</code></td><td>流量削峰</td></tr><tr><td>CLIENT UNPAUSE</td><td><code>CLIENT UNPAUSE</code></td><td>恢复处理</td><td><code>CLIENT UNPAUSE</code></td><td>恢复服务</td></tr><tr><td>CLIENT REPLY</td><td><code>CLIENT REPLY ON|OFF|SKIP</code></td><td>控制是否返回响应</td><td><code>CLIENT REPLY OFF</code></td><td>管道优化</td></tr></tbody></table><h3 id="四、客户端行为控制（缓存、访问、跟踪）">四、客户端行为控制（缓存、访问、跟踪）</h3><ul class="lvl-0"><li class="lvl-2"><p>用于控制客户端与 Redis 的交互行为。</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th><th>典型用途</th></tr></thead><tbody><tr><td>CLIENT CACHING</td><td><code>CLIENT CACHING YES|NO</code></td><td>开启/关闭客户端缓存</td><td><code>CLIENT CACHING YES</code></td><td>客户端缓存优化</td></tr><tr><td>CLIENT TRACKING</td><td><code>CLIENT TRACKING ON [options]</code></td><td>开启 key 失效跟踪</td><td><code>CLIENT TRACKING ON</code></td><td>客户端缓存一致性</td></tr><tr><td>CLIENT TRACKINGINFO</td><td><code>CLIENT TRACKINGINFO</code></td><td>查询跟踪状态</td><td><code>CLIENT TRACKINGINFO</code></td><td>调试</td></tr><tr><td>CLIENT NO-TOUCH</td><td><code>CLIENT NO-TOUCH ON|OFF</code></td><td>禁止更新 LRU/LFU</td><td><code>CLIENT NO-TOUCH ON</code></td><td>热度统计控制</td></tr><tr><td>CLIENT NO-EVICT</td><td><code>CLIENT NO-EVICT ON|OFF</code></td><td>禁止触发淘汰</td><td><code>CLIENT NO-EVICT ON</code></td><td>防止误淘汰</td></tr><tr><td>CLIENT SETINFO</td><td><code>CLIENT SETINFO LIB-NAME name</code></td><td>设置客户端库信息</td><td><code>CLIENT SETINFO LIB-NAME my-sdk</code></td><td>可观测性</td></tr></tbody></table><h3 id="五、协议行为与响应控制">五、协议行为与响应控制</h3><ul class="lvl-0"><li class="lvl-2"><p>用于调试和高级客户端行为控制。</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th><th>典型用途</th></tr></thead><tbody><tr><td>CLIENT REPLY</td><td><code>CLIENT REPLY ON|OFF|SKIP</code></td><td>控制是否接收响应</td><td><code>CLIENT REPLY SKIP</code></td><td>Pipeline 优化</td></tr><tr><td>CLIENT GETREDIR</td><td><code>CLIENT GETREDIR</code></td><td>获取重定向信息</td><td><code>CLIENT GETREDIR</code></td><td>集群调试</td></tr></tbody></table>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/11/redis7-command-04-connection-management/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/11/redis7-command-04-connection-management/"/>
    <published>2026-01-11T15:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文基于<code>redis-7.4.7</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
</ul>]]>
    </summary>
    <title>Redis 命令详解：Connection Management 命令</title>
    <updated>2026-01-11T05:42:18.496Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文基于<code>redis-7.4.7</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li></ul><span id="more"></span><h2 id="Server-Management-简介">Server Management 简介</h2><ul class="lvl-0"><li class="lvl-2">Server Management Commands 是用于管理 Redis 服务器实例本身运行状态、资源、配置、安全、复制、持久化、模块和诊断能力的一组系统级命令。</li><li class="lvl-2">不建议在业务代码中调用。</li><li class="lvl-2">不操作业务数据内容，而是操作：</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Redis 服务进程状态</span><br><span class="line">内存 / CPU / IO</span><br><span class="line">持久化机制</span><br><span class="line">主从复制 / 高可用</span><br><span class="line">安全权限</span><br><span class="line">配置项</span><br><span class="line">模块生命周期</span><br><span class="line">性能诊断</span><br></pre></td></tr></table></figure><h2 id="Server-Management-命令详解">Server Management 命令详解</h2><h3 id="一、ACL-权限与安全管理">一、ACL 权限与安全管理</h3><ul class="lvl-0"><li class="lvl-2"><p>关于 ACL 权限的具体说明，请查看 <a href="/2025/12/07/redis7-acl/" title="Redis 7 + ACL 简介">Redis 7 + ACL 简介</a></p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>ACL CAT</td><td><code>ACL CAT [category]</code></td><td>查看命令分类或某分类下命令</td><td><code>ACL CAT admin</code></td></tr><tr><td>ACL USERS</td><td><code>ACL USERS</code></td><td>列出所有用户</td><td><code>ACL USERS</code></td></tr><tr><td>ACL WHOAMI</td><td><code>ACL WHOAMI</code></td><td>显示当前连接用户</td><td><code>ACL WHOAMI</code></td></tr><tr><td>ACL GETUSER</td><td><code>ACL GETUSER username</code></td><td>查询用户权限</td><td><code>ACL GETUSER app</code></td></tr><tr><td>ACL SETUSER</td><td><code>ACL SETUSER username [rule ...]</code></td><td>创建/修改用户规则</td><td><code>ACL SETUSER app on &gt;pwd ~* +get</code></td></tr><tr><td>ACL DELUSER</td><td><code>ACL DELUSER username [username ...]</code></td><td>删除用户</td><td><code>ACL DELUSER test</code></td></tr><tr><td>ACL LIST</td><td><code>ACL LIST</code></td><td>列出 ACL 配置规则</td><td><code>ACL LIST</code></td></tr><tr><td>ACL LOAD</td><td><code>ACL LOAD</code></td><td>从配置文件加载 ACL</td><td><code>ACL LOAD</code></td></tr><tr><td>ACL SAVE</td><td><code>ACL SAVE</code></td><td>将 ACL 写入磁盘</td><td><code>ACL SAVE</code></td></tr><tr><td>ACL LOG</td><td><code>ACL LOG [count|RESET]</code></td><td>查看权限拒绝日志</td><td><code>ACL LOG 10</code></td></tr><tr><td>ACL DRYRUN</td><td><code>ACL DRYRUN username command [args...]</code></td><td>模拟权限校验</td><td><code>ACL DRYRUN app GET k1</code></td></tr><tr><td>ACL GENPASS</td><td><code>ACL GENPASS [bits]</code></td><td>生成随机密码</td><td><code>ACL GENPASS 128</code></td></tr></tbody></table><h3 id="二、持久化与后台任务">二、持久化与后台任务</h3><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>SAVE</td><td><code>SAVE</code></td><td>同步生成 RDB 快照（阻塞）</td><td><code>SAVE</code></td></tr><tr><td>BGSAVE</td><td><code>BGSAVE</code></td><td>后台生成 RDB</td><td><code>BGSAVE</code></td></tr><tr><td>BGREWRITEAOF</td><td><code>BGREWRITEAOF</code></td><td>重写 AOF 文件</td><td><code>BGREWRITEAOF</code></td></tr><tr><td>LASTSAVE</td><td><code>LASTSAVE</code></td><td>最近一次 RDB 保存时间</td><td><code>LASTSAVE</code></td></tr><tr><td>SHUTDOWN</td><td><code>SHUTDOWN [NOSAVE|SAVE]</code></td><td>关闭 Redis 实例</td><td><code>SHUTDOWN SAVE</code></td></tr></tbody></table><h3 id="三、命令元信息与能力发现">三、命令元信息与能力发现</h3><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>COMMAND</td><td><code>COMMAND</code></td><td>返回所有命令信息</td><td><code>COMMAND</code></td></tr><tr><td>COMMAND COUNT</td><td><code>COMMAND COUNT</code></td><td>返回命令总数</td><td><code>COMMAND COUNT</code></td></tr><tr><td>COMMAND LIST</td><td><code>COMMAND LIST</code></td><td>返回命令列表</td><td><code>COMMAND LIST</code></td></tr><tr><td>COMMAND INFO</td><td><code>COMMAND INFO cmd [cmd ...]</code></td><td>查询命令元数据</td><td><code>COMMAND INFO GET SET</code></td></tr><tr><td>COMMAND DOCS</td><td><code>COMMAND DOCS [cmd ...]</code></td><td>返回命令文档</td><td><code>COMMAND DOCS GET</code></td></tr><tr><td>COMMAND GETKEYS</td><td><code>COMMAND GETKEYS cmd args...</code></td><td>解析命令中的 key</td><td><code>COMMAND GETKEYS MSET a 1 b 2</code></td></tr><tr><td>COMMAND GETKEYSANDFLAGS</td><td><code>COMMAND GETKEYSANDFLAGS cmd args...</code></td><td>返回 key 与访问标志</td><td><code>COMMAND GETKEYSANDFLAGS SET k v</code></td></tr></tbody></table><h3 id="四、配置与运行状态">四、配置与运行状态</h3><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>CONFIG GET</td><td><code>CONFIG GET pattern</code></td><td>查询配置</td><td><code>CONFIG GET maxmemory*</code></td></tr><tr><td>CONFIG SET</td><td><code>CONFIG SET key value</code></td><td>修改配置</td><td><code>CONFIG SET timeout 300</code></td></tr><tr><td>CONFIG RESETSTAT</td><td><code>CONFIG RESETSTAT</code></td><td>重置统计信息</td><td><code>CONFIG RESETSTAT</code></td></tr><tr><td>CONFIG REWRITE</td><td><code>CONFIG REWRITE</code></td><td>重写配置文件</td><td><code>CONFIG REWRITE</code></td></tr><tr><td>INFO</td><td><code>INFO [section]</code></td><td>查看运行状态</td><td><code>INFO memory</code></td></tr><tr><td>DBSIZE</td><td><code>DBSIZE</code></td><td>当前 DB key 数量</td><td><code>DBSIZE</code></td></tr><tr><td>TIME</td><td><code>TIME</code></td><td>返回服务器时间</td><td><code>TIME</code></td></tr><tr><td>LOLWUT</td><td><code>LOLWUT</code></td><td>调试彩蛋命令</td><td><code>LOLWUT</code></td></tr></tbody></table><h3 id="五、数据库管理">五、数据库管理</h3><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>FLUSHDB</td><td><code>FLUSHDB [ASYNC]</code></td><td>清空当前 DB</td><td><code>FLUSHDB ASYNC</code></td></tr><tr><td>FLUSHALL</td><td><code>FLUSHALL [ASYNC]</code></td><td>清空所有 DB</td><td><code>FLUSHALL</code></td></tr><tr><td>SWAPDB</td><td><code>SWAPDB index1 index2</code></td><td>交换两个 DB</td><td><code>SWAPDB 0 1</code></td></tr></tbody></table><h3 id="六、复制、主从、高可用">六、复制、主从、高可用</h3><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>REPLICAOF</td><td><code>REPLICAOF host port</code></td><td>设置为从节点</td><td><code>REPLICAOF 10.0.0.1 6379</code></td></tr><tr><td>SLAVEOF</td><td><code>SLAVEOF host port</code></td><td>REPLICAOF (旧别名)</td><td><code>SLAVEOF NO ONE</code></td></tr><tr><td>SYNC</td><td><code>SYNC</code></td><td>全量复制（旧协议）</td><td><code>SYNC</code></td></tr><tr><td>PSYNC</td><td><code>PSYNC replid offset</code></td><td>增量复制</td><td><code>PSYNC ? -1</code></td></tr><tr><td>REPLCONF</td><td><code>REPLCONF option value</code></td><td>复制参数协商</td><td><code>REPLCONF capa eof</code></td></tr><tr><td>ROLE</td><td><code>ROLE</code></td><td>查询节点角色</td><td><code>ROLE</code></td></tr><tr><td>FAILOVER</td><td><code>FAILOVER [TO host port]</code></td><td>触发主从切换</td><td><code>FAILOVER</code></td></tr><tr><td>RESTORE-ASKING</td><td><code>RESTORE-ASKING</code></td><td>集群迁移辅助</td><td><code>RESTORE-ASKING</code></td></tr></tbody></table><h3 id="七、延迟与慢查询诊断">七、延迟与慢查询诊断</h3><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>LATENCY DOCTOR</td><td><code>LATENCY DOCTOR</code></td><td>自动诊断延迟问题</td><td><code>LATENCY DOCTOR</code></td></tr><tr><td>LATENCY GRAPH</td><td><code>LATENCY GRAPH event</code></td><td>延迟图</td><td><code>LATENCY GRAPH command</code></td></tr><tr><td>LATENCY HISTOGRAM</td><td><code>LATENCY HISTOGRAM event</code></td><td>延迟分布</td><td><code>LATENCY HISTOGRAM command</code></td></tr><tr><td>LATENCY HISTORY</td><td><code>LATENCY HISTORY event</code></td><td>历史记录</td><td><code>LATENCY HISTORY command</code></td></tr><tr><td>LATENCY LATEST</td><td><code>LATENCY LATEST</code></td><td>最近延迟事件</td><td><code>LATENCY LATEST</code></td></tr><tr><td>LATENCY RESET</td><td><code>LATENCY RESET [event]</code></td><td>重置统计</td><td><code>LATENCY RESET</code></td></tr><tr><td>SLOWLOG GET</td><td><code>SLOWLOG GET [n]</code></td><td>获取慢日志</td><td><code>SLOWLOG GET 10</code></td></tr><tr><td>SLOWLOG LEN</td><td><code>SLOWLOG LEN</code></td><td>慢日志条数</td><td><code>SLOWLOG LEN</code></td></tr><tr><td>SLOWLOG RESET</td><td><code>SLOWLOG RESET</code></td><td>清空慢日志</td><td><code>SLOWLOG RESET</code></td></tr><tr><td>MONITOR</td><td><code>MONITOR</code></td><td>实时监听所有命令</td><td><code>MONITOR</code></td></tr></tbody></table><h3 id="八、内存诊断与优化">八、内存诊断与优化</h3><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>MEMORY USAGE</td><td><code>MEMORY USAGE key [SAMPLES n]</code></td><td>key 占用内存</td><td><code>MEMORY USAGE k1</code></td></tr><tr><td>MEMORY STATS</td><td><code>MEMORY STATS</code></td><td>内存统计</td><td><code>MEMORY STATS</code></td></tr><tr><td>MEMORY DOCTOR</td><td><code>MEMORY DOCTOR</code></td><td>内存问题诊断</td><td><code>MEMORY DOCTOR</code></td></tr><tr><td>MEMORY PURGE</td><td><code>MEMORY PURGE</code></td><td>释放碎片</td><td><code>MEMORY PURGE</code></td></tr><tr><td>MEMORY MALLOC-STATS</td><td><code>MEMORY MALLOC-STATS</code></td><td>分配器统计</td><td><code>MEMORY MALLOC-STATS</code></td></tr></tbody></table><h3 id="九、模块管理（Redis-Modules）">九、模块管理（Redis Modules）</h3><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>MODULE LIST</td><td><code>MODULE LIST</code></td><td>查看已加载模块</td><td><code>MODULE LIST</code></td></tr><tr><td>MODULE LOAD</td><td><code>MODULE LOAD path [args...]</code></td><td>加载模块</td><td><code>MODULE LOAD /opt/redisearch.so</code></td></tr><tr><td>MODULE LOADEX</td><td><code>MODULE LOADEX path [CONFIG ...]</code></td><td>扩展加载参数</td><td><code>MODULE LOADEX mod.so CONFIG a 1</code></td></tr><tr><td>MODULE UNLOAD</td><td><code>MODULE UNLOAD name</code></td><td>卸载模块</td><td><code>MODULE UNLOAD redisearch</code></td></tr></tbody></table>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/11/redis7-command-03-server-management/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/11/redis7-command-03-server-management/"/>
    <published>2026-01-11T14:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文基于<code>redis-7.4.7</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
</ul>]]>
    </summary>
    <title>Redis 命令详解：Server Management 命令</title>
    <updated>2026-01-11T05:34:33.747Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文基于<code>redis-7.4.7</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li></ul><span id="more"></span><h2 id="通用命令-简介">通用命令 简介</h2><ul class="lvl-0"><li class="lvl-2">不依赖于具体数据类型，对所有 Key 或 Redis 对象都通用的一组基础管理命令。</li><li class="lvl-2">这些命令作用在 Key 层面、对象元数据层面、存储管理层面、复制与持久化层面，而不是具体的数据结构内容。</li></ul><table><thead><tr><th>特征</th><th>说明</th></tr></thead><tbody><tr><td>与数据类型无关</td><td>不关心 value 是 String、Hash、List、JSON、Bitmap 等</td></tr><tr><td>作用对象是 Key 或对象元数据</td><td>如 TTL、类型、编码、引用计数、是否存在</td></tr><tr><td>管理属性为主</td><td>生命周期、复制、迁移、删除、遍历</td></tr><tr><td>可用于所有 Redis 模块数据</td><td>RedisJSON、RediSearch、TimeSeries 等同样适用</td></tr><tr><td>运维和系统级使用频繁</td><td>监控、迁移、数据治理、容量管理</td></tr></tbody></table><h2 id="通用命令-命令详解">通用命令 命令详解</h2><h3 id="一、Key-生命周期-过期时间管理">一、Key 生命周期 / 过期时间管理</h3><ul class="lvl-0"><li class="lvl-2"><p>用于设置、查询、取消 Key 的过期时间。</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>EXPIRE</td><td><code>EXPIRE key seconds [NX|XX|GT|LT]</code></td><td>seconds：过期秒数<br>NX：仅当 key 没有过期时间时设置<br>XX：仅当 key 已有过期时间时设置<br>GT：仅当新过期时间大于旧值<br>LT：仅当新过期时间小于旧值</td><td><code>EXPIRE user:1 60</code></td></tr><tr><td>PEXPIRE</td><td><code>PEXPIRE key milliseconds [NX|XX|GT|LT]</code></td><td>milliseconds：过期毫秒数</td><td><code>PEXPIRE user:1 1500</code></td></tr><tr><td>EXPIREAT</td><td><code>EXPIREAT key unix_timestamp [NX|XX|GT|LT]</code></td><td>unix_timestamp：秒级时间戳</td><td><code>EXPIREAT user:1 1737000000</code></td></tr><tr><td>PEXPIREAT</td><td><code>PEXPIREAT key unix_milliseconds_timestamp [NX|XX|GT|LT]</code></td><td>毫秒级 Unix 时间戳</td><td><code>PEXPIREAT user:1 1737000000123</code></td></tr><tr><td>TTL</td><td><code>TTL key</code></td><td>返回剩余过期时间（秒）<br>-1：永久 key<br>-2：不存在</td><td><code>TTL user:1</code></td></tr><tr><td>PTTL</td><td><code>PTTL key</code></td><td>返回剩余过期时间（毫秒）</td><td><code>PTTL user:1</code></td></tr><tr><td>EXPIRETIME</td><td><code>EXPIRETIME key</code></td><td>返回过期时间（秒级时间戳）</td><td><code>EXPIRETIME user:1</code></td></tr><tr><td>PEXPIRETIME</td><td><code>PEXPIRETIME key</code></td><td>返回过期时间（毫秒级时间戳）</td><td><code>PEXPIRETIME user:1</code></td></tr><tr><td>PERSIST</td><td><code>PERSIST key</code></td><td>移除过期时间，使 key 永久存在</td><td><code>PERSIST user:1</code></td></tr><tr><td>TOUCH</td><td><code>TOUCH key [key ...]</code></td><td>更新 key 最近访问时间，返回成功更新数量</td><td><code>TOUCH k1 k2</code></td></tr></tbody></table><h3 id="二、Key-查询与遍历">二、Key 查询与遍历</h3><ul class="lvl-0"><li class="lvl-2"><p>用于查询 key 是否存在、类型、批量扫描。</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>EXISTS</td><td><code>EXISTS key [key ...]</code></td><td>返回存在的 key 数量</td><td><code>EXISTS a b c</code></td></tr><tr><td>TYPE</td><td><code>TYPE key</code></td><td>返回 key 的数据类型</td><td><code>TYPE user:1</code></td></tr><tr><td>KEYS</td><td><code>KEYS pattern</code></td><td>按模式匹配所有 key（生产慎用）</td><td><code>KEYS user:*</code></td></tr><tr><td>SCAN</td><td><code>SCAN cursor [MATCH pattern] [COUNT count]</code></td><td>cursor：游标<br>MATCH：匹配模式<br>COUNT：期望返回数量</td><td><code>SCAN 0 MATCH user:* COUNT 100</code></td></tr><tr><td>RANDOMKEY</td><td><code>RANDOMKEY</code></td><td>随机返回一个 key</td><td><code>RANDOMKEY</code></td></tr></tbody></table><h3 id="三、Key-修改-删除-复制-移动">三、Key 修改 / 删除 / 复制 / 移动</h3><ul class="lvl-0"><li class="lvl-2"><p>用于 key 的生命周期与位置管理。</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>DEL</td><td><code>DEL key [key ...]</code></td><td>同步删除 key</td><td><code>DEL k1 k2</code></td></tr><tr><td>UNLINK</td><td><code>UNLINK key [key ...]</code></td><td>异步删除 key（大 key 推荐）</td><td><code>UNLINK bigkey</code></td></tr><tr><td>COPY</td><td><code>COPY source destination [DB db] [REPLACE]</code></td><td>DB：目标数据库<br>REPLACE：覆盖目标</td><td><code>COPY k1 k2 REPLACE</code></td></tr><tr><td>MOVE</td><td><code>MOVE key db</code></td><td>db：目标数据库编号</td><td><code>MOVE user:1 1</code></td></tr><tr><td>RENAME</td><td><code>RENAME key newkey</code></td><td>强制覆盖目标 key</td><td><code>RENAME a b</code></td></tr><tr><td>RENAMENX</td><td><code>RENAMENX key newkey</code></td><td>目标不存在时才重命名</td><td><code>RENAMENX a b</code></td></tr><tr><td>MIGRATE</td><td><code>MIGRATE host port &lt;key | &quot;&quot;&gt; destination-db timeout [COPY] [REPLACE] [AUTH password | AUTH2 username password] [KEYS key [key ...]]</code></td><td>用于跨实例迁移 key</td><td><code>MIGRATE 127.0.0.1 6379 k1 0 5000</code></td></tr></tbody></table><h4 id="MIGRATE-—-命令总览">MIGRATE — 命令总览</h4><ul class="lvl-0"><li class="lvl-2"><p>将一个或多个键 原子性地 从当前 Redis 实例迁移到另一个 Redis 实例的指定数据库。迁移成功后：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">目标实例上一定会有该键</span><br><span class="line">默认源实例上该键将被删除（除非使用 COPY）</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>该命令保证一致性，在迁移期间两个实例都会被阻塞一段时间。</p></li><li class="lvl-2"><p>参数说明表</p></li></ul><table><thead><tr><th>参数</th><th>类型</th><th>含义</th><th>是否必需</th></tr></thead><tbody><tr><td><strong>host</strong></td><td>string</td><td>目标 Redis 实例主机名或 IP</td><td>是</td></tr><tr><td><strong>port</strong></td><td>integer</td><td>目标 Redis 实例端口</td><td>是</td></tr><tr><td><strong>&lt;key | “”&gt;</strong></td><td>string</td><td>单个要迁移的键名，或空字符串用于批量模式</td><td>是</td></tr><tr><td><strong>destination-db</strong></td><td>integer</td><td>目标实例上要写入的数据库索引（0–15）</td><td>是</td></tr><tr><td><strong>timeout</strong></td><td>integer</td><td>最大允许的空闲 I/O 超时时间（毫秒）</td><td>是</td></tr><tr><td><strong>COPY</strong></td><td>keyword</td><td>迁移后不删除源实例上的键</td><td>否</td></tr><tr><td><strong>REPLACE</strong></td><td>keyword</td><td>如果目标实例已存在同名键则覆盖</td><td>否</td></tr><tr><td><strong>AUTH password</strong></td><td>credential</td><td>用于目标实例密码认证（旧 ACL 模式）</td><td>否</td></tr><tr><td><strong>AUTH2 username password</strong></td><td>credential</td><td>用于目标实例 ACL 用户名+密码认证</td><td>否</td></tr><tr><td><strong>KEYS key [key …]</strong></td><td>list</td><td>如果前面的 key 是空字符串，则使用这一组键名批量迁移</td><td>否</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 示例 1：迁移单个 key</span></span><br><span class="line">MIGRATE 192.168.0.100 6379 user:123 0 5000</span><br><span class="line"></span><br><span class="line"><span class="comment"># 示例 2：复制 key 而不删除源</span></span><br><span class="line">MIGRATE 10.0.0.2 6379 cache:session 1 3000 COPY</span><br><span class="line"></span><br><span class="line"><span class="comment"># 示例 3：覆盖目标已有 key</span></span><br><span class="line">MIGRATE 10.0.0.5 6379 app:data 2 2000 REPLACE</span><br><span class="line"></span><br><span class="line"><span class="comment"># 示例 4：迁移多个 key</span></span><br><span class="line">MIGRATE 10.0.0.10 6379 <span class="string">&quot;&quot;</span> 0 8000 KEYS orders:1 orders:2 orders:3</span><br><span class="line"></span><br><span class="line"><span class="comment"># 示例 5：带目标认证</span></span><br><span class="line">MIGRATE 10.0.0.20 6379 user:token 1 5000 AUTH s3cr3t REPLACE</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>典型使用场景</p></li></ul><table><thead><tr><th>场景</th><th>应用举例</th></tr></thead><tbody><tr><td>单节点间数据迁移</td><td>从 dev Redis 到 prod Redis</td></tr><tr><td>大库数据切分</td><td>数据重分片</td></tr><tr><td>集群迁移前准备</td><td>手动分配 slot 关联 key</td></tr><tr><td>灾备数据同步</td><td>复制业务数据库的部分 key</td></tr></tbody></table><h3 id="四、序列化-备份-恢复">四、序列化 / 备份 / 恢复</h3><ul class="lvl-0"><li class="lvl-2"><p>用于数据导出和导入。</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>DUMP</td><td><code>DUMP key</code></td><td>返回序列化后的二进制值</td><td><code>DUMP user:1</code></td></tr><tr><td>RESTORE</td><td><code>RESTORE key ttl serialized-value [REPLACE]</code></td><td>ttl：毫秒<br>serialized-value：DUMP 输出</td><td><code>RESTORE user:2 0 &quot;&lt;dump&gt;&quot; REPLACE</code></td></tr></tbody></table><h3 id="五、排序相关">五、排序相关</h3><ul class="lvl-0"><li class="lvl-2"><p>对集合数据进行排序。</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>SORT</td><td><code>SORT key [BY pattern] [LIMIT offset count] [GET pattern ...] [ASC|DESC] [ALPHA] [STORE dest]</code></td><td>支持排序、分页、映射、存储</td><td><code>SORT mylist ASC STORE sorted:list</code></td></tr><tr><td>SORT_RO</td><td><code>SORT_RO key ...</code></td><td>只读排序（不会修改数据）</td><td><code>SORT_RO mylist DESC</code></td></tr></tbody></table><h3 id="六、对象内部信息（调试-运维）">六、对象内部信息（调试 / 运维）</h3><ul class="lvl-0"><li class="lvl-2"><p>用于诊断 Redis 内部对象状态。</p></li></ul><table><thead><tr><th>命令</th><th>语法</th><th>参数说明</th><th>示例</th></tr></thead><tbody><tr><td>OBJECT ENCODING</td><td><code>OBJECT ENCODING key</code></td><td>返回内部编码方式</td><td><code>OBJECT ENCODING user:1</code></td></tr><tr><td>OBJECT FREQ</td><td><code>OBJECT FREQ key</code></td><td>返回 LFU 访问频率</td><td><code>OBJECT FREQ user:1</code></td></tr><tr><td>OBJECT IDLETIME</td><td><code>OBJECT IDLETIME key</code></td><td>返回空闲时间（秒）</td><td><code>OBJECT IDLETIME user:1</code></td></tr><tr><td>OBJECT REFCOUNT</td><td><code>OBJECT REFCOUNT key</code></td><td>返回引用计数</td><td><code>OBJECT REFCOUNT user:1</code></td></tr></tbody></table><h3 id="七、复制一致性-持久化确认">七、复制一致性 / 持久化确认</h3><ul class="lvl-0"><li class="lvl-2"><p>主要用于 写入可靠性保证、强一致性场景。</p></li></ul><h4 id="7-1-WAIT-——-等待副本确认">7.1 WAIT —— 等待副本确认</h4><table><thead><tr><th>项目</th><th>内容</th></tr></thead><tbody><tr><td>命令</td><td>WAIT</td></tr><tr><td>语法</td><td><code>WAIT numreplicas timeout</code></td></tr><tr><td>参数说明</td><td>numreplicas：需要确认写入的副本数量<br>timeout：最大等待时间（毫秒）</td></tr><tr><td>返回值</td><td>实际确认的副本数量</td></tr><tr><td>作用</td><td>阻塞当前客户端，直到之前的写命令被至少 numreplicas 个副本确认</td></tr><tr><td>典型场景</td><td>强一致写、主从同步确认、金融类写入</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">SET order:1 PAID</span><br><span class="line">WAIT 2 5000</span><br><span class="line"><span class="comment"># 含义：</span></span><br><span class="line"><span class="comment">#     等待至少 2 个副本确认该写入。</span></span><br><span class="line"><span class="comment">#     最多等待 5 秒。</span></span><br><span class="line"><span class="comment">#     如果 5 秒内只确认 1 个副本，则返回 1。</span></span><br><span class="line"><span class="comment">#     返回0，则表示没有副本确认该写入。</span></span><br></pre></td></tr></table></figure><h4 id="7-2-WAITAOF-——-等待-AOF-和副本持久化确认">7.2 WAITAOF —— 等待 AOF 和副本持久化确认</h4><ul class="lvl-0"><li class="lvl-2"><p>Redis 7+ 新增，用于保证 本地 AOF fsync + 副本同步 的写入可靠性。</p></li></ul><table><thead><tr><th>项目</th><th>内容</th></tr></thead><tbody><tr><td>命令</td><td>WAITAOF</td></tr><tr><td>语法</td><td><code>WAITAOF numlocal numreplicas timeout</code></td></tr><tr><td>参数说明</td><td>numlocal：本地 AOF fsync 确认数量（0/1）<br>numreplicas：副本确认数量<br>timeout：最大等待时间（毫秒）</td></tr><tr><td>返回值</td><td>数组：[local_acks, replica_acks]</td></tr><tr><td>作用</td><td>阻塞直到写入被本地 AOF 持久化和/或副本确认</td></tr><tr><td>典型场景</td><td>金融交易、强持久化一致性要求</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>参数语义说明</p></li></ul><table><thead><tr><th>参数</th><th>含义</th></tr></thead><tbody><tr><td>numlocal = 1</td><td>等待本地 AOF fsync 完成</td></tr><tr><td>numlocal = 0</td><td>不要求本地 fsync</td></tr><tr><td>numreplicas</td><td>等待副本确认数量</td></tr><tr><td>timeout</td><td>超时时间（毫秒）</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例 1：要求本地 AOF 持久化</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">SET pay:1001 SUCCESS</span><br><span class="line">WAITAOF 1 0 3000</span><br><span class="line"><span class="comment"># 含义：</span></span><br><span class="line"><span class="comment">#     必须确认 写入已经 fsync 到本地 AOF 文件。</span></span><br><span class="line"><span class="comment">#     不关心副本同步。</span></span><br><span class="line"><span class="comment">#     超时 3 秒。</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 2：同时要求 AOF + 副本确认</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">SET pay:1002 SUCCESS</span><br><span class="line">WAITAOF 1 2 5000</span><br><span class="line"><span class="comment"># 含义：</span></span><br><span class="line"><span class="comment">#     必须确认本地 AOF 已 fsync。</span></span><br><span class="line"><span class="comment">#     至少 2 个副本确认。</span></span><br><span class="line"><span class="comment">#     最大等待 5 秒。</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例返回值</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">1) (<span class="built_in">integer</span>) 1   <span class="comment"># 本地 fsync 确认数量</span></span><br><span class="line">2) (<span class="built_in">integer</span>) 2   <span class="comment"># 副本确认数量</span></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/11/redis7-command-02-generic/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/11/redis7-command-02-generic/"/>
    <published>2026-01-11T13:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文基于<code>redis-7.4.7</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
</ul>]]>
    </summary>
    <title>Redis 命令详解：通用命令</title>
    <updated>2026-01-12T03:38:07.363Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文基于<code>redis-7.4.7</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li></ul><span id="more"></span><h2 id="Redis-Transaction-简介">Redis Transaction 简介</h2><ul class="lvl-0"><li class="lvl-2">最关键的核心点是：Redis 的事务模型和关系型数据库（如 MySQL）的事务模型有本质区别。</li><li class="lvl-2">关系型数据库事务：强调 ACID（原子性、一致性、隔离性、持久性），特别是在执行过程中，命令会看到一致的数据库状态（通过锁或MVCC），并且可以回滚。</li><li class="lvl-2">Redis 事务：主要目的是将多个命令打包，按顺序、连续、排他地执行。它更接近于一个批量执行和乐观锁的机制。它不提供回滚功能（只有命令入队时的语法检查，没有执行时的错误回滚）。</li></ul><h2 id="事务-Transaction-命令详解">事务 Transaction 命令详解</h2><ul class="lvl-0"><li class="lvl-2"><p>Redis 事务命令总览表</p></li></ul><table><thead><tr><th>命令</th><th>作用</th><th>所处阶段</th><th>返回值</th><th>典型使用场景</th></tr></thead><tbody><tr><td><strong>MULTI</strong></td><td>开启一个事务，之后的命令进入队列</td><td>事务开始</td><td><code>OK</code></td><td>批量原子执行多个命令</td></tr><tr><td><strong>EXEC</strong></td><td>执行事务队列中的所有命令</td><td>事务提交</td><td>数组（每个命令的执行结果）或 <code>nil</code></td><td>提交事务</td></tr><tr><td><strong>DISCARD</strong></td><td>放弃事务队列中的所有命令</td><td>事务取消</td><td><code>OK</code></td><td>回滚未执行的事务</td></tr><tr><td><strong>WATCH key [key …]</strong></td><td>监控一个或多个 key 是否被修改（乐观锁）</td><td>事务前</td><td><code>OK</code></td><td>并发控制、防止覆盖更新</td></tr><tr><td><strong>UNWATCH</strong></td><td>取消对所有 key 的监控</td><td>事务前 / 中</td><td><code>OK</code></td><td>主动释放监控</td></tr></tbody></table><h3 id="MULTI-EXEC-——-基本事务示例">MULTI / EXEC —— 基本事务示例</h3><ul class="lvl-0"><li class="lvl-2"><p>可以在 MULTI 和 EXEC 之间使用的命令</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">几乎所有 “写操作” 和 “只读操作” 命令都可以放入 MULTI...EXEC 块中。例如：</span><br><span class="line">    字符串操作：SET, GET, INCR, MSET</span><br><span class="line">    哈希操作：HSET, HGET, HINCRBY</span><br><span class="line">    列表操作：LPUSH, RPOP, LRANGE</span><br><span class="line">    集合操作：SADD, SREM, SMEMBERS</span><br><span class="line">    有序集合操作：ZADD, ZRANGE, ZREM</span><br><span class="line">    流操作（Redis 5.0+）：XADD, XREADGROUP</span><br><span class="line">    地理空间、位图等：所有相关操作命令。</span><br><span class="line">    管理类命令：DEL, EXPIRE, PERSIST 等。</span><br><span class="line">一个重要的特例：WATCH 命令</span><br><span class="line">    WATCH 命令用于实现乐观锁，它必须在 MULTI 命令之前执行，用来监视一个或多个键。</span><br><span class="line">阻塞命令在事务中是被禁止的</span><br><span class="line">    如 BLPOP， BRPOP， BRPOPLPUSH， BZPOPMIN， BZPOPMAX， XREAD， XREADGROUP（带阻塞选项），因为事务要求所有命令被一次性入队，而阻塞命令需要等待数据到达，这会阻塞整个服务器，导致死锁。Redis 会直接返回错误。</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例：原子递增两个计数器</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; MULTI</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; INCR counter:a</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; INCR counter:b</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; EXEC</span><br><span class="line">1) (<span class="built_in">integer</span>) 1</span><br><span class="line">2) (<span class="built_in">integer</span>) 1</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>说明</p><ul class="lvl-2"><li class="lvl-6">MULTI 后，所有命令只会进入队列，返回 QUEUED，不会立即执行。</li><li class="lvl-6">EXEC 时才真正执行。</li><li class="lvl-6">Redis 保证：<ul class="lvl-4"><li class="lvl-10">命令顺序执行</li><li class="lvl-10">执行期间不会被其他客户端插入命令</li></ul></li><li class="lvl-6">但不支持回滚（某条命令失败，不会自动撤销已执行的命令）。</li></ul></li></ul><h3 id="DISCARD-——-放弃事务">DISCARD —— 放弃事务</h3><ul class="lvl-0"><li class="lvl-2"><p>示例：取消事务</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; MULTI</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; SET k1 v1</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; SET k2 v2</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; DISCARD</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; GET k1</span><br><span class="line">(nil)</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>说明</p><ul class="lvl-2"><li class="lvl-6">DISCARD 会：<ul class="lvl-4"><li class="lvl-10">清空事务队列</li><li class="lvl-10">退出事务状态</li></ul></li><li class="lvl-6">队列中的命令 不会被执行。</li></ul></li></ul><h3 id="WATCH-EXEC-——-乐观锁示例">WATCH / EXEC —— 乐观锁示例</h3><ul class="lvl-0"><li class="lvl-2"><p>乐观锁事务模板</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">WATCH key</span><br><span class="line">读取并校验数据</span><br><span class="line">MULTI</span><br><span class="line">  写操作...</span><br><span class="line">EXEC</span><br><span class="line">如果返回 nil → 重试</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>场景：实现一个安全的余额扣减逻辑，如果余额在事务执行前被别人修改，则放弃本次事务。</p></li><li class="lvl-2"><p>客户端 A</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; SET balance 100</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; WATCH balance</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; GET balance</span><br><span class="line"><span class="string">&quot;100&quot;</span></span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; MULTI</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; DECRBY balance 30</span><br><span class="line">QUEUED</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>客户端 B（并发修改）</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; INCRBY balance 50</span><br><span class="line">(<span class="built_in">integer</span>) 150</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>客户端 A 提交事务</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; EXEC</span><br><span class="line">(nil)</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>说明</p><ul class="lvl-2"><li class="lvl-6">因为 balance 在 WATCH 之后被客户端 B 修改过：<ul class="lvl-4"><li class="lvl-10">Redis 判定监控失败</li><li class="lvl-10">EXEC 返回 nil</li><li class="lvl-10">事务不会执行</li></ul></li><li class="lvl-6">这是典型的 乐观锁（CAS）机制。</li></ul></li></ul><h3 id="UNWATCH-——-取消监控">UNWATCH —— 取消监控</h3><ul class="lvl-0"><li class="lvl-2"><p>示例：取消 WATCH 监控</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; WATCH k1 k2</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; UNWATCH</span><br><span class="line">OK</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>说明</p><ul class="lvl-2"><li class="lvl-6">取消所有被监控的 key。</li><li class="lvl-6">常见用途：<ul class="lvl-4"><li class="lvl-10">逻辑判断失败，不再继续事务</li><li class="lvl-10">主动释放监控，避免误触发事务失败</li></ul></li><li class="lvl-6">如果执行了 EXEC 或 DISCARD，Redis 会自动 UNWATCH。</li></ul></li></ul><h2 id="关键行为总结（工程视角）">关键行为总结（工程视角）</h2><table><thead><tr><th>维度</th><th>行为</th></tr></thead><tbody><tr><td>原子性</td><td><code>EXEC</code> 内命令顺序执行，不会被其他客户端插入</td></tr><tr><td>隔离性</td><td>不是数据库级事务隔离，仅保证执行期串行</td></tr><tr><td>回滚能力</td><td>❌ 不支持回滚</td></tr><tr><td>并发控制</td><td>通过 <code>WATCH</code> 实现乐观锁</td></tr><tr><td>失败行为</td><td>WATCH 冲突 → <code>EXEC</code> 返回 <code>nil</code></td></tr><tr><td>性能</td><td>队列入内存，执行非常快</td></tr></tbody></table><h2 id="SpringBoot-中使用-Redis-事务">SpringBoot 中使用 Redis 事务</h2><ul class="lvl-0"><li class="lvl-2"><p>实际项目中Redis事务很少使用，因为<code>WATCH + MULTI</code> 的性能不如 <code>Lua</code> 或 <code>INCR</code> 等原子指令。</p></li><li class="lvl-2"><p>同一事务代码必须在同一线程内完成，推荐使用 <code>SessionCallback</code></p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> <span class="string">&quot;stock:1001&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化库存</span></span><br><span class="line">redisTemplate.opsForValue().set(key, <span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 执行事务操作</span></span><br><span class="line">List&lt;Object&gt; result = redisTemplate.execute(<span class="keyword">new</span> <span class="title class_">SessionCallback</span>&lt;&gt;() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;Object&gt; <span class="title function_">execute</span><span class="params">(RedisOperations operations)</span> <span class="keyword">throws</span> DataAccessException &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 监视key的变化</span></span><br><span class="line">            operations.watch(key);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 安全获取当前库存值</span></span><br><span class="line">            <span class="type">Object</span> <span class="variable">stock</span> <span class="operator">=</span> operations.opsForValue().get(key);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 检查库存是否充足</span></span><br><span class="line">            <span class="keyword">if</span> (stock == <span class="literal">null</span> || (Integer) stock &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                operations.unwatch(); <span class="comment">// 取消监视</span></span><br><span class="line">                System.out.println(<span class="string">&quot;库存不足，无法扣减&quot;</span>);</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 开始事务</span></span><br><span class="line">            operations.multi();</span><br><span class="line">            operations.opsForValue().decrement(key);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 执行事务并返回结果</span></span><br><span class="line">            List&lt;Object&gt; execResult = operations.exec();</span><br><span class="line">            System.out.println(<span class="string">&quot;事务执行成功，扣减库存后结果: &quot;</span> + execResult);</span><br><span class="line">            <span class="keyword">return</span> execResult;</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="comment">// 发生异常时取消监视</span></span><br><span class="line">            operations.unwatch();</span><br><span class="line">            System.err.println(<span class="string">&quot;事务执行异常: &quot;</span> + e.getMessage());</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">DataAccessException</span>(<span class="string">&quot;Redis事务执行失败&quot;</span>, e) &#123;</span><br><span class="line">            &#125;;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (result != <span class="literal">null</span>) &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;最终事务结果: &quot;</span> + result);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;事务未执行或执行失败&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>验证事务是否生效，可以在 redis 终端执行 <code>MONITOR</code> 命令查询</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; MONITOR</span><br><span class="line">OK</span><br><span class="line">1768054048.497866 [0 127.0.0.1:53634] <span class="string">&quot;HELLO&quot;</span> <span class="string">&quot;3&quot;</span> <span class="string">&quot;AUTH&quot;</span> <span class="string">&quot;(redacted)&quot;</span> <span class="string">&quot;(redacted)&quot;</span></span><br><span class="line">1768054048.510741 [0 127.0.0.1:53634] <span class="string">&quot;CLIENT&quot;</span> <span class="string">&quot;SETINFO&quot;</span> <span class="string">&quot;lib-name&quot;</span> <span class="string">&quot;Lettuce&quot;</span></span><br><span class="line">1768054048.510759 [0 127.0.0.1:53634] <span class="string">&quot;CLIENT&quot;</span> <span class="string">&quot;SETINFO&quot;</span> <span class="string">&quot;lib-ver&quot;</span> <span class="string">&quot;6.6.0.RELEASE/643bd47&quot;</span></span><br><span class="line">1768054048.538458 [0 127.0.0.1:53634] <span class="string">&quot;SET&quot;</span> <span class="string">&quot;stock:1001&quot;</span> <span class="string">&quot;10&quot;</span></span><br><span class="line">1768054048.557699 [0 127.0.0.1:53635] <span class="string">&quot;HELLO&quot;</span> <span class="string">&quot;3&quot;</span> <span class="string">&quot;AUTH&quot;</span> <span class="string">&quot;(redacted)&quot;</span> <span class="string">&quot;(redacted)&quot;</span></span><br><span class="line">1768054048.559907 [0 127.0.0.1:53635] <span class="string">&quot;CLIENT&quot;</span> <span class="string">&quot;SETINFO&quot;</span> <span class="string">&quot;lib-name&quot;</span> <span class="string">&quot;Lettuce&quot;</span></span><br><span class="line">1768054048.559918 [0 127.0.0.1:53635] <span class="string">&quot;CLIENT&quot;</span> <span class="string">&quot;SETINFO&quot;</span> <span class="string">&quot;lib-ver&quot;</span> <span class="string">&quot;6.6.0.RELEASE/643bd47&quot;</span></span><br><span class="line">1768054048.562642 [0 127.0.0.1:53635] <span class="string">&quot;WATCH&quot;</span> <span class="string">&quot;stock:1001&quot;</span></span><br><span class="line">1768054048.566201 [0 127.0.0.1:53634] <span class="string">&quot;GET&quot;</span> <span class="string">&quot;stock:1001&quot;</span></span><br><span class="line">1768054048.583727 [0 127.0.0.1:53635] <span class="string">&quot;MULTI&quot;</span></span><br><span class="line">1768054048.645412 [0 127.0.0.1:53635] <span class="string">&quot;DECR&quot;</span> <span class="string">&quot;stock:1001&quot;</span></span><br><span class="line">1768054048.645421 [0 127.0.0.1:53635] <span class="string">&quot;EXEC&quot;</span></span><br></pre></td></tr></table></figure><blockquote><p>重点看：<code>WATCH</code> 以及 <code>MULTI</code> 和 <code>EXEC</code>之间的输出，要在一个连接中才能生效</p></blockquote>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/10/redis7-command-01-transaction/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/10/redis7-command-01-transaction/"/>
    <published>2026-01-10T13:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文基于<code>redis-7.4.7</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
</ul>]]>
    </summary>
    <title>Redis 命令详解：事务 Transaction</title>
    <updated>2026-01-11T04:35:02.873Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 Redisson 的使用方法</li><li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li><li class="lvl-2"><a href="https://redisson.pro/docs/getting-started/">Redisson 官方文档</a></li><li class="lvl-2">本文以Springboot项目为例，介绍Redisson的使用方法</li><li class="lvl-2">示例代码：<a href="https://github.com/hanqunfeng/springbootchapter/tree/master/springboot3-demo/redis-demo/redisson-demo">GitHub</a></li></ul><span id="more"></span><h2 id="与Springboot集成">与Springboot集成</h2><h3 id="添加依赖">添加依赖</h3><ul class="lvl-0"><li class="lvl-2">Redisson有两个版本，一个是<code>Community Edition</code>，一个是<code>Redisson PRO</code>，前者社区版，后者付费版，两个版本的区别参见<a href="https://redisson.pro/feature-comparison.html">Redisson PRO vs. Community Edition</a></li><li class="lvl-2">Springboot 集成的是 Redisson Community Edition，<a href="https://redisson.pro/docs/integration-with-spring/#spring-boot-starter">Redisson Spring Boot Starter</a></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;dependency&gt;</span><br><span class="line">    &lt;groupId&gt;org.redisson&lt;/groupId&gt;</span><br><span class="line">    &lt;artifactId&gt;redisson-spring-boot-starter&lt;/artifactId&gt;</span><br><span class="line">    &lt;version&gt;3.52.0&lt;/version&gt;</span><br><span class="line">&lt;/dependency&gt;</span><br></pre></td></tr></table></figure><blockquote><p>PS: 目前<code>redisson-spring-boot-starte</code>最新版是<code>4.1.0</code>，其对应的是 Springboot 4.x 版本，本文基于<code>springboot-3.5.8</code>，所以这里用的是<code>3.52.0</code>这个版本。</p></blockquote><table><thead><tr><th>Redisson 模块</th><th>对应 Spring Boot</th><th>对应 Spring Data Redis</th></tr></thead><tbody><tr><td>redisson-spring-data-16</td><td>Spring Boot <strong>1.3.x</strong></td><td>Spring Data Redis <strong>1.6.x</strong></td></tr><tr><td>redisson-spring-data-17</td><td>Spring Boot <strong>1.4.x</strong></td><td>Spring Data Redis <strong>1.7.x</strong></td></tr><tr><td>redisson-spring-data-18</td><td>Spring Boot <strong>1.5.x</strong></td><td>Spring Data Redis <strong>1.8.x</strong></td></tr><tr><td>redisson-spring-data-2x</td><td>Spring Boot <strong>2.x</strong></td><td>Spring Data Redis <strong>2.x</strong></td></tr><tr><td>redisson-spring-data-3x</td><td>Spring Boot <strong>3.x</strong></td><td>Spring Data Redis <strong>3.x</strong></td></tr><tr><td>redisson-spring-data-4x</td><td>Spring Boot <strong>4.x</strong></td><td>Spring Data Redis <strong>4.x</strong></td></tr></tbody></table><h3 id="配置文件">配置文件</h3><ul class="lvl-0"><li class="lvl-2"><p>配置文件<code>application.properties</code> 示例</p></li></ul><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># redis config</span></span><br><span class="line"><span class="attr">spring.data.redis.host</span>=<span class="string">localhost</span></span><br><span class="line"><span class="attr">spring.data.redis.port</span>=<span class="string">6379</span></span><br><span class="line"><span class="attr">spring.data.redis.username</span>=<span class="string">admin</span></span><br><span class="line"><span class="attr">spring.data.redis.password</span>=<span class="string">123456</span></span><br><span class="line"><span class="attr">spring.data.redis.database</span>=<span class="string">0</span></span><br><span class="line"><span class="comment"># 超时时间</span></span><br><span class="line"><span class="attr">spring.data.redis.timeout</span>=<span class="string">3s</span></span><br><span class="line"><span class="comment"># 连接池配置</span></span><br><span class="line"><span class="comment"># 启用连接池</span></span><br><span class="line"><span class="attr">spring.data.redis.lettuce.pool.enabled</span>=<span class="string">true</span></span><br><span class="line"><span class="comment"># 连接池最大连接数</span></span><br><span class="line"><span class="attr">spring.data.redis.lettuce.pool.max-active</span>=<span class="string">8</span></span><br><span class="line"><span class="comment"># 获取连接最大等待时间</span></span><br><span class="line"><span class="attr">spring.data.redis.lettuce.pool.max-wait</span>=<span class="string">2s</span></span><br><span class="line"><span class="comment"># 最大空闲连接</span></span><br><span class="line"><span class="attr">spring.data.redis.lettuce.pool.max-idle</span>=<span class="string">8</span></span><br><span class="line"><span class="comment"># 最小空闲连接</span></span><br><span class="line"><span class="attr">spring.data.redis.lettuce.pool.min-idle</span>=<span class="string">0</span></span><br><span class="line"><span class="comment"># 关闭超时时间</span></span><br><span class="line"><span class="attr">spring.data.redis.lettuce.shutdown-timeout</span>=<span class="string">5s</span></span><br></pre></td></tr></table></figure><h3 id="获取RedissonClient">获取RedissonClient</h3><ul class="lvl-0"><li class="lvl-2"><p>Redisson 提供了3个 Client，本质上是同一套底层连接与命令能力的不同编程模型封装，主要区别在于：</p></li></ul><blockquote><p>调用方式（同步 / 异步 / Reactive / RxJava）、线程模型、背压能力、适用场景。</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Sync and Async API</span></span><br><span class="line"><span class="meta">@Autowired</span></span><br><span class="line">RedissonClient redisson;</span><br><span class="line"><span class="comment">// Reactive API</span></span><br><span class="line"><span class="type">RedissonReactiveClient</span> <span class="variable">redissonReactive</span> <span class="operator">=</span> redisson.reactive();</span><br><span class="line"><span class="comment">// RxJava3 API</span></span><br><span class="line"><span class="type">RedissonRxClient</span> <span class="variable">redissonRx</span> <span class="operator">=</span> redisson.rxJava();</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>三个 Client 的定位概览</p></li></ul><table><thead><tr><th>Client</th><th>类型</th><th>编程模型</th><th>返回值</th><th>是否阻塞</th><th>背压支持</th><th>典型用途</th></tr></thead><tbody><tr><td><code>RedissonClient</code></td><td>同步 / 异步</td><td>Imperative</td><td>T / RFuture<T></td><td>同步会阻塞</td><td>❌</td><td>传统服务、简单业务</td></tr><tr><td><code>RedissonReactiveClient</code></td><td>Reactive Streams</td><td>Reactor / Flow</td><td>Mono / Flux</td><td>❌</td><td>✅</td><td>WebFlux、高并发</td></tr><tr><td><code>RedissonRxClient</code></td><td>RxJava3</td><td>RxJava3</td><td>Observable / Single / Flowable</td><td>❌</td><td>✅</td><td>RxJava 项目</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>📊 Redisson Client 选型对照表</p></li></ul><table><thead><tr><th>场景</th><th>技术架构特征</th><th>推荐 Client</th><th>编程模型</th><th>核心理由</th><th>典型应用</th></tr></thead><tbody><tr><td>普通 Spring Boot（MVC）</td><td>同步请求模型<br>线程池可控<br>阻塞 IO 可接受</td><td><strong>RedissonClient（同步 / Async）</strong></td><td>Imperative + Future</td><td>- 编码简单，维护成本低<br>- 与 JDBC / 事务模型一致<br>- 易于线程与连接池治理<br>- 调试与排障成本低</td><td>业务系统、管理后台、内部服务</td></tr><tr><td>高并发 IO 服务（WebFlux / 网关）</td><td>非阻塞架构<br>事件驱动<br>高并发连接数</td><td><strong>RedissonReactiveClient</strong></td><td>Reactive Streams（Mono / Flux）</td><td>- 全链路非阻塞<br>- 支持背压控制<br>- 与 Reactor 生态天然兼容<br>- 最大化吞吐与资源利用率</td><td>API 网关、实时服务、推送系统</td></tr><tr><td>已有 RxJava 技术栈</td><td>已广泛使用 RxJava3<br>成熟流式管道</td><td><strong>RedissonRxClient</strong></td><td>RxJava（Single / Flowable）</td><td>- 无需引入 Reactor 生态<br>- 与现有流式模型无缝集成<br>- 支持背压（Flowable）<br>- 改造成本最低</td><td>Android 后端、历史系统</td></tr></tbody></table><h2 id="📊-Redis-数据类型-↔-Redisson-对象映射表">📊 Redis 数据类型 ↔ Redisson 对象映射表</h2><h3 id="1️⃣-String">1️⃣ String</h3><table><thead><tr><th>Redis</th><th>Redisson 对象</th><th>Java 语义</th><th>常用 API</th><th>底层映射</th><th>说明</th></tr></thead><tbody><tr><td>String</td><td><code>RBucket&lt;T&gt;</code></td><td>单值对象</td><td>get / set / delete</td><td>GET / SET</td><td>最常用</td></tr><tr><td>String</td><td><code>RAtomicLong</code></td><td>原子计数器</td><td>incrementAndGet</td><td>INCRBY</td><td>强语义</td></tr><tr><td>String</td><td><code>RAtomicDouble</code></td><td>原子浮点</td><td>addAndGet</td><td>自实现</td><td>Lua</td></tr><tr><td>String</td><td><code>RLongAdder</code></td><td>高并发计数</td><td>increment</td><td>Hash + Lua</td><td>分片</td></tr><tr><td>String</td><td><code>RBitSet</code></td><td>位集合</td><td>set / get</td><td>SETBIT / GETBIT</td><td>位图</td></tr><tr><td>String</td><td><code>RBinaryStream</code></td><td>二进制流</td><td>read / write</td><td>GETRANGE</td><td>大对象</td></tr></tbody></table><h3 id="2️⃣-Hash">2️⃣ Hash</h3><table><thead><tr><th>Redis</th><th>Redisson 对象</th><th>Java 语义</th><th>常用 API</th><th>底层映射</th><th>说明</th></tr></thead><tbody><tr><td>Hash</td><td><code>RMap&lt;K,V&gt;</code></td><td>Map</td><td>get / put / remove</td><td>HGET / HSET</td><td>核心</td></tr><tr><td>Hash</td><td><code>RMapCache&lt;K,V&gt;</code></td><td>带 TTL Map</td><td>put(key,val,ttl)</td><td>Hash + ZSet</td><td>过期控制</td></tr></tbody></table><h3 id="3️⃣-List">3️⃣ List</h3><table><thead><tr><th>Redis</th><th>Redisson 对象</th><th>Java 语义</th><th>常用 API</th><th>底层映射</th><th>说明</th></tr></thead><tbody><tr><td>List</td><td><code>RList&lt;V&gt;</code></td><td>List</td><td>add / get / remove</td><td>LPUSH / LRANGE</td><td>顺序</td></tr><tr><td>List</td><td><code>RQueue&lt;V&gt;</code></td><td>Queue</td><td>offer / poll</td><td>RPUSH / LPOP</td><td>队列</td></tr><tr><td>List</td><td><code>RDeque&lt;V&gt;</code></td><td>Deque</td><td>addFirst / addLast</td><td>LPUSH / RPUSH</td><td>双端</td></tr><tr><td>List</td><td><code>RBlockingQueue&lt;V&gt;</code></td><td>BlockingQueue</td><td>take</td><td>BLPOP</td><td>阻塞消费</td></tr></tbody></table><h3 id="4️⃣-Set">4️⃣ Set</h3><table><thead><tr><th>Redis</th><th>Redisson 对象</th><th>Java 语义</th><th>常用 API</th><th>底层映射</th><th>说明</th></tr></thead><tbody><tr><td>Set</td><td><code>RSet&lt;V&gt;</code></td><td>Set</td><td>add / contains</td><td>SADD / SISMEMBER</td><td>无序</td></tr><tr><td>Set</td><td><code>RSetCache&lt;V&gt;</code></td><td>TTL Set</td><td>add(ttl)</td><td>Set + ZSet</td><td>自动过期</td></tr><tr><td>Set</td><td><code>RSortedSet&lt;V&gt;</code></td><td>SortedSet</td><td>add / first</td><td>ZSet</td><td>有序</td></tr></tbody></table><h3 id="5️⃣-ZSet">5️⃣ ZSet</h3><table><thead><tr><th>Redis</th><th>Redisson 对象</th><th>Java 语义</th><th>常用 API</th><th>底层映射</th><th>说明</th></tr></thead><tbody><tr><td>ZSet</td><td><code>RScoredSortedSet&lt;V&gt;</code></td><td>带 score 集合</td><td>add(score)</td><td>ZADD</td><td>排行榜</td></tr><tr><td>ZSet</td><td><code>RLexSortedSet</code></td><td>字典排序集合</td><td>add</td><td>ZRANGEBYLEX</td><td>字典序</td></tr><tr><td>ZSet</td><td><code>RPriorityQueue&lt;V&gt;</code></td><td>优先队列</td><td>poll</td><td>ZSet</td><td>堆语义</td></tr><tr><td>ZSet</td><td><code>RPriorityDeque&lt;V&gt;</code></td><td>双端优先队列</td><td>pollFirst</td><td>ZSet</td><td>扩展</td></tr></tbody></table><h3 id="6️⃣-Bitmap">6️⃣ Bitmap</h3><table><thead><tr><th>Redis</th><th>Redisson 对象</th><th>Java 语义</th><th>常用 API</th><th>底层映射</th><th>说明</th></tr></thead><tbody><tr><td>Bitmap</td><td><code>RBitSet</code></td><td>BitSet</td><td>set / get</td><td>SETBIT</td><td>位图</td></tr><tr><td>Bitmap</td><td><code>RLongAdder</code></td><td>计数位</td><td>increment</td><td>Bitmap 分片</td><td>高并发</td></tr></tbody></table><h3 id="7️⃣-HyperLogLog">7️⃣ HyperLogLog</h3><table><thead><tr><th>Redis</th><th>Redisson 对象</th><th>Java 语义</th><th>常用 API</th><th>底层映射</th><th>说明</th></tr></thead><tbody><tr><td>HyperLogLog</td><td><code>RHyperLogLog&lt;V&gt;</code></td><td>去重计数</td><td>add / count</td><td>PFADD / PFCOUNT</td><td>UV统计</td></tr></tbody></table><h3 id="8️⃣-Geo">8️⃣ Geo</h3><table><thead><tr><th>Redis</th><th>Redisson 对象</th><th>Java 语义</th><th>常用 API</th><th>底层映射</th><th>说明</th></tr></thead><tbody><tr><td>Geo</td><td><code>RGeo&lt;V&gt;</code></td><td>地理索引</td><td>add / radius</td><td>GEOADD</td><td>LBS</td></tr></tbody></table><h3 id="9️⃣-Stream">9️⃣ Stream</h3><table><thead><tr><th>Redis</th><th>Redisson 对象</th><th>Java 语义</th><th>常用 API</th><th>底层映射</th><th>说明</th></tr></thead><tbody><tr><td>Stream</td><td><code>RStream&lt;K,V&gt;</code></td><td>消息流</td><td>add / read</td><td>XADD / XREAD</td><td>消息队列</td></tr><tr><td>Stream</td><td><code>RStreamReactive</code></td><td>Reactive Stream</td><td>subscribe</td><td>XREADGROUP</td><td>流式</td></tr></tbody></table><h3 id="🔟-Pub-Sub">🔟 Pub/Sub</h3><table><thead><tr><th>Redis</th><th>Redisson 对象</th><th>Java 语义</th><th>常用 API</th><th>底层映射</th><th>说明</th></tr></thead><tbody><tr><td>PubSub</td><td><code>RTopic</code></td><td>Topic</td><td>publish / addListener</td><td>PUBLISH</td><td>广播</td></tr><tr><td>PubSub</td><td><code>RPatternTopic</code></td><td>Pattern</td><td>subscribe</td><td>PSUBSCRIBE</td><td>模式订阅</td></tr></tbody></table><h2 id="后记">后记</h2><ul class="lvl-0"><li class="lvl-2"><p>Redisson 操作 RedisJSON 参看 <a href="/2025/12/24/redis7-datatype-16-JSON/" title="Redis 命令及数据类型 -- JSON">Redis 命令及数据类型 -- JSON</a></p></li><li class="lvl-2"><p>Redisson 操作 RediSearch 参看 <a href="/2026/01/05/redis7-module-RediSearch-study/" title="RediSearch 开发实战">RediSearch 开发实战</a></p></li></ul>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/09/redis-Redisson-02-datatype/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/09/redis-Redisson-02-datatype/"/>
    <published>2026-01-09T14:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 Redisson 的使用方法</li>
<li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li>
<li class="lvl-2"><a href="https://redisson.pro/docs/getting-started/">Redisson 官方文档</a></li>
<li class="lvl-2">本文以Springboot项目为例，介绍Redisson的使用方法</li>
<li class="lvl-2">示例代码：<a href="https://github.com/hanqunfeng/springbootchapter/tree/master/springboot3-demo/redis-demo/redisson-demo">GitHub</a></li>
</ul>]]>
    </summary>
    <title>Redisson 实战</title>
    <updated>2026-01-11T05:07:57.184Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 Redisson</li><li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li><li class="lvl-2"><a href="https://redisson.pro/docs/getting-started/">Redisson 官方文档</a></li></ul><span id="more"></span><h2 id="Redisson-的官方定位">Redisson 的官方定位</h2><ul class="lvl-0"><li class="lvl-2"><p>✅ 面向 Valkey / Redis 的 Java 客户端</p></li><li class="lvl-2"><p>✅ 同时也是一个 实时数据平台（Real-Time Data Platform）</p></li><li class="lvl-2"><p>✅ 提供 高级抽象 + 分布式能力扩展</p></li></ul><blockquote><p>可以理解为：Redisson = Redis Java Client + 分布式对象平台 + 高级并发语义</p></blockquote><ul class="lvl-0"><li class="lvl-2"><p>它并不是简单替代 Jedis / Lettuce，而是：</p><ul class="lvl-2"><li class="lvl-6">在 Redis 之上构建了一层 面向对象的分布式抽象模型</li><li class="lvl-6">把 Redis 从“命令型数据库”提升为“分布式数据结构与服务平台”。</li></ul></li></ul><blockquote><p><a href="https://redisson.pro/blog/feature-comparison-redisson-vs-jedis.html">Redisson vs Jedis</a><br><a href="https://redisson.pro/blog/feature-comparison-redisson-vs-lettuce.html">Redisson vs Lettuce</a></p></blockquote><ul class="lvl-0"><li class="lvl-2"><p>Redisson 在 Java 代码与 Redis 命令之间增加了一层抽象，其带来的价值:</p></li></ul><table><thead><tr><th>价值</th><th>说明</th></tr></thead><tbody><tr><td>屏蔽命令细节</td><td>不再直接拼 Redis 命令</td></tr><tr><td>面向对象建模</td><td>Map / Set / Lock / Queue</td></tr><tr><td>语义更稳定</td><td>不依赖命令细节</td></tr><tr><td>降低学习成本</td><td>Java 开发者无需深度理解 Redis 协议</td></tr><tr><td>提升可维护性</td><td>业务代码更清晰</td></tr></tbody></table><h2 id="Redisson-扩展的能力">Redisson 扩展的能力</h2><ul class="lvl-0"><li class="lvl-2"><p>分布式集合（Distributed Collections）</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">特点：</span><br><span class="line">    数据存储在 Redis</span><br><span class="line">    语义与 Java 集合一致</span><br><span class="line">    支持分布式并发访问</span><br></pre></td></tr></table></figure><table><thead><tr><th>Redisson 对象</th><th>类比 Java</th></tr></thead><tbody><tr><td>RMap</td><td>Map</td></tr><tr><td>RSet</td><td>Set</td></tr><tr><td>RList</td><td>List</td></tr><tr><td>RQueue</td><td>Queue</td></tr><tr><td>RDeque</td><td>Deque</td></tr><tr><td>RSortedSet</td><td>SortedSet</td></tr><tr><td>RBlockingQueue</td><td>BlockingQueue</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>分布式对象（Distributed Objects）</p></li></ul><table><thead><tr><th>对象</th><th>语义</th></tr></thead><tbody><tr><td>RAtomicLong</td><td>分布式原子计数</td></tr><tr><td>RBucket</td><td>单值对象，如：RedissonBucket，RedissonJsonBucket</td></tr><tr><td>RBitSet</td><td>位集合</td></tr><tr><td>RBloomFilter</td><td>布隆过滤器</td></tr><tr><td>RHyperLogLog</td><td>基数统计</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>分布式服务（Distributed Services）</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">这是 Redisson 真正的核心竞争力。</span><br><span class="line">这些能力：</span><br><span class="line">    Redis 原生并不直接提供</span><br><span class="line">    Redisson 通过 Lua + 状态机 + Watchdog 实现</span><br></pre></td></tr></table></figure><table><thead><tr><th>服务</th><th>解决问题</th></tr></thead><tbody><tr><td>RLock</td><td>分布式锁</td></tr><tr><td>RFairLock</td><td>公平锁</td></tr><tr><td>RReadWriteLock</td><td>读写锁</td></tr><tr><td>RSemaphore</td><td>信号量</td></tr><tr><td>RCountDownLatch</td><td>闭锁</td></tr><tr><td>RRateLimiter</td><td>限流</td></tr><tr><td>RDelayedQueue</td><td>延迟队列</td></tr><tr><td>RExecutorService</td><td>分布式线程池</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>命令映射能力（Command Mapping）</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Redisson 提供：</span><br><span class="line">    Redis Command → Redisson API 映射表</span><br><span class="line"></span><br><span class="line">用途：</span><br><span class="line">    从命令式思维过渡到对象式 API</span><br><span class="line">    快速定位功能接口</span><br><span class="line">    验证底层实现机制</span><br></pre></td></tr></table></figure><table><thead><tr><th>Redis 类型</th><th>Redisson 对象</th></tr></thead><tbody><tr><td>String</td><td>RBucket, RAtomicLong, RBitSet</td></tr><tr><td>Hash</td><td>RMap, RMapCache</td></tr><tr><td>List</td><td>RList, RQueue, RBlockingQueue</td></tr><tr><td>Set</td><td>RSet, RSetCache</td></tr><tr><td>ZSet</td><td>RScoredSortedSet, RPriorityQueue</td></tr><tr><td>Bitmap</td><td>RBitSet</td></tr><tr><td>HyperLogLog</td><td>RHyperLogLog</td></tr><tr><td>Geo</td><td>RGeo</td></tr><tr><td>Stream</td><td>RStream</td></tr><tr><td>PubSub</td><td>RTopic</td></tr><tr><td>JSON</td><td>RJSONBucket</td></tr><tr><td>Vector<code>(Redis8+) </code></td><td>RVectorSet</td></tr></tbody></table><h2 id="RedisTemplate-VS-Redisson">RedisTemplate VS Redisson</h2><ul class="lvl-0"><li class="lvl-2"><p>结论先行：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">✅ 如果你只是把 Redis 当作 KV / Cache / 简单数据结构存储 → 优先使用 RedisTemplate</span><br><span class="line">✅ 如果你需要分布式能力（锁、限流、队列、同步器、原子对象） → 优先使用 RedissonClient</span><br><span class="line">❗在复杂系统中，两者可以同时存在，各司其职</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>核心定位差异</p></li></ul><table><thead><tr><th>维度</th><th>RedisTemplate</th><th>RedissonClient</th></tr></thead><tbody><tr><td>抽象层级</td><td>Redis 原生命令封装</td><td>分布式对象与同步器</td></tr><tr><td>编程模型</td><td>Command-oriented</td><td>Object-oriented</td></tr><tr><td>关注点</td><td>数据访问</td><td>分布式协调</td></tr><tr><td>功能粒度</td><td>String / Hash / List / ZSet</td><td>Map / Lock / Semaphore / Queue</td></tr><tr><td>一致性保障</td><td>需要自己设计</td><td>内置实现</td></tr><tr><td>业务语义</td><td>偏底层</td><td>偏业务</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>性能与稳定性对比</p></li></ul><blockquote><p>在“纯 KV 访问”场景下，RedisTemplate 的性能通常优于 Redisson。</p></blockquote><table><thead><tr><th>维度</th><th>RedisTemplate</th><th>Redisson</th></tr></thead><tbody><tr><td>延迟</td><td>更低</td><td>略高（对象封装）</td></tr><tr><td>吞吐</td><td>更高</td><td>略低</td></tr><tr><td>内存开销</td><td>最小</td><td>较高</td></tr><tr><td>网络请求</td><td>精简</td><td>可能多次交互</td></tr><tr><td>稳定性</td><td>极高</td><td>高（依赖内部状态机）</td></tr><tr><td>排障复杂度</td><td>低</td><td>中~高</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>✅ 实际生产中的最佳实践: ✔️ 两者共存，而不是二选一。</p></li></ul><table><thead><tr><th>功能</th><th>技术</th></tr></thead><tbody><tr><td>缓存 / 查询</td><td>RedisTemplate</td></tr><tr><td>分布式锁</td><td>Redisson</td></tr><tr><td>延迟队列</td><td>Redisson</td></tr><tr><td>原子计数</td><td>RedisTemplate 或 Redisson Atomic</td></tr><tr><td>搜索 / 时序</td><td>RedisTemplate</td></tr></tbody></table>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/09/redis-Redisson-01-base/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/09/redis-Redisson-01-base/"/>
    <published>2026-01-09T13:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 Redisson</li>
<li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li>
<li class="lvl-2"><a href="https://redisson.pro/docs/getting-started/">Redisson 官方文档</a></li>
</ul>]]>
    </summary>
    <title>Redisson 简介</title>
    <updated>2026-01-09T06:08:21.079Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 Redis 扩展模块 – RedisRoaring 中 RoaringBitmap 数据类型</li><li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li><li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li><li class="lvl-2">RedisRoaring 的安装参见 <a href="/2026/01/08/redis7-module-RedisRoaring/" title="Redis 扩展模块 -- RedisRoaring 的安装方法">Redis 扩展模块 -- RedisRoaring 的安装方法</a></li></ul><span id="more"></span><h2 id="RoaringBitmap-命令">RoaringBitmap 命令</h2><ul class="lvl-0"><li class="lvl-2"><p>可以在 redis-cli 中执行 <code>help @module</code>，找出所有 <code>R.*</code> 和 <code>R64.*</code> 的命令说明</p></li><li class="lvl-2"><p>R.*：操作的是 32 位无符号整数（最大约 2^32-1）</p></li><li class="lvl-2"><p>R64.*：操作的是 64 位无符号整数（最大约 2^64-1），适合超大 ID 空间，如 Snowflake / 时间戳映射</p><blockquote><p>R64.* 支持的整数理论上是 0 ~ 2⁶⁴-1（约 1.84e19），但工程上应谨慎使用超大 offset，避免稀疏爆炸和性能退化。</p></blockquote> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">high 32 bits  -&gt; container key 大桶</span><br><span class="line">low  32 bits  -&gt; container offset 小桶</span><br><span class="line"></span><br><span class="line">当你写入一个非常大的值，例如 10^18</span><br><span class="line">   虽然 Roaring 只为存在数据的 container 分配内存。</span><br><span class="line">   但 container 的 key 本身是一个 map / array 索引结构。</span><br><span class="line">   极端稀疏、超大 offset 会增加索引管理成本，会导致某些命令操作会变得很慢。</span><br><span class="line">   另外，一个容器至少要 几十字节 ~ 几百字节级别，容器的数量过多，单机内存也无法满足</span><br><span class="line">   如果你创建 10 亿个 container，假设每个 container 占用 100 字节，那么内存占用为：</span><br><span class="line">   10^9 containers × 100 bytes ≈ 100 GB 内存</span><br></pre></td></tr></table></figure></li><li class="lvl-2"><p>两者语义完全一致，仅支持的 offset 范围不同，以下示例以 R.* 为主。</p></li><li class="lvl-2"><p>对比 R 与 R64</p></li></ul><table><thead><tr><th>维度</th><th>R.*</th><th>R64.*</th></tr></thead><tbody><tr><td>Offset 类型</td><td>uint32</td><td>uint64</td></tr><tr><td>最大值</td><td>4,294,967,295 (~4.29e9)</td><td>18,446,744,073,709,551,615 (~1.84e19)</td></tr><tr><td>可表达规模</td><td>十亿级</td><td>万亿亿级</td></tr><tr><td>典型用途</td><td>用户ID、索引</td><td>时间戳、雪花ID、全局唯一ID</td></tr></tbody></table><h3 id="一、数据写入-修改类">一、数据写入 / 修改类</h3><table><thead><tr><th>命令</th><th>功能</th><th>典型用途</th><th>示例</th></tr></thead><tbody><tr><td>R.SETBIT</td><td>设置单个 bit（0/1），返回旧值</td><td>单点标记</td><td><code>R.SETBIT users 1001 1</code></td></tr><tr><td>R.SETRANGE</td><td>批量设置区间 bit=1</td><td>连续区间打标</td><td><code>R.SETRANGE users 3000 3999</code>，不包含3999</td></tr><tr><td>R.APPENDINTARRAY</td><td>追加多个整数（bit=1）</td><td>批量插入 ID</td><td><code>R.APPENDINTARRAY users 1001 1002 1003</code></td></tr><tr><td>R.SETINTARRAY</td><td>用整数数组重建 bitmap</td><td>初始化数据</td><td><code>R.SETINTARRAY users 1 3 5 7</code></td></tr><tr><td>R.SETBITARRAY</td><td>用 bit 字符串重建 bitmap</td><td>外部导入</td><td><code>R.SETBITARRAY users &quot;101001&quot;</code></td></tr><tr><td>R.DELETEINTARRAY</td><td>删除多个整数（bit=0）</td><td>批量删除</td><td><code>R.DELETEINTARRAY users 1002 2000</code></td></tr><tr><td>R.CLEARBITS</td><td>清除指定 offset</td><td>精确清理</td><td><code>R.CLEARBITS users 1001 1003</code></td></tr><tr><td>R.SETFULL</td><td>填充整个 bitmap</td><td>全量初始化</td><td><code>R.SETFULL users</code></td></tr><tr><td>R.CLEAR</td><td>删除整个 bitmap</td><td>释放资源</td><td><code>R.CLEAR users</code></td></tr></tbody></table><h3 id="二、数据读取-查询类">二、数据读取 / 查询类</h3><table><thead><tr><th>命令</th><th>功能</th><th>典型用途</th><th>示例</th></tr></thead><tbody><tr><td>R.GETBIT</td><td>获取单个 bit</td><td>判断是否存在</td><td><code>R.GETBIT users 1001</code></td></tr><tr><td>R.GETBITS</td><td>批量获取 bit</td><td>多点查询</td><td><code>R.GETBITS users 1001 1002 1003</code></td></tr><tr><td>R.GETBITARRAY</td><td>返回完整 bit 字符串</td><td>调试 / 导出</td><td><code>R.GETBITARRAY users</code></td></tr><tr><td>R.GETINTARRAY</td><td>返回所有为 1 的整数</td><td>全量遍历</td><td><code>R.GETINTARRAY users</code></td></tr><tr><td>R.RANGEINTARRAY</td><td>返回区间内整数，注意这里参数是<code>索引下标</code>，不是<code>数值</code></td><td>分页扫描</td><td><code>R.RANGEINTARRAY users 1 5</code></td></tr><tr><td>R.MIN</td><td>最小 offset</td><td>最小 ID 查询</td><td><code>R.MIN users</code></td></tr><tr><td>R.MAX</td><td>最大 offset</td><td>最大 ID 查询</td><td><code>R.MAX users</code></td></tr></tbody></table><h3 id="三、统计类">三、统计类</h3><table><thead><tr><th>命令</th><th>功能</th><th>典型用途</th><th>示例</th></tr></thead><tbody><tr><td>R.BITCOUNT</td><td>bit=1 数量统计</td><td>集合基数</td><td><code>R.BITCOUNT users</code></td></tr><tr><td>R.BITPOS</td><td>查找第一个 1 或 0</td><td>稀疏分析</td><td><code>R.BITPOS users 1</code></td></tr><tr><td>R.MIN</td><td>最小 bit 位置</td><td>边界检测</td><td><code>R.MIN users</code></td></tr><tr><td>R.MAX</td><td>最大 bit 位置</td><td>边界检测</td><td><code>R.MAX users</code></td></tr><tr><td>R.STAT</td><td>容器 / 内存统计</td><td>性能调优</td><td><code>R.STAT users TEXT</code></td></tr></tbody></table><h3 id="四、集合运算类">四、集合运算类</h3><table><thead><tr><th>命令</th><th>功能</th><th>集合语义</th><th>示例</th></tr></thead><tbody><tr><td>R.BITOP AND</td><td>位图交集</td><td>A ∩ B</td><td><code>R.BITOP AND res a b</code></td></tr><tr><td>R.BITOP OR</td><td>位图并集</td><td>A ∪ B</td><td><code>R.BITOP OR res a b</code></td></tr><tr><td>R.BITOP XOR</td><td>对称差集</td><td>A ⊕ B</td><td><code>R.BITOP XOR res a b</code></td></tr><tr><td>R.DIFF</td><td>差集</td><td>A - B</td><td><code>R.DIFF res a b</code></td></tr><tr><td>R.CONTAINS</td><td>包含关系判断</td><td>子集检测</td><td><code>R.CONTAINS a b ALL</code></td></tr><tr><td>R.JACCARD</td><td>相似度计算</td><td>J(A,B)</td><td><code>R.JACCARD a b</code></td></tr></tbody></table><h3 id="五、维护-优化类">五、维护 / 优化类</h3><table><thead><tr><th>命令</th><th>功能</th><th>典型用途</th><th>示例</th></tr></thead><tbody><tr><td>R.OPTIMIZE</td><td>容器重整 / 压缩</td><td>降低内存</td><td><code>R.OPTIMIZE users MEM</code></td></tr><tr><td>R.CLEAR</td><td>清理 bitmap</td><td>回收资源</td><td><code>R.CLEAR users</code></td></tr></tbody></table><h3 id="六、R-R64-对照表（含示例）">六、R / R64 对照表（含示例）</h3><table><thead><tr><th>功能</th><th>R (32bit)</th><th>R64 (64bit)</th><th>示例</th></tr></thead><tbody><tr><td>设置 bit</td><td>R.SETBIT</td><td>R64.SETBIT</td><td><code>R64.SETBIT users64 90000000000 1</code></td></tr><tr><td>批量插入</td><td>R.APPENDINTARRAY</td><td>R64.APPENDINTARRAY</td><td><code>R64.APPENDINTARRAY users64 90000000001 90000000002</code></td></tr><tr><td>查询 bit</td><td>R.GETBIT</td><td>R64.GETBIT</td><td><code>R64.GETBIT users64 90000000001</code></td></tr><tr><td>统计</td><td>R.BITCOUNT</td><td>R64.BITCOUNT</td><td><code>R64.BITCOUNT users64</code></td></tr><tr><td>集合运算</td><td>R.BITOP</td><td>R64.BITOP</td><td><code>R64.BITOP OR res64 a64 b64</code></td></tr><tr><td>优化</td><td>R.OPTIMIZE</td><td>R64.OPTIMIZE</td><td><code>R64.OPTIMIZE users64 MEM</code></td></tr></tbody></table><h2 id="真实案例">真实案例</h2><ul class="lvl-0"><li class="lvl-2"><p>场景说明</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">假设我们有一个电商平台，需要统计以下指标：</span><br><span class="line">   VIP 用户（购买金额超过 1000 的用户）</span><br><span class="line">   付费用户（至少购买一次的用户）</span><br><span class="line">   最近活跃用户（过去 7 天内登录过的用户）</span><br><span class="line"></span><br><span class="line">我们想要计算：</span><br><span class="line">   VIP 用户人数</span><br><span class="line">   VIP 且最近活跃的用户人数</span><br><span class="line">   VIP 且付费用户且最近活跃的人数</span><br><span class="line">   付费用户与最近活跃用户的 Jaccard 相似度</span><br><span class="line"></span><br><span class="line">假设用户 ID 为整数（最大值 &lt; 2^32，使用 R.* 即可）。</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><ol><li class="lvl-5">写入数据（模拟）</li></ol></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># VIP 用户</span></span><br><span class="line">R.APPENDINTARRAY vip_users 1001 1002 1003 1004 1005</span><br><span class="line"></span><br><span class="line"><span class="comment"># 付费用户</span></span><br><span class="line">R.APPENDINTARRAY paid_users 1002 1003 1005 1006 1007</span><br><span class="line"></span><br><span class="line"><span class="comment"># 最近活跃用户</span></span><br><span class="line">R.APPENDINTARRAY active_users 1001 1003 1005 1008</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看数据类型</span></span><br><span class="line">127.0.0.1:6379&gt; <span class="built_in">type</span> active_users</span><br><span class="line">reroaring</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><ol start="2"><li class="lvl-5">查询基础统计</li></ol></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># VIP 用户人数</span></span><br><span class="line">R.BITCOUNT vip_users</span><br><span class="line"><span class="comment"># 返回 5</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 最近活跃用户人数</span></span><br><span class="line">R.BITCOUNT active_users</span><br><span class="line"><span class="comment"># 返回 4</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 付费用户人数</span></span><br><span class="line">R.BITCOUNT paid_users</span><br><span class="line"><span class="comment"># 返回 5</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><ol start="3"><li class="lvl-5">计算交集 / 子集</li></ol></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1.VIP 且最近活跃用户</span></span><br><span class="line">R.BITOP AND vip_active vip_users active_users</span><br><span class="line">R.BITCOUNT vip_active</span><br><span class="line"><span class="comment"># 返回 3</span></span><br><span class="line"><span class="comment"># 解释：用户 1001, 1003, 1005 同时是 VIP 且最近活跃。</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2.VIP 且付费用户且最近活跃</span></span><br><span class="line"><span class="comment"># 先计算 VIP 与付费交集</span></span><br><span class="line">R.BITOP AND vip_paid vip_users paid_users</span><br><span class="line"><span class="comment"># 再计算 vip_paid 与活跃用户交集</span></span><br><span class="line">R.BITOP AND vip_paid_active vip_paid active_users</span><br><span class="line">R.BITCOUNT vip_paid_active</span><br><span class="line"><span class="comment"># 返回 2</span></span><br><span class="line"><span class="comment"># 解释：用户 1003, 1005 同时满足三条件。</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><ol start="4"><li class="lvl-5">计算 Jaccard 相似度</li></ol></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 计算 付费用户 与 最近活跃用户 的相似度</span></span><br><span class="line">R.JACCARD paid_users active_users</span><br><span class="line"><span class="comment"># 返回 &quot;0.2857142857142857&quot;</span></span><br><span class="line"><span class="comment"># Jaccard 相似度计算公式：</span></span><br><span class="line">   J(A, B) = |A ∩ B| / |A ∪ B| = 交集 / 并集</span><br><span class="line">   0.2857142857142857 = |1003, 1005| / |1001, 1002, 1003, 1005, 1006, 1007, 1008| = 2 / 7 = 0.2857142857142857</span><br></pre></td></tr></table></figure><p class='katex-block'><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>J</mi><mo stretchy="false">(</mo><mi>A</mi><mo separator="true">,</mo><mi>B</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mrow><mi mathvariant="normal">∣</mi><mi>A</mi><mo>∩</mo><mi>B</mi><mi mathvariant="normal">∣</mi></mrow><mrow><mi mathvariant="normal">∣</mi><mi>A</mi><mo>∪</mo><mi>B</mi><mi mathvariant="normal">∣</mi></mrow></mfrac></mrow><annotation encoding="application/x-tex">J(A, B) = \frac{|A \cap B|}{|A \cup B|}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.09618em;">J</span><span class="mopen">(</span><span class="mord mathnormal">A</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:2.363em;vertical-align:-0.936em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">∣</span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∪</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mord">∣</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">∣</span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∩</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mord">∣</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><ul class="lvl-0"><li class="lvl-2"><ol start="5"><li class="lvl-5">范围 / ID 查询</li></ol></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1.获取 VIP 用户 ID 列表</span></span><br><span class="line">R.GETINTARRAY vip_users</span><br><span class="line"><span class="comment"># 输出</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 1001  <span class="comment"># 1 是索引，1001 是值</span></span><br><span class="line">2) (<span class="built_in">integer</span>) 1002</span><br><span class="line">3) (<span class="built_in">integer</span>) 1003</span><br><span class="line">4) (<span class="built_in">integer</span>) 1004</span><br><span class="line">5) (<span class="built_in">integer</span>) 1005</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2.获取最近活跃用户 ID，索引范围 在 1~3 范围，注意这里的索引是 R.GETINTARRAY 返回的索引下标，不是值。</span></span><br><span class="line">R.RANGEINTARRAY active_users 1 3</span><br><span class="line"><span class="comment"># 返回 1003, 1005, 1008</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 1003</span><br><span class="line">2) (<span class="built_in">integer</span>) 1005</span><br><span class="line">3) (<span class="built_in">integer</span>) 1008</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><ol start="6"><li class="lvl-5">优化存储</li></ol></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 如果数据量非常大，可以优化内存</span></span><br><span class="line">R.OPTIMIZE vip_users MEM</span><br><span class="line">R.OPTIMIZE paid_users MEM</span><br><span class="line">R.OPTIMIZE active_users MEM</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/08/redis7-datatype-19-RoaringBitmap/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/08/redis7-datatype-19-RoaringBitmap/"/>
    <published>2026-01-08T14:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 Redis 扩展模块 – RedisRoaring 中 RoaringBitmap 数据类型</li>
<li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li>
<li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li>
<li class="lvl-2">RedisRoaring 的安装参见 <a href="/2026/01/08/redis7-module-RedisRoaring/" title="Redis 扩展模块 -- RedisRoaring 的安装方法">Redis 扩展模块 -- RedisRoaring 的安装方法</a></li>
</ul>]]>
    </summary>
    <title>Redis 命令及数据类型 -- RoaringBitmap</title>
    <updated>2026-01-08T09:36:30.364Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 Redis 扩展模块 – RedisRoaring 的安装方法</li><li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li><li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li></ul><span id="more"></span><h2 id="RedisRoaring-简介">RedisRoaring 简介</h2><ul class="lvl-0"><li class="lvl-2"><p><a href="https://github.com/aviggiano/redis-roaring">RedisRoaring</a>是一个开源的 Redis 模块（Redis Module），由 Antonio Guilherme Ferreira Viggiano 维护，目的是将 Roaring Bitmap 数据结构原生集成到 Redis 中作为新的数据类型与命令集。</p></li><li class="lvl-2"><p>它基于 CRoaring 库实现压缩位图（Roaring Bitmap）功能，这是一种高效、压缩的位图数据结构，适合处理大规模整数集合。</p></li><li class="lvl-2"><p>兼容 Redis 6.0 及以上版本，在不同版本中对命令元数据、ACL 分类等功能有自动适配支持。</p></li></ul><h2 id="原生的redis-bitmap有哪些问题">原生的<code>redis bitmap</code>有哪些问题?</h2><ul class="lvl-0"><li class="lvl-2"><p>结论先行：Redis 原生 Bitmap 的核心问题是「空间不可控 + 大规模位运算性能随最大偏移线性增长」，而 redis-roaring 通过 Roaring Bitmap 解决了稀疏数据内存浪费和大范围扫描效率问题。</p></li><li class="lvl-2"><p>Bitmap 是连续数组，不支持稀疏压缩， 其必须 从 0 到最大 bit offset 全部连续分配内存。你只要设置了 bit=1 的最大下标是 N，那么 Redis 至少要分配 N/8 字节内存。</p></li><li class="lvl-2"><p>假设你要存储用户ID集合，但是存储的ID范围较大，比如 1-2000000000，那么就会导致Bitmap的bit offset非常大，导致Bitmap的存储空间非常大。</p></li><li class="lvl-2"><p>另外如果数据比较稀疏，极端情况下只存储了2个ID，ID1:1，ID2:2000000000，那么Bitmap就要按照最大的 offset进行分配空间，导致Bitmap的存储空间非常大，但是中间的空间又被浪费了。</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">SETBIT user:bitmap 1 1</span><br><span class="line">SETBIT user:bitmap 2000000000 1</span><br><span class="line"><span class="comment"># 2_000_000_000 bits ≈ 250 MB</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Redis 内部必须扫描：0 ... max_offset，即使只有 2 个 bit 为 1，也必须扫描 250MB 内存。</span></span><br><span class="line">BITCOUNT user:bitmap</span><br></pre></td></tr></table></figure><h2 id="Roaring-Bitmap实现思路">Roaring Bitmap实现思路</h2><ul class="lvl-0"><li class="lvl-2"><p>将32位的int类型数据进行<code>高16位</code>、<code>低16位</code>划分，高16位就被划分成<code>2^16=65536个大桶（容器）</code>，低16位最多被划分成<code>2^16=65536个小桶（元素）</code></p></li><li class="lvl-2"><p>把要统计的数字拆分位高16位和低16位，高16位用作容器的索引、用于定位数字在哪个容器；低16位用作容器内元素的索引、用作定位数字在容器内的位置。</p></li><li class="lvl-2"><p>高16位和低16位的计算</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">公式：x = high * 2^16 + low</span><br><span class="line">对一个整数 x：</span><br><span class="line">高 16 位（High） = x / 2^16（整数除法）</span><br><span class="line">低 16 位（Low） = x % 2^16</span><br><span class="line"></span><br><span class="line">高 16 位用于定位“容器（container）”，低 16 位用于定位容器内部的位置。</span><br><span class="line"></span><br><span class="line">假设一个 32 位整数：x = 305419896 = 0x12345678 （十六进制）</span><br><span class="line">高 16 位（High） = x / 2^16 = 305419896 / 65536 = 4660</span><br><span class="line">低 16 位（Low） = x % 2^16  = 305419896 % 65536 = 22136</span><br><span class="line">属于：container 4660, offset 22136</span><br><span class="line"></span><br><span class="line">转为二进制（32 位）进行验证：</span><br><span class="line">0001 0010 0011 0100 | 0101 0110 0111 1000</span><br><span class="line">   ↑ 高 16 位       |    ↑ 低 16 位</span><br><span class="line"></span><br><span class="line">| 部分   | 二进制              | 十六进制   | 十进制   |</span><br><span class="line">| ---- | ------------------- | ------ | ----- |</span><br><span class="line">| 高16位 | 0001 0010 0011 0100 | 0x1234 | 4660  |</span><br><span class="line">| 低16位 | 0101 0110 0111 1000 | 0x5678 | 22136 |</span><br><span class="line"></span><br><span class="line">边界例子（容器切换点）</span><br><span class="line">x = 65535</span><br><span class="line">high = 65535 / 65536 = 0</span><br><span class="line">low  = 65535 % 65536 = 65535</span><br><span class="line">属于：container 0, offset 65535 （容器0的最后一个位置）</span><br><span class="line"></span><br><span class="line">x = 65536</span><br><span class="line">high = 65536 / 65536 = 1</span><br><span class="line">low  = 65536 % 65536 = 0</span><br><span class="line">属于：container 1, offset 0 （容器1的第一个位置）</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>容器有三种不同类型: <code>ArrayContainer</code>、<code>BitmapContainer</code>和<code>RunContainer</code>，根据要统计的数字的数量和数字的连续性自动选择合适的容器。</p></li></ul><table><thead><tr><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>ArrayContainer</td><td>稀疏（少量整数），元素为short类型的有序数组<br>最多存储 65536 个 short 元素，占用 128 KB 内存</td></tr><tr><td>BitmapContainer</td><td>密集 ，使用bitmap存储数据，固定占用 8kB 内存</td></tr><tr><td>RunContainer</td><td>连续区间，使用Run-Length Encoding方式压缩存储的元素，对连续数据的压缩效果特别好。<br>它的原理是，对于连续出现的数字，只记录初始数字和后续数量。即：<br>对于数据集：5，会压缩为5, 0<br>对于数据集：5, 6, 7, 8, 9, 10，会压缩为5, 5<br>对于数据集：5, 7, 9, 11, 12，会压缩为5, 0, 7, 0, 9, 0, 11, 1 <br>最好情况：全部是连续数字，只会存储 2 个 short ，占用 4 bytes<br>最坏情况：0~65535的范围内填充所有的奇数位或偶数位，需要 65536 个short，占用 128 KB</td></tr></tbody></table><h3 id="容器的使用及容器之间的转换">容器的使用及容器之间的转换</h3><ul class="lvl-0"><li class="lvl-2"><p>Roaring 的目标是：在保证操作性能的前提下，使内存占用最小。</p></li><li class="lvl-2"><p>因此：插入、删除、合并、运算后，会评估当前 container 的“最优类型”，必要时自动转换</p></li><li class="lvl-2"><p>✅ Array → Bitmap，触发条件：容器中元素数量 &gt; 4096</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">原因：</span><br><span class="line">   Array 每个元素占 2 bytes</span><br><span class="line">   4096 × 2 = 8192 bytes</span><br><span class="line">   Bitmap 固定 8192 bytes</span><br><span class="line"></span><br><span class="line">超过这个点，Bitmap 更省内存 + 查询更快。</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>✅ Bitmap → Array，触发条件：容器中元素数量 &lt;= 4096，一般在大量删除或 AND 运算后触发。</p></li><li class="lvl-2"><p>✅ RunContainer ↔ Array / Bitmap，Run 没有固定阈值，而是 动态评估内存模型，基于如下三个指标：</p><ul class="lvl-2"><li class="lvl-5">run_count : 当前 RunContainer 中有多少个连续区间</li></ul> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">例如集合：&#123;1,2,3,4,5,  10,11,12,  100&#125;</span><br><span class="line">连续区间：[1..5], [10..12], [100..100]</span><br><span class="line">因此：run_count = 3</span><br></pre></td></tr></table></figure></li></ul><table><thead><tr><th>run_count</th><th>数据形态</th><th>适合 Run 吗</th></tr></thead><tbody><tr><td>很小（1~几十）</td><td>大段连续</td><td>极其适合</td></tr><tr><td>中等（几百）</td><td>稍碎</td><td>可能适合</td></tr><tr><td>很大（上千）</td><td>高度碎片化</td><td>不适合</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-5"><p>run_total_coverage : run 覆盖的总元素数量(本质就是元素的数量)</p></li></ul>   <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[1..5]  → 5 个</span><br><span class="line">[10..12] → 3 个</span><br><span class="line">[100..100] → 1 个</span><br><span class="line"></span><br><span class="line">run_total_coverage = 9</span><br><span class="line"></span><br><span class="line">实际上更重要的指标是 平均 run 长度</span><br><span class="line">平均 run 长度 = run_total_coverage / run_count = 9 / 3 = 3</span><br></pre></td></tr></table></figure><table><thead><tr><th>run_count</th><th>coverage</th><th>平均长度</th><th>形态</th><th>适合 Run 吗</th></tr></thead><tbody><tr><td>1</td><td>60,000</td><td>60,000</td><td>超长连续</td><td>极适合</td></tr><tr><td>10</td><td>50,000</td><td>5,000</td><td>大段连续</td><td>适合</td></tr><tr><td>5,000</td><td>50,000</td><td>10</td><td>碎片连续</td><td>非常不适合</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-5"><p>estimated_memory : 估算内存占用，这是核心决策指标</p></li></ul>   <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">memory_run: 如果用 RunContainer，需要多少内存， ≈ run_count × RUN_ENTRY_SIZE(4 bytes)</span><br><span class="line">memory_array: 如果用 ArrayContainer，需要多少内存， ≈ 元素数量 × 2 bytes</span><br><span class="line">memory_bitmap: 如果用 BitmapContainer，需要多少内存，固定内存 = 8192 bytes</span><br><span class="line"></span><br><span class="line">▶ 选择逻辑（简化）</span><br><span class="line"><span class="keyword">if</span> memory_run &lt; min(memory_array, memory_bitmap):</span><br><span class="line">   use RunContainer</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> memory_array &lt; memory_bitmap:</span><br><span class="line">   use ArrayContainer</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">   use BitmapContainer</span><br></pre></td></tr></table></figure><table><thead><tr><th>转换方向</th><th>触发条件</th></tr></thead><tbody><tr><td>Array → Bitmap</td><td>元素数量 &gt; 4096</td></tr><tr><td>Bitmap → Array</td><td>元素数量 ≤ 4096</td></tr><tr><td>任意 → Run</td><td>连续区间压缩明显</td></tr><tr><td>Run → Array / Bitmap</td><td>run 碎片化或内存劣化</td></tr><tr><td>批量运算</td><td>根据结果自动选择</td></tr><tr><td>optimize</td><td>强制重新评估</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>✅ 在 Roaring 的自动选择机制下，正常情况下单个 container 的内存占用会被控制在 ≈ 8KB 左右或更小，在“理论极限完全填满65535个 container”情况下，大约占 520 MB 内存(除了数据本身还有一些元数据)</p></li></ul><table><thead><tr><th>项目</th><th>估算</th></tr></thead><tbody><tr><td>Bitmap 数据</td><td>~512 MB</td></tr><tr><td>Container 元数据</td><td>~4 MB</td></tr><tr><td>索引结构</td><td>~1 MB</td></tr><tr><td>Redis Key 自身</td><td>~0.1 MB</td></tr><tr><td><strong>合计</strong></td><td><strong>约 517 MB</strong></td></tr></tbody></table><p><img src="https://upic-oss.oss-cn-beijing.aliyuncs.com/uPic/Vw540M.png" alt="" width="1000" height="500"></p><ul class="lvl-0"><li class="lvl-2"><p>直观对比表</p></li></ul><table><thead><tr><th>维度</th><th>Redis Bitmap</th><th>redis-roaring</th></tr></thead><tbody><tr><td>存储结构</td><td>连续 bit array</td><td>压缩 bitmap（分块）</td></tr><tr><td>稀疏数据内存</td><td>极差</td><td>极优</td></tr><tr><td>最大 offset</td><td>越大内存越爆</td><td>几乎无影响</td></tr><tr><td>BITCOUNT / BITOP</td><td>扫描整个 bitmap</td><td>只扫描有效 container</td></tr><tr><td>扩容成本</td><td>可能大规模 realloc</td><td>局部 container 扩展</td></tr><tr><td>单 bit 读写</td><td>极快</td><td>略慢（结构层）</td></tr><tr><td>模块依赖</td><td>原生</td><td>Redis Module</td></tr><tr><td>运维复杂度</td><td>最低</td><td>稍高</td></tr></tbody></table><h2 id="RedisRoaring核心价值与优势">RedisRoaring核心价值与优势</h2><ul class="lvl-0"><li class="lvl-2"><ol><li class="lvl-5">内存效率更高</li></ol><ul class="lvl-2"><li class="lvl-5">Roaring Bitmap 会根据数据稀疏/密集自动选择最优表示方式（数组、位图或运行长度编码），显著减少内存占用，相比传统 Redis Bitmap 在稀疏数据上能节省大量空间。</li></ul></li><li class="lvl-2"><ol start="2"><li class="lvl-5">高性能位操作</li></ol><ul class="lvl-2"><li class="lvl-5">对单个位的操作保持 O(1) 性能，与 Redis 原生位图操作一致。</li><li class="lvl-5">对于大规模操作（例如 BITOP AND/OR/XOR）性能显著优于原生位图，某些操作可达到 8 倍性能提升。</li></ul></li><li class="lvl-2"><ol start="3"><li class="lvl-5">结构丰富的命令集</li></ol><ul class="lvl-2"><li class="lvl-5">除了传统位图命令之外，redis-roaring 提供更丰富的数据操作命令，支持批量和分析型用例。</li></ul></li></ul><h2 id="安装-RedisRoaring">安装 RedisRoaring</h2><blockquote><p>安装时需要科学上网，主要是安装依赖时需要从海外网下载，如果要部署在国内服务器，可能会连接失败。<br>可以在海外的<code>相同配置</code>的服务器上进行编译，之后将编译好的<code>redistimeseries.so</code>上传到国内服务器即可。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p /usr/local/soft/modules/</span><br><span class="line"><span class="built_in">cd</span> /usr/local/soft/modules</span><br><span class="line"><span class="comment"># clone 代码</span></span><br><span class="line">git <span class="built_in">clone</span> https://github.com/aviggiano/redis-roaring.git</span><br><span class="line"><span class="built_in">cd</span> redis-roaring/</span><br><span class="line"><span class="comment"># 推荐切换到稳定的release版本</span></span><br><span class="line">git checkout v1.7.1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译构建，作者将下载子模块、编译安装等命令都封装到一个脚本中，这里直接执行脚本即可</span></span><br><span class="line">./configure.sh</span><br><span class="line"><span class="comment"># 如果没有错误，则说明编译成功，输出: dist/libredis-roaring.so</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>实际上<code>redis-roaring</code>在构建后会在<code>dist</code>目录下生成好 Redis 相关命令和配置文件，如果没有安装Redis则可以直接使用。<code>v1.7.1</code>版本提供的Redis的版本为<code>8.2.1</code>，但此时是不带 Stack 相关模块的。</p></li></ul><h2 id="Redis-启用模块">Redis 启用模块</h2><ul class="lvl-0"><li class="lvl-2"><p>将生成的 <code>libredis-roaring.so</code> 拷贝到 redis 的 modules 目录下（非必须），目录不存在则创建</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 注意 .so 文件需要包含可执行权限</span></span><br><span class="line"><span class="built_in">cp</span> dist/libredis-roaring.so /usr/local/soft/redis-7.4.7/modules/libredis-roaring.so</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>本文采用 <code>loadmodule</code> 加载模块</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 将 libredis-roaring.so 添加到 redis.conf 中，需要重启 redis</span></span><br><span class="line">loadmodule /usr/local/soft/redis-7.4.7/modules/libredis-roaring.so</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动redis</span></span><br><span class="line">redis-server redis.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># 登录测试</span></span><br><span class="line">redis-cli --user admin --pass 123456</span><br><span class="line"><span class="comment"># 查看模块</span></span><br><span class="line">127.0.0.1:6379&gt; MODULE LIST</span><br><span class="line"><span class="comment"># 输出</span></span><br><span class="line">1) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;ReJSON&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 20816</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/local/soft/redis-7.4.7/modules/rejson.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">2) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;timeseries&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 11209</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/local/soft/redis-7.4.7/modules/redistimeseries.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">3) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;search&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 21025</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/local/soft/redis-7.4.7/modules/redisearch.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">4) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;bf&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 20817</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/local/soft/redis-7.4.7/modules/redisbloom.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">5) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;REDIS-ROARING&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 10700</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/local/soft/redis-7.4.7/modules/libredis-roaring.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/08/redis7-module-RedisRoaring/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/08/redis7-module-RedisRoaring/"/>
    <published>2026-01-08T13:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 Redis 扩展模块 – RedisRoaring 的安装方法</li>
<li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li>
<li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li>
</ul>]]>
    </summary>
    <title>Redis 扩展模块 -- RedisRoaring 的安装方法</title>
    <updated>2026-01-08T09:44:21.470Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 Redis 扩展模块 – RedisTimeSeries 中 TimeSeries 数据类型</li><li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li><li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li><li class="lvl-2">RedisTimeSeries 的安装参见 <a href="/2026/01/07/redis7-module-RedisTimeSeries/" title="Redis 扩展模块 -- RedisTimeSeries 的安装方法">Redis 扩展模块 -- RedisTimeSeries 的安装方法</a></li></ul><span id="more"></span><h2 id="TimeSeries-简介">TimeSeries 简介</h2><ul class="lvl-0"><li class="lvl-2"><p>TimeSeries 是 Redis 的一个扩展模块，用于处理时间序列数据，<a href="https://redis.io/modules/redis-timeseries/?utm_source=chatgpt.com">RedisTimeSeries | A NoSQL Time Series Database</a></p></li><li class="lvl-2"><p>核心设计与数据模型</p></li></ul><blockquote><p>一个 Redis 时间序列（Time Series） 是有序的一系列采样数据点：</p></blockquote><table><thead><tr><th>组成部分</th><th>说明</th></tr></thead><tbody><tr><td><strong>时间戳（timestamp）</strong></td><td>毫秒级时间标签，可客户端指定或由服务器填充</td></tr><tr><td><strong>值（value）</strong></td><td>64 位浮点数</td></tr><tr><td><strong>元数据（labels）</strong></td><td>一组键值对，可用于过滤与聚合查询</td></tr><tr><td><strong>保留策略（retention）</strong></td><td>数据生命周期控制，过期自动丢弃</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>主要功能与特性</p></li></ul><table><thead><tr><th>特性</th><th>说明</th></tr></thead><tbody><tr><td><strong>高吞吐写入</strong></td><td>支持每秒接收大量数据点，延迟极低</td></tr><tr><td><strong>区间查询</strong></td><td>可按时间范围检索数据，如 <code>TS.RANGE</code> / <code>TS.MRANGE</code></td></tr><tr><td><strong>聚合查询</strong></td><td>内建 min、max、avg、sum、count、first、last 等聚合功能</td></tr><tr><td><strong>自动下采样 &amp; 累积规则</strong></td><td>通过 aggregation rules 自动生成更粗粒度指标</td></tr><tr><td><strong>保留策略（Retention）</strong></td><td>设置最大保存时间或样本数量</td></tr><tr><td><strong>标签索引（Labels）</strong></td><td>支持基于标签的跨序列查询</td></tr><tr><td><strong>高效存储</strong></td><td>使用压缩与内存结构优化空间成本</td></tr><tr><td><strong>工具整合</strong></td><td>支持 Grafana、Prometheus、Telegraf 等生态集成</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>应用场景</p></li></ul><table><thead><tr><th>场景</th><th>描述</th></tr></thead><tbody><tr><td><strong>物联网（IoT）监控</strong></td><td>温度、湿度、设备状态等</td></tr><tr><td><strong>系统监控与指标收集</strong></td><td>CPU、内存、网络指标的实时存储与分析</td></tr><tr><td><strong>业务指标分析</strong></td><td>每日/每小时活跃用户数量、请求延迟等</td></tr><tr><td><strong>金融时间序列</strong></td><td>股票价格、交易量的快速写入和分析</td></tr><tr><td><strong>实时告警</strong></td><td>数据越界时触发告警逻辑</td></tr></tbody></table><h2 id="TimeSeries-命令说明">TimeSeries 命令说明</h2><ul class="lvl-0"><li class="lvl-2"><p>按“生命周期”视角的快速索引</p></li></ul><table><thead><tr><th>阶段</th><th>对应命令</th></tr></thead><tbody><tr><td>创建</td><td>TS.CREATE</td></tr><tr><td>修改结构</td><td>TS.ALTER</td></tr><tr><td>建立聚合</td><td>TS.CREATERULE / TS.DELETERULE</td></tr><tr><td>写入</td><td>TS.ADD / TS.MADD / TS.INCRBY / TS.DECRBY</td></tr><tr><td>查询单序列</td><td>TS.GET / TS.RANGE / TS.REVRANGE</td></tr><tr><td>查询多序列</td><td>TS.MGET / TS.MRANGE / TS.MREVRANGE / TS.QUERYINDEX</td></tr><tr><td>清理数据</td><td>TS.DEL</td></tr><tr><td>查看状态</td><td><a href="http://TS.INFO">TS.INFO</a></td></tr></tbody></table><h3 id="一、时间序列创建与结构管理类">一、时间序列创建与结构管理类</h3><table><thead><tr><th>命令</th><th>功能说明</th><th>典型使用场景</th></tr></thead><tbody><tr><td><strong>TS.CREATE</strong></td><td>创建一个新的时间序列（可指定保留策略、标签、chunk 大小等）</td><td>初始化指标，如创建 CPU、QPS、延迟等指标序列</td></tr><tr><td><strong>TS.ALTER</strong></td><td>修改已有时间序列的配置（retention、chunk size、duplicate policy、labels）</td><td>动态调整数据保留周期、标签信息</td></tr><tr><td><strong>TS.CREATERULE</strong></td><td>创建压缩 / 聚合规则（从 source series 自动聚合到 dest series）</td><td>原始数据 → 按分钟 / 小时聚合</td></tr><tr><td><strong>TS.DELETERULE</strong></td><td>删除已有的压缩 / 聚合规则</td><td>停止某条自动聚合规则</td></tr></tbody></table><h4 id="1️⃣-TS-CREATE-创建时间序列">1️⃣ TS.CREATE : 创建时间序列</h4><ul class="lvl-0"><li class="lvl-2"><p>语法：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">TS.CREATE key</span><br><span class="line">  [RETENTION retentionPeriod]</span><br><span class="line">  [ENCODING &lt;COMPRESSED|UNCOMPRESSED&gt;]</span><br><span class="line">  [CHUNK_SIZE size]</span><br><span class="line">  [DUPLICATE_POLICY policy]</span><br><span class="line">  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]</span><br><span class="line">  [LABELS [label value ...]]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>参数说明：</p></li></ul><table><thead><tr><th>参数</th><th>作用</th><th>可选值 / 约束</th><th>默认值</th><th>典型使用建议</th></tr></thead><tbody><tr><td><strong>key</strong></td><td>时间序列的 Redis Key</td><td>任意合法 Redis key</td><td>无</td><td>建议包含业务语义，如 <code>ts:cpu:node1</code></td></tr><tr><td><strong>RETENTION</strong></td><td>数据保留时长（毫秒），超过自动删除</td><td>≥ 0，0 表示永久保留</td><td>0</td><td>高频指标必须设置，避免内存无限增长</td></tr><tr><td><strong>ENCODING</strong></td><td>数据存储编码方式</td><td><code>COMPRESSED</code>: 内存占用低，CPU 有轻微开销<br>  <code>UNCOMPRESSED</code>:查询快，占用内存高</td><td><code>COMPRESSED</code></td><td>绝大多数场景保持默认</td></tr><tr><td><strong>CHUNK_SIZE</strong></td><td>每个数据块的大小（字节），影响内存碎片与压缩效率</td><td>≥ 128 bytes（通常 1KB–64KB）</td><td>4096</td><td>写入频繁时适当调大</td></tr><tr><td><strong>DUPLICATE_POLICY</strong></td><td>同一 timestamp 重复写入时的处理策略</td><td>见下表</td><td>全局配置</td><td>明确指定，避免默认行为变化</td></tr><tr><td><strong>IGNORE</strong></td><td>忽略与上一点差异过小的数据</td><td><code>ignoreMaxTimediff</code>: 最大允许时间差（毫秒）<br> <code>ignoreMaxValDiff</code>: 最大允许的数值差</td><td>关闭</td><td>用于降噪和降采样 <br>示例：<code>IGNORE 1000 0.01</code>： 1 秒内，变化小于 0.01 的数据将被忽略</td></tr><tr><td><strong>LABELS</strong></td><td>标签键值对，用于索引和过滤</td><td>任意字符串对<br><code>LABELS key1 value1 key2 value2 ... </code></td><td>无</td><td>强烈建议用于查询</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>DUPLICATE_POLICY（重复时间戳策略）</p></li></ul><blockquote><p>当同一个 timestamp 被多次写入时生效</p></blockquote><table><thead><tr><th>策略</th><th>处理逻辑</th></tr></thead><tbody><tr><td><strong>BLOCK</strong></td><td>默认，重复时间戳直接失败</td></tr><tr><td><strong>FIRST</strong></td><td>保留已有值，忽略新值</td></tr><tr><td><strong>LAST</strong></td><td>覆盖已有值，用新值替代</td></tr><tr><td><strong>MIN</strong></td><td>仅当新值更小才覆盖</td></tr><tr><td><strong>MAX</strong></td><td>仅当新值更大才覆盖</td></tr><tr><td><strong>SUM</strong></td><td>累加值（已有值 + 新值）</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例: 创建一个 CPU 指标时间序列，自动过期 1 小时之前的数据</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">TS.CREATE ts:cpu:server1</span><br><span class="line">  RETENTION 3600000</span><br><span class="line">  ENCODING COMPRESSED</span><br><span class="line">  CHUNK_SIZE 8192</span><br><span class="line">  DUPLICATE_POLICY LAST</span><br><span class="line">  IGNORE 1000 0.1</span><br><span class="line">  LABELS host server1 metric cpu</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看数据类型</span></span><br><span class="line">127.0.0.1:6379&gt; <span class="built_in">type</span> ts:cpu:server1</span><br><span class="line">TSDB-TYPE</span><br></pre></td></tr></table></figure><h4 id="2️⃣-TS-ALTER-修改时间序列配置">2️⃣ TS.ALTER : 修改时间序列配置</h4><ul class="lvl-0"><li class="lvl-2"><p>语法：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">TS.ALTER key</span><br><span class="line">  [RETENTION retentionPeriod]</span><br><span class="line">  [CHUNK_SIZE size]</span><br><span class="line">  [DUPLICATE_POLICY policy]</span><br><span class="line">  [LABELS [label value ...]]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例：修改保留周期和标签</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">TS.ALTER ts:cpu:server1</span><br><span class="line">  RETENTION 7200000</span><br><span class="line">  LABELS host server1 metric cpu <span class="built_in">env</span> prod</span><br></pre></td></tr></table></figure><h4 id="3️⃣-TS-CREATERULE-创建自动聚合规则">3️⃣ TS.CREATERULE : 创建自动聚合规则</h4><ul class="lvl-0"><li class="lvl-2"><p>场景说明</p><ul class="lvl-2"><li class="lvl-6">原始序列：ts:cpu:server1（秒级数据）</li><li class="lvl-6">聚合目标：ts:cpu:server1:avg1m（按 1 分钟平均值聚合）</li></ul></li><li class="lvl-2"><p>语法：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">TS.CREATERULE sourceKey destKey</span><br><span class="line">  AGGREGATION aggregator bucketDuration</span><br><span class="line">  [alignTimestamp]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>参数说明</p></li></ul><table><thead><tr><th>参数</th><th>作用</th><th>可选值 / 约束</th><th>是否必填</th><th>行为说明</th><th>使用注意事项</th></tr></thead><tbody><tr><td><strong>sourceKey</strong></td><td>源时间序列 Key</td><td>必须是已存在的 TS key</td><td>是</td><td>新写入数据从该序列触发聚合</td><td>必须先创建</td></tr><tr><td><strong>destKey</strong></td><td>目标聚合时间序列 Key</td><td>必须是已存在的 TS key</td><td>是</td><td>聚合结果写入该序列</td><td>通常 retention 更长</td></tr><tr><td><strong>aggregator</strong></td><td>聚合函数</td><td>见下表</td><td>是</td><td>定义每个 bucket 内如何计算</td><td>不同函数计算成本不同</td></tr><tr><td><strong>bucketDuration</strong></td><td>聚合时间桶宽度（毫秒）</td><td>&gt; 0</td><td>是</td><td>控制聚合粒度</td><td>决定数据降采样程度</td></tr><tr><td><strong>alignTimestamp</strong></td><td>对齐时间戳基准</td><td>毫秒时间戳</td><td>否</td><td>控制 bucket 起始对齐方式</td><td>跨系统对齐时非常重要</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>aggregator（聚合函数）</p></li></ul><table><thead><tr><th>聚合器</th><th>说明</th></tr></thead><tbody><tr><td><strong>AVG</strong></td><td>平均值</td></tr><tr><td><strong>SUM</strong></td><td>求和</td></tr><tr><td><strong>MIN</strong></td><td>最小值</td></tr><tr><td><strong>MAX</strong></td><td>最大值</td></tr><tr><td><strong>COUNT</strong></td><td>样本数量</td></tr><tr><td><strong>FIRST</strong></td><td>第一个样本</td></tr><tr><td><strong>LAST</strong></td><td>最后一个样本</td></tr><tr><td><strong>RANGE</strong></td><td>MAX - MIN</td></tr><tr><td><strong>STD.P</strong></td><td>总体标准差</td></tr><tr><td><strong>STD.S</strong></td><td>样本标准差</td></tr><tr><td><strong>VAR.P</strong></td><td>总体方差</td></tr><tr><td><strong>VAR.S</strong></td><td>样本方差</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>alignTimestamp（对齐时间戳基准）</p></li></ul><table><thead><tr><th>类型</th><th>示例值</th><th>含义</th></tr></thead><tbody><tr><td><strong>0（Unix Epoch）</strong>（推荐做法）</td><td><code>0</code></td><td>从 1970-01-01 00:00:00 UTC 对齐</td></tr><tr><td><strong>任意固定时间戳</strong></td><td><code>1700000000000</code></td><td>从指定时间点对齐</td></tr><tr><td><strong>当前时间戳</strong></td><td><code>$(date +%s%3N)</code></td><td>从创建规则时刻对齐</td></tr><tr><td><strong>业务时间边界</strong></td><td>某天 00:00 的毫秒值</td><td>对齐到业务周期</td></tr><tr><td><strong>不指定</strong></td><td>（参数省略）</td><td>自动使用首条写入时间</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例: 每 60,000ms（1分钟）做一次平均聚合</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1.创建目标聚合序列</span></span><br><span class="line">TS.CREATE ts:cpu:server1:avg1m</span><br><span class="line">  RETENTION 86400000</span><br><span class="line">  LABELS host server1 metric cpu_1m</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2.创建自动聚合规则</span></span><br><span class="line">TS.CREATERULE ts:cpu:server1 ts:cpu:server1:avg1m</span><br><span class="line">  AGGREGATION AVG 60000</span><br><span class="line"><span class="comment">## 如果未设置 alignTimestamp，则以创建的第一条记录的时间为基准</span></span><br><span class="line"><span class="comment">## 比如第一条记录的时间为：12:00:23</span></span><br><span class="line"><span class="comment">## bucket 会对齐到:</span></span><br><span class="line">    <span class="comment"># 12:00:23 ~ 12:01:23</span></span><br><span class="line">    <span class="comment"># 12:01:23 ~ 12:02:23</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定 alignTimestamp 对齐到整分钟</span></span><br><span class="line">TS.CREATERULE ts:cpu:server1 ts:cpu:server1:avg1m</span><br><span class="line">  AGGREGATION AVG 60000 0</span><br><span class="line"><span class="comment">## bucket 会严格对齐:</span></span><br><span class="line">    <span class="comment"># 12:00:00 ~ 12:01:00</span></span><br><span class="line">    <span class="comment"># 12:01:00 ~ 12:02:00</span></span><br></pre></td></tr></table></figure><h4 id="4️⃣-TS-DELETERULE-删除自动聚合规则">4️⃣ TS.DELETERULE : 删除自动聚合规则</h4><ul class="lvl-0"><li class="lvl-2"><p>语法：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">TS.DELETERULE sourceKey destKey</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例：删除自动聚合规则</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">TS.DELETERULE ts:cpu:server1 ts:cpu:server1:avg1m</span><br></pre></td></tr></table></figure><h3 id="二、数据写入与更新类">二、数据写入与更新类</h3><table><thead><tr><th>命令</th><th>功能说明</th><th>典型使用场景</th></tr></thead><tbody><tr><td><strong>TS.ADD</strong></td><td>向时间序列追加一个样本点（timestamp, value）</td><td>实时写入监控数据</td></tr><tr><td><strong>TS.MADD</strong></td><td>批量向多个时间序列追加样本</td><td>高吞吐场景，减少网络 RTT</td></tr><tr><td><strong>TS.INCRBY</strong></td><td>对最新时间戳的值做自增（不存在则创建新样本）</td><td>计数器、累计指标</td></tr><tr><td><strong>TS.DECRBY</strong></td><td>对最新时间戳的值做自减（不存在则创建新样本）</td><td>库存、余额类递减指标</td></tr></tbody></table><h4 id="1️⃣-TS-ADD-向时间序列追加一个样本点">1️⃣ TS.ADD : 向时间序列追加一个样本点</h4><ul class="lvl-0"><li class="lvl-2"><p>语法：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">TS.ADD key timestamp value</span><br><span class="line">  [RETENTION retentionPeriod]</span><br><span class="line">  [ENCODING &lt;COMPRESSED|UNCOMPRESSED&gt;]</span><br><span class="line">  [CHUNK_SIZE size]</span><br><span class="line">  [DUPLICATE_POLICY policy]</span><br><span class="line">  [ON_DUPLICATE policy_ovr]</span><br><span class="line">  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]</span><br><span class="line">  [LABELS [label value ...]]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>如果 key 不存在，TS.ADD 会 隐式创建时间序列（类似 TS.CREATE）。</p></li><li class="lvl-2"><p>参数说明</p></li></ul><table><thead><tr><th>参数</th><th>作用</th><th>可选值 / 约束</th><th>是否覆盖已有配置</th><th>生效范围</th><th>使用注意事项</th></tr></thead><tbody><tr><td><strong>key</strong></td><td>时间序列 Key</td><td>合法 Redis key</td><td>—</td><td>—</td><td>不存在时自动创建</td></tr><tr><td><strong>timestamp</strong></td><td>样本时间戳</td><td>毫秒整数 / <code>*</code></td><td>—</td><td>当前写入</td><td><code>*</code> 表示服务器当前时间</td></tr><tr><td><strong>value</strong></td><td>样本值</td><td>浮点数</td><td>—</td><td>当前写入</td><td>NaN / Inf 不允许</td></tr><tr><td><strong>RETENTION</strong></td><td>设置保留周期</td><td>≥ 0（毫秒）</td><td>仅首次创建生效</td><td>序列级</td><td>已存在 key 不会被修改</td></tr><tr><td><strong>ENCODING</strong></td><td>存储编码</td><td>COMPRESSED / UNCOMPRESSED</td><td>仅首次创建生效</td><td>序列级</td><td>生产推荐 COMPRESSED</td></tr><tr><td><strong>CHUNK_SIZE</strong></td><td>数据块大小</td><td>≥ 128 bytes</td><td>仅首次创建生效</td><td>序列级</td><td>不影响已有 chunk</td></tr><tr><td><strong>DUPLICATE_POLICY</strong></td><td>设置默认重复策略</td><td>BLOCK / FIRST / LAST / MIN / MAX / SUM</td><td>仅首次创建生效</td><td>序列级</td><td>与 ON_DUPLICATE 有优先级关系</td></tr><tr><td><strong>ON_DUPLICATE</strong></td><td>本次写入的重复策略</td><td>同上</td><td>覆盖序列级策略</td><td>当前写入</td><td>推荐用于临时覆盖</td></tr><tr><td><strong>IGNORE</strong></td><td>忽略微小变化写入</td><td>两个阈值</td><td>仅首次创建生效</td><td>序列级</td><td>用于降噪</td></tr><tr><td><strong>LABELS</strong></td><td>设置标签</td><td>键值对</td><td>仅首次创建生效</td><td>序列级</td><td>已存在 key 不会修改</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例：写入一个时间序列</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">TS.ADD ts:cpu:server1 * 80.5</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">(<span class="built_in">integer</span>) 1767777655255 <span class="comment"># 当前时间戳</span></span><br></pre></td></tr></table></figure><h4 id="2️⃣-TS-MADD-批量向多个时间序列追加样本">2️⃣ TS.MADD : 批量向多个时间序列追加样本</h4><ul class="lvl-0"><li class="lvl-2"><p>语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">TS.MADD key timestamp value [key timestamp value ...]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例：批量写入多个时间序列</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 批量写入三个时间序列，时间序列必须存在</span></span><br><span class="line">TS.MADD ts:cpu:server1 * 80.5 ts:cpu:server2 * 90.5 ts:cpu:server3 * 70.5</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 1767777807933</span><br><span class="line">2) (<span class="built_in">integer</span>) 1767777807933</span><br><span class="line">3) (<span class="built_in">integer</span>) 1767777807933</span><br></pre></td></tr></table></figure><h4 id="3️⃣-TS-INCRBY-对最新时间戳的值做自增（不存在则创建新样本）">3️⃣ TS.INCRBY : 对最新时间戳的值做自增（不存在则创建新样本）</h4><ul class="lvl-0"><li class="lvl-2"><p>语法：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">TS.INCRBY key addend</span><br><span class="line">  [TIMESTAMP timestamp]</span><br><span class="line">  [RETENTION retentionPeriod]</span><br><span class="line">  [ENCODING &lt;COMPRESSED|UNCOMPRESSED&gt;]</span><br><span class="line">  [CHUNK_SIZE size]</span><br><span class="line">  [DUPLICATE_POLICY policy]</span><br><span class="line">  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]</span><br><span class="line">  [LABELS [label value ...]]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>参数说明</p></li></ul><table><thead><tr><th>参数</th><th>作用</th><th>可选值 / 约束</th><th>是否覆盖已有配置</th><th>生效范围</th><th>使用注意事项</th></tr></thead><tbody><tr><td><strong>key</strong></td><td>时间序列 Key</td><td>合法 Redis key</td><td>—</td><td>—</td><td>不存在会自动创建</td></tr><tr><td><strong>addend</strong></td><td>增量值</td><td>浮点数</td><td>—</td><td>当前写入</td><td>支持负数（等价 DECR）</td></tr><tr><td><strong>TIMESTAMP</strong></td><td>指定写入时间戳</td><td>毫秒整数</td><td>—</td><td>当前写入</td><td>默认使用服务器时间</td></tr><tr><td><strong>RETENTION</strong></td><td>设置保留周期</td><td>≥ 0（毫秒）</td><td>仅首次创建生效</td><td>序列级</td><td>已存在 key 不生效</td></tr><tr><td><strong>ENCODING</strong></td><td>存储编码</td><td>COMPRESSED / UNCOMPRESSED</td><td>仅首次创建生效</td><td>序列级</td><td>推荐 COMPRESSED</td></tr><tr><td><strong>CHUNK_SIZE</strong></td><td>数据块大小</td><td>≥ 128 bytes</td><td>仅首次创建生效</td><td>序列级</td><td>高频写入可调大</td></tr><tr><td><strong>DUPLICATE_POLICY</strong></td><td>默认重复时间戳策略</td><td>BLOCK / FIRST / LAST / MIN / MAX / SUM</td><td>仅首次创建生效</td><td>序列级</td><td>对计数器通常设 SUM</td></tr><tr><td><strong>IGNORE</strong></td><td>忽略微小变化</td><td>两个阈值</td><td>仅首次创建生效</td><td>序列级</td><td>计数器一般不使用</td></tr><tr><td><strong>LABELS</strong></td><td>标签</td><td>键值对</td><td>仅首次创建生效</td><td>序列级</td><td>用于 MGET / MRANGE</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>如果 key 不存在，TS.INCRBY 会 隐式创建时间序列。</p></li><li class="lvl-2"><p>如果没有指定 TIMESTAMP，则自动使用服务器当前时间。</p></li><li class="lvl-2"><p>写入逻辑等价于：读取最新值 + addend → 写回一个新样本。</p></li><li class="lvl-2"><p>示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 对最新时间戳的值做自增</span></span><br><span class="line">TS.INCRBY ts:cpu:server1 10</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">(<span class="built_in">integer</span>) 1767778308125</span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定时间戳，TIMESTAMP 必须大于或等于当前时间序列中最大的时间戳</span></span><br><span class="line">TS.INCRBY ts:cpu:server1 5 TIMESTAMP 1767778308126</span><br></pre></td></tr></table></figure><h4 id="4️⃣-TS-DECRBY-对最新时间戳的值做自减（不存在则创建新样本）">4️⃣ TS.DECRBY : 对最新时间戳的值做自减（不存在则创建新样本）</h4><ul class="lvl-0"><li class="lvl-2"><p>与 <code>TS.INCRBY</code> 相似，不再赘述。</p></li><li class="lvl-2"><p>实际上 <code>TS.INCRBY</code> 中设置 <code>addend</code> 为负数 就是 <code>TS.DECR</code></p></li></ul><h3 id="三、数据查询（单序列）类">三、数据查询（单序列）类</h3><table><thead><tr><th>命令</th><th>功能说明</th><th>典型使用场景</th></tr></thead><tbody><tr><td><strong>TS.GET</strong></td><td>获取某个时间序列最新（最大 timestamp）的样本</td><td>获取当前最新指标值</td></tr><tr><td><strong>TS.RANGE</strong></td><td>按时间正序查询一个时间序列的区间数据</td><td>绘图、趋势分析</td></tr><tr><td><strong>TS.REVRANGE</strong></td><td>按时间逆序查询一个时间序列的区间数据</td><td>获取最近 N 条数据</td></tr></tbody></table><h4 id="1️⃣-TS-GET-获取某个时间序列最新（最大-timestamp）的样本">1️⃣ TS.GET : 获取某个时间序列最新（最大 timestamp）的样本</h4><ul class="lvl-0"><li class="lvl-2"><p>语法：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">TS.GET key [LATEST]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>参数说明：</p></li></ul><table><thead><tr><th>参数</th><th>作用</th></tr></thead><tbody><tr><td><strong>key</strong></td><td>时间序列 Key</td></tr><tr><td><strong>LATEST</strong></td><td>获取最新样本</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>LATEST 说明</p><ul class="lvl-2"><li class="lvl-6">当一个时间序列是经过聚合（compaction）处理的时，LATEST 参数会生效。使用 LATEST 时，TS.GET 会返回最新（可能是部分的）桶的聚合值。不使用 LATEST 时，TS.GET 不会返回最新（可能是部分的）桶的值。当时间序列不是聚合的时，LATEST 会被忽略。</li><li class="lvl-6">最新桶中的数据可能是不完整的。只有当有新的样本到来并开启一个新的最新桶时，原桶才会被关闭并进行聚合。然而，在某些情况下，也需要获取最新（可能是部分的）桶的聚合值，此时就可以使用 LATEST。</li></ul></li><li class="lvl-2"><p>示例：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">TS.GET ts:cpu:server1 LATEST</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 1767778308126</span><br><span class="line">2) 115.5</span><br></pre></td></tr></table></figure><h4 id="2️⃣-TS-RANGE-按时间正序查询一个时间序列的区间数据">2️⃣ TS.RANGE : 按时间正序查询一个时间序列的区间数据</h4><ul class="lvl-0"><li class="lvl-2"><p>语法：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">TS.RANGE key fromTimestamp toTimestamp</span><br><span class="line">  [LATEST]</span><br><span class="line">  [FILTER_BY_TS ts...]</span><br><span class="line">  [FILTER_BY_VALUE min max]</span><br><span class="line">  [COUNT count]</span><br><span class="line">  [[ALIGN align] AGGREGATION aggregator bucketDuration [BUCKETTIMESTAMP bt] [EMPTY]]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>参数说明：</p></li></ul><table><thead><tr><th>参数</th><th>类型</th><th>说明</th><th>可选值 / 说明</th></tr></thead><tbody><tr><td><code>key</code></td><td>string</td><td>时间序列的键名</td><td>必选</td></tr><tr><td><code>fromTimestamp</code></td><td>integer</td><td>查询起始时间戳，<code>-</code>: 最小值</td><td>必选</td></tr><tr><td><code>toTimestamp</code></td><td>integer</td><td>查询结束时间戳 ，<code>+</code>: 最大值</td><td>必选</td></tr><tr><td><code>LATEST</code></td><td>flag</td><td>当时间序列是 compaction 时，返回最新（可能部分）的桶的聚合值</td><td>可选</td></tr><tr><td><code>FILTER_BY_TS ts...</code></td><td>list of integers</td><td>只返回指定时间戳的样本</td><td>可选</td></tr><tr><td><code>FILTER_BY_VALUE min max</code></td><td>range</td><td>只返回值在 <code>[min, max]</code> 区间的样本</td><td>可选</td></tr><tr><td><code>COUNT count</code></td><td>integer</td><td>限制返回的样本数量（最多 <code>count</code> 个）</td><td>可选</td></tr><tr><td><code>ALIGN align</code></td><td>string</td><td>对齐聚合桶的时间戳（如 <code>start</code>、<code>end</code>、自定义时间戳）</td><td>可选，需与 <code>AGGREGATION</code> 一起使用</td></tr><tr><td><code>AGGREGATION aggregator bucketDuration</code></td><td>aggregation</td><td>对数据进行聚合计算，<code>aggregator</code> 为聚合函数（如 <code>avg</code>, <code>sum</code>, <code>min</code>, <code>max</code> 等），<code>bucketDuration</code> 为桶的时长</td><td>可选</td></tr><tr><td><code>BUCKETTIMESTAMP bt</code></td><td>string</td><td>聚合桶时间戳选择，<code>bt</code> 可为 <code>start</code> 或 <code>end</code></td><td>可选</td></tr><tr><td><code>EMPTY</code></td><td>flag</td><td>即使桶为空也返回结果</td><td>可选</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查询时间序列 ts:cpu:server1 的区间数据</span></span><br><span class="line">TS.RANGE ts:cpu:server1 0 1767778308126</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) 1) (<span class="built_in">integer</span>) 1767777655255</span><br><span class="line">   2) 80.5</span><br><span class="line">2) 1) (<span class="built_in">integer</span>) 1767777807933</span><br><span class="line">   2) 70.5</span><br><span class="line">3) 1) (<span class="built_in">integer</span>) 1767777873866</span><br><span class="line">   2) 80.5</span><br><span class="line">4) 1) (<span class="built_in">integer</span>) 1767777965870</span><br><span class="line">   2) 80.5</span><br><span class="line">5) 1) (<span class="built_in">integer</span>) 1767778300909</span><br><span class="line">   2) 90.5</span><br><span class="line">6) 1) (<span class="built_in">integer</span>) 1767778308125</span><br><span class="line">   2) 105.5</span><br><span class="line">7) 1) (<span class="built_in">integer</span>) 1767778308126</span><br><span class="line">   2) 115.5</span><br><span class="line"><span class="comment"># 查询时间序列 ts:cpu:server1 的区间数据，并返回聚合结果，求1000ms内平均值</span></span><br><span class="line">TS.RANGE ts:cpu:server1 - + AGGREGATION avg 1000</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) 1) (<span class="built_in">integer</span>) 1767777655000</span><br><span class="line">   2) 80.5</span><br><span class="line">2) 1) (<span class="built_in">integer</span>) 1767777807000</span><br><span class="line">   2) 70.5</span><br><span class="line">3) 1) (<span class="built_in">integer</span>) 1767777873000</span><br><span class="line">   2) 80.5</span><br><span class="line">4) 1) (<span class="built_in">integer</span>) 1767777965000</span><br><span class="line">   2) 80.5</span><br><span class="line">5) 1) (<span class="built_in">integer</span>) 1767778300000</span><br><span class="line">   2) 90.5</span><br><span class="line">6) 1) (<span class="built_in">integer</span>) 1767778308000</span><br><span class="line">   2) 110.5</span><br></pre></td></tr></table></figure><h4 id="3️⃣-TS-REVRANGE-按时间逆序查询一个时间序列的区间数据">3️⃣ TS.REVRANGE : 按时间逆序查询一个时间序列的区间数据</h4><ul class="lvl-0"><li class="lvl-2"><p>与 TS.RANGE 类似，只是返回结果是倒序的</p></li><li class="lvl-2"><p>示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">TS.REVRANGE ts:cpu:server1 0 1767778308126</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) 1) (<span class="built_in">integer</span>) 1767778308126</span><br><span class="line">   2) 115.5</span><br><span class="line">2) 1) (<span class="built_in">integer</span>) 1767778308125</span><br><span class="line">   2) 105.5</span><br><span class="line">3) 1) (<span class="built_in">integer</span>) 1767778300909</span><br><span class="line">   2) 90.5</span><br><span class="line">4) 1) (<span class="built_in">integer</span>) 1767777965870</span><br><span class="line">   2) 80.5</span><br><span class="line">5) 1) (<span class="built_in">integer</span>) 1767777873866</span><br><span class="line">   2) 80.5</span><br><span class="line">6) 1) (<span class="built_in">integer</span>) 1767777807933</span><br><span class="line">   2) 70.5</span><br><span class="line">7) 1) (<span class="built_in">integer</span>) 1767777655255</span><br><span class="line">   2) 80.5</span><br></pre></td></tr></table></figure><h3 id="四、数据查询（多序列-聚合）类">四、数据查询（多序列 / 聚合）类</h3><table><thead><tr><th>命令</th><th>功能说明</th><th>典型使用场景</th></tr></thead><tbody><tr><td><strong>TS.MGET</strong></td><td>根据标签过滤，获取多个时间序列的最新值</td><td>批量获取多个实例的当前指标</td></tr><tr><td><strong>TS.MRANGE</strong></td><td>按标签过滤，正序查询多个时间序列的区间数据</td><td>多维指标分析、聚合</td></tr><tr><td><strong>TS.MREVRANGE</strong></td><td>按标签过滤，逆序查询多个时间序列的区间数据</td><td>最近数据聚合分析</td></tr><tr><td><strong>TS.QUERYINDEX</strong></td><td>根据标签过滤，返回匹配的时间序列 key 列表</td><td>发现有哪些指标符合条件</td></tr></tbody></table><h4 id="1️⃣-TS-MGET-根据标签过滤，获取多个时间序列的最新值">1️⃣ TS.MGET : 根据标签过滤，获取多个时间序列的最新值</h4><ul class="lvl-0"><li class="lvl-2"><p>语法：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">TS.MGET [LATEST] [WITHLABELS | &lt;SELECTED_LABELS label...&gt;] FILTER filterExpr...</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>参数说明：</p></li></ul><table><thead><tr><th>参数</th><th>类型</th><th>说明</th><th>可选值 / 说明</th></tr></thead><tbody><tr><td><code>LATEST</code></td><td>flag</td><td>对 compaction 类型的时间序列，返回最新（可能部分）的样本值</td><td>可选</td></tr><tr><td><code>WITHLABELS</code></td><td>flag</td><td>返回样本的同时附带时间序列的所有标签</td><td>可选</td></tr><tr><td><code>&lt;SELECTED_LABELS label...&gt;</code></td><td>list</td><td>返回样本时只附带指定标签</td><td>可选，不能与 <code>WITHLABELS</code> 一起使用</td></tr><tr><td><code>FILTER filterExpr...</code></td><td>list of expressions</td><td>过滤时间序列，根据标签匹配规则筛选出符合条件的序列</td><td>必选，支持标签匹配表达式，如 <code>sensor=temperature</code> 或 <code>region=*</code></td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 获取所有时间序列的最新值，根据标签过滤</span></span><br><span class="line">TS.MGET WITHLABELS FILTER metric=cpu</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) 1) <span class="string">&quot;ts:cpu:server1&quot;</span></span><br><span class="line">   2) 1) 1) <span class="string">&quot;host&quot;</span></span><br><span class="line">         2) <span class="string">&quot;server1&quot;</span></span><br><span class="line">      2) 1) <span class="string">&quot;metric&quot;</span></span><br><span class="line">         2) <span class="string">&quot;cpu&quot;</span></span><br><span class="line">      3) 1) <span class="string">&quot;env&quot;</span></span><br><span class="line">         2) <span class="string">&quot;prod&quot;</span></span><br><span class="line">   3) 1) (<span class="built_in">integer</span>) 1767778308126</span><br><span class="line">      2) 115.5</span><br><span class="line">2) 1) <span class="string">&quot;ts:cpu:server2&quot;</span></span><br><span class="line">   2) 1) 1) <span class="string">&quot;host&quot;</span></span><br><span class="line">         2) <span class="string">&quot;server1&quot;</span></span><br><span class="line">      2) 1) <span class="string">&quot;metric&quot;</span></span><br><span class="line">         2) <span class="string">&quot;cpu&quot;</span></span><br><span class="line">   3) 1) (<span class="built_in">integer</span>) 1767777965870</span><br><span class="line">      2) 90.5</span><br><span class="line">3) 1) <span class="string">&quot;ts:cpu:server3&quot;</span></span><br><span class="line">   2) 1) 1) <span class="string">&quot;host&quot;</span></span><br><span class="line">         2) <span class="string">&quot;server1&quot;</span></span><br><span class="line">      2) 1) <span class="string">&quot;metric&quot;</span></span><br><span class="line">         2) <span class="string">&quot;cpu&quot;</span></span><br><span class="line">   3) 1) (<span class="built_in">integer</span>) 1767777965870</span><br><span class="line">      2) 70.5</span><br></pre></td></tr></table></figure><h4 id="2️⃣-TS-MRANGE-按标签过滤，正序查询多个时间序列的区间数据">2️⃣ TS.MRANGE : 按标签过滤，正序查询多个时间序列的区间数据</h4><ul class="lvl-0"><li class="lvl-2"><p>语法：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">TS.MRANGE fromTimestamp toTimestamp</span><br><span class="line">  [LATEST]</span><br><span class="line">  [FILTER_BY_TS ts...]</span><br><span class="line">  [FILTER_BY_VALUE min max]</span><br><span class="line">  [WITHLABELS | &lt;SELECTED_LABELS label...&gt;]</span><br><span class="line">  [COUNT count]</span><br><span class="line">  [[ALIGN align] AGGREGATION aggregator bucketDuration [BUCKETTIMESTAMP bt] [EMPTY]]</span><br><span class="line">  FILTER filterExpr...</span><br><span class="line">  [GROUPBY label REDUCE reducer]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>参数说明：</p></li></ul><table><thead><tr><th>参数</th><th>类型</th><th>说明</th><th>可选值 / 说明</th></tr></thead><tbody><tr><td><code>fromTimestamp</code></td><td>integer</td><td>查询起始时间戳，<code>-</code>: 最小值</td><td>必选</td></tr><tr><td><code>toTimestamp</code></td><td>integer</td><td>查询结束时间戳，<code>+</code>: 最大值</td><td>必选</td></tr><tr><td><code>LATEST</code></td><td>flag</td><td>对 compaction 类型的时间序列，返回最新（可能部分）的桶的聚合值</td><td>可选</td></tr><tr><td><code>FILTER_BY_TS ts...</code></td><td>list of integers</td><td>只返回指定时间戳的样本</td><td>可选</td></tr><tr><td><code>FILTER_BY_VALUE min max</code></td><td>range</td><td>只返回值在 <code>[min, max]</code> 区间的样本</td><td>可选</td></tr><tr><td><code>WITHLABELS</code></td><td>flag</td><td>返回样本的同时附带时间序列的所有标签</td><td>可选</td></tr><tr><td><code>&lt;SELECTED_LABELS label...&gt;</code></td><td>list</td><td>返回样本时只附带指定标签</td><td>可选，不能与 <code>WITHLABELS</code> 一起使用</td></tr><tr><td><code>COUNT count</code></td><td>integer</td><td>限制返回的样本数量（最多 <code>count</code> 个）</td><td>可选</td></tr><tr><td><code>ALIGN align</code></td><td>string</td><td>对齐聚合桶的时间戳（如 <code>start</code>、<code>end</code> 或自定义时间戳）</td><td>可选，需与 <code>AGGREGATION</code> 一起使用</td></tr><tr><td><code>AGGREGATION aggregator bucketDuration</code></td><td>aggregation</td><td>对数据进行聚合计算，<code>aggregator</code> 为聚合函数（如 <code>avg</code>, <code>sum</code>, <code>min</code>, <code>max</code> 等），<code>bucketDuration</code> 为桶的时长</td><td>可选</td></tr><tr><td><code>BUCKETTIMESTAMP bt</code></td><td>string</td><td>聚合桶时间戳选择，<code>bt</code> 可为 <code>start</code> 或 <code>end</code></td><td>可选</td></tr><tr><td><code>EMPTY</code></td><td>flag</td><td>即使桶为空也返回结果</td><td>可选</td></tr><tr><td><code>FILTER filterExpr...</code></td><td>list of expressions</td><td>过滤时间序列，根据标签匹配规则筛选出符合条件的序列</td><td>必选</td></tr><tr><td><code>GROUPBY label REDUCE reducer</code></td><td>aggregation</td><td>对返回结果按指定标签分组并应用 reducer 聚合函数（如 <code>SUM</code>, <code>MIN</code>, <code>MAX</code> 等）</td><td>可选</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">TS.MRANGE - + FILTER metric=cpu</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) 1) <span class="string">&quot;ts:cpu:server1&quot;</span></span><br><span class="line">   2) (empty array)</span><br><span class="line">   3) 1) 1) (<span class="built_in">integer</span>) 1767777655255</span><br><span class="line">         2) 80.5</span><br><span class="line">      2) 1) (<span class="built_in">integer</span>) 1767777807933</span><br><span class="line">         2) 70.5</span><br><span class="line">      3) 1) (<span class="built_in">integer</span>) 1767777873866</span><br><span class="line">         2) 80.5</span><br><span class="line">      4) 1) (<span class="built_in">integer</span>) 1767777965870</span><br><span class="line">         2) 80.5</span><br><span class="line">      5) 1) (<span class="built_in">integer</span>) 1767778300909</span><br><span class="line">         2) 90.5</span><br><span class="line">      6) 1) (<span class="built_in">integer</span>) 1767778308125</span><br><span class="line">         2) 105.5</span><br><span class="line">      7) 1) (<span class="built_in">integer</span>) 1767778308126</span><br><span class="line">         2) 115.5</span><br><span class="line">2) 1) <span class="string">&quot;ts:cpu:server2&quot;</span></span><br><span class="line">   2) (empty array)</span><br><span class="line">   3) 1) 1) (<span class="built_in">integer</span>) 1767777965870</span><br><span class="line">         2) 90.5</span><br><span class="line">3) 1) <span class="string">&quot;ts:cpu:server3&quot;</span></span><br><span class="line">   2) (empty array)</span><br><span class="line">   3) 1) 1) (<span class="built_in">integer</span>) 1767777965870</span><br><span class="line">         2) 70.5</span><br><span class="line"></span><br><span class="line"><span class="comment"># 按标签聚合</span></span><br><span class="line">TS.MRANGE - +</span><br><span class="line">    AGGREGATION avg 1000</span><br><span class="line">    FILTER metric=cpu</span><br><span class="line">    GROUPBY host REDUCE avg</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) 1) <span class="string">&quot;host=server1&quot;</span></span><br><span class="line">   2) (empty array)</span><br><span class="line">   3) 1) 1) (<span class="built_in">integer</span>) 1767777655000</span><br><span class="line">         2) 80.5</span><br><span class="line">      2) 1) (<span class="built_in">integer</span>) 1767777807000</span><br><span class="line">         2) 70.5</span><br><span class="line">      3) 1) (<span class="built_in">integer</span>) 1767777873000</span><br><span class="line">         2) 80.5</span><br><span class="line">      4) 1) (<span class="built_in">integer</span>) 1767777965000</span><br><span class="line">         2) 80.5</span><br><span class="line">      5) 1) (<span class="built_in">integer</span>) 1767778300000</span><br><span class="line">         2) 90.5</span><br><span class="line">      6) 1) (<span class="built_in">integer</span>) 1767778308000</span><br><span class="line">         2) 110.5</span><br></pre></td></tr></table></figure><h4 id="3️⃣-TS-MREVRANGE-按标签过滤，逆序查询多个时间序列的区间数据">3️⃣ TS.MREVRANGE : 按标签过滤，逆序查询多个时间序列的区间数据</h4><ul class="lvl-0"><li class="lvl-2"><p>与 TS.MRANGE 类似，只是查询结果是逆序的</p></li></ul><h4 id="4️⃣-TS-QUERYINDEX-根据标签过滤，返回匹配的时间序列-key-列表">4️⃣ TS.QUERYINDEX : 根据标签过滤，返回匹配的时间序列 key 列表</h4><ul class="lvl-0"><li class="lvl-2"><p>语法：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">S.QUERYINDEX filterExpr...</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">TS.QUERYINDEX metric=cpu host=server1</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) <span class="string">&quot;ts:cpu:server1&quot;</span></span><br><span class="line">2) <span class="string">&quot;ts:cpu:server2&quot;</span></span><br><span class="line">3) <span class="string">&quot;ts:cpu:server3&quot;</span></span><br></pre></td></tr></table></figure><h3 id="五、数据删除与维护类">五、数据删除与维护类</h3><table><thead><tr><th>命令</th><th>功能说明</th><th>典型使用场景</th></tr></thead><tbody><tr><td><strong>TS.DEL</strong></td><td>删除某个时间序列在指定时间范围内的样本</td><td>清理异常数据、历史数据修正</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">TS.DEL key fromTimestamp toTimestamp</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">TS.DEL ts:cpu:server1 0 1767777965870</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">(<span class="built_in">integer</span>) 4 <span class="comment"># 删除了 4 个样本</span></span><br></pre></td></tr></table></figure><h3 id="六、元数据与状态查询类">六、元数据与状态查询类</h3><table><thead><tr><th>命令</th><th>功能说明</th><th>典型使用场景</th></tr></thead><tbody><tr><td><strong><a href="http://TS.INFO">TS.INFO</a></strong></td><td>返回时间序列的元信息与统计信息</td><td>排查问题、容量评估、监控状态</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">TS.INFO key [DEBUG]</span><br><span class="line"><span class="comment"># DEBUG: 显示更为详细的信息</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">TS.INFO ts:cpu:server1</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) totalSamples</span><br><span class="line"> 2) (<span class="built_in">integer</span>) 3</span><br><span class="line"> 3) memoryUsage</span><br><span class="line"> 4) (<span class="built_in">integer</span>) 9184</span><br><span class="line"> 5) firstTimestamp</span><br><span class="line"> 6) (<span class="built_in">integer</span>) 1767778300909</span><br><span class="line"> 7) lastTimestamp</span><br><span class="line"> 8) (<span class="built_in">integer</span>) 1767778308126</span><br><span class="line"> 9) retentionTime</span><br><span class="line">10) (<span class="built_in">integer</span>) 7200000</span><br><span class="line">11) chunkCount</span><br><span class="line">12) (<span class="built_in">integer</span>) 1</span><br><span class="line">13) chunkSize</span><br><span class="line">14) (<span class="built_in">integer</span>) 8192</span><br><span class="line">15) chunkType</span><br><span class="line">16) compressed</span><br><span class="line">17) duplicatePolicy</span><br><span class="line">18) last</span><br><span class="line">19) labels</span><br><span class="line">20) 1) 1) <span class="string">&quot;host&quot;</span></span><br><span class="line">       2) <span class="string">&quot;server1&quot;</span></span><br><span class="line">    2) 1) <span class="string">&quot;metric&quot;</span></span><br><span class="line">       2) <span class="string">&quot;cpu&quot;</span></span><br><span class="line">    3) 1) <span class="string">&quot;env&quot;</span></span><br><span class="line">       2) <span class="string">&quot;prod&quot;</span></span><br><span class="line">21) sourceKey</span><br><span class="line">22) (nil)</span><br><span class="line">23) rules</span><br><span class="line">24) (empty array)</span><br><span class="line">25) ignoreMaxTimeDiff</span><br><span class="line">26) (<span class="built_in">integer</span>) 1000</span><br><span class="line">27) ignoreMaxValDiff</span><br><span class="line">28) <span class="string">&quot;0.1&quot;</span></span><br></pre></td></tr></table></figure><h2 id="真实案例">真实案例</h2><h3 id="场景说明">场景说明</h3><ul class="lvl-0"><li class="lvl-2"><p>假设我们有一个 IoT 设备监控系统，每台设备每分钟会上报一次温度和湿度数据。我们希望：</p><ul class="lvl-2"><li class="lvl-6">存储每台设备的时间序列数据。</li><li class="lvl-6">查询最近一小时的数据。</li><li class="lvl-6">统计每 5 分钟的平均温度和湿度。</li><li class="lvl-6">获取最新的温度值。</li></ul></li></ul><h3 id="步骤">步骤</h3><ul class="lvl-0"><li class="lvl-2"><ol><li class="lvl-5">创建时间序列</li></ol></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建设备 device:1001 的温度时间序列</span></span><br><span class="line">TS.CREATE device:1001:temperature RETENTION 86400000 LABELS device_id 1001 <span class="built_in">type</span> temperature</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建设备 device:1001 的湿度时间序列</span></span><br><span class="line">TS.CREATE device:1001:humidity RETENTION 86400000 LABELS device_id 1001 <span class="built_in">type</span> humidity</span><br><span class="line"></span><br><span class="line"><span class="comment">## 说明：</span></span><br><span class="line">    <span class="comment"># RETENTION 86400000：数据保留 24 小时（单位毫秒）。</span></span><br><span class="line">    <span class="comment"># LABELS：方便后续按标签聚合查询。</span></span><br><span class="line">    <span class="comment"># TS.CREATE 用于创建时间序列 key。</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><ol start="2"><li class="lvl-5">插入数据</li></ol></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 假设时间戳为当前毫秒</span></span><br><span class="line">TS.ADD device:1001:temperature * 26.5</span><br><span class="line">TS.ADD device:1001:humidity * 60.2</span><br><span class="line"><span class="comment"># 此处假设会每分钟插入一次数据</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><ol start="3"><li class="lvl-5">查询时间范围数据</li></ol></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 获取 过去一小时的温度数据：</span></span><br><span class="line"><span class="comment"># 过去一小时的时间戳可以这样获得</span></span><br><span class="line">EVAL <span class="string">&quot;return redis.call(&#x27;TIME&#x27;)[1]*1000 - 3600000&quot;</span> 0</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">(<span class="built_in">integer</span>) 1767777794000</span><br><span class="line"></span><br><span class="line">TS.RANGE device:1001:temperature 1767777794000 +</span><br><span class="line"><span class="comment">## 说明</span></span><br><span class="line">    <span class="comment"># -3600000 表示一小时前。</span></span><br><span class="line">    <span class="comment"># + 表示到现在。</span></span><br><span class="line">    <span class="comment"># 返回结果为 [timestamp, value] 数组。</span></span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) 1) (<span class="built_in">integer</span>) 1767781256846</span><br><span class="line">   2) 26.5</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><ol start="4"><li class="lvl-5">聚合查询（Downsampling）</li></ol></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 统计 每 5 分钟平均温度：</span></span><br><span class="line">TS.RANGE device:1001:temperature 1767777794000 + AGGREGATION avg 300000</span><br><span class="line"><span class="comment"># 说明：</span></span><br><span class="line">    <span class="comment"># AGGREGATION avg 300000 表示每 300,000 ms（5 分钟）聚合一次。</span></span><br><span class="line">    <span class="comment"># RedisTimeSeries 支持多种聚合函数：avg, sum, min, max, count。</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><ol start="5"><li class="lvl-5">获取最新值</li></ol></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 获取最新的温度值：</span></span><br><span class="line">TS.GET device:1001:temperature</span><br><span class="line"><span class="comment"># 说明</span></span><br><span class="line">    <span class="comment"># TS.GET 会返回最新一个数据点 [timestamp, value]。</span></span><br><span class="line">    <span class="comment"># 如果使用了压缩策略 LATEST，返回的值可能是当前聚合桶的值。</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><ol start="6"><li class="lvl-5">按标签查询</li></ol></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 如果我们想获取 所有设备的温度平均值（假设有很多设备）：</span></span><br><span class="line">TS.MRANGE - + AGGREGATION avg 300000 FILTER <span class="built_in">type</span>=temperature</span><br><span class="line"><span class="comment"># 说明：</span></span><br><span class="line">    <span class="comment"># TS.MRANGE 支持多 key 查询，并且可以按标签过滤。</span></span><br><span class="line">    <span class="comment"># 聚合函数可以对每个匹配 key 单独计算。</span></span><br><span class="line"><span class="comment"># 输出</span></span><br><span class="line">1) 1) <span class="string">&quot;device:1001:temperature&quot;</span></span><br><span class="line">   2) (empty array)</span><br><span class="line">   3) 1) 1) (<span class="built_in">integer</span>) 1767781200000</span><br><span class="line">         2) 26.5</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/07/redis7-datatype-18-TimeSeries/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/07/redis7-datatype-18-TimeSeries/"/>
    <published>2026-01-07T14:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 Redis 扩展模块 – RedisTimeSeries 中 TimeSeries 数据类型</li>
<li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li>
<li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li>
<li class="lvl-2">RedisTimeSeries 的安装参见 <a href="/2026/01/07/redis7-module-RedisTimeSeries/" title="Redis 扩展模块 -- RedisTimeSeries 的安装方法">Redis 扩展模块 -- RedisTimeSeries 的安装方法</a></li>
</ul>]]>
    </summary>
    <title>Redis 命令及数据类型 -- TimeSeries</title>
    <updated>2026-01-07T10:32:15.918Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 Redis 扩展模块 – RedisTimeSeries 的安装方法</li><li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li><li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li></ul><span id="more"></span><h2 id="RedisTimeSeries-简介">RedisTimeSeries 简介</h2><ul class="lvl-0"><li class="lvl-2"><p><a href="https://github.com/RedisTimeSeries/RedisTimeSeries">RedisTimeSeries</a> 是 Redis 官方提供的一个 时序数据模块（Time Series Module），用于高效地存储、查询和分析时间序列数据。它可以作为 Redis 的一个模块加载使用（在 Redis Stack 中内置支持），将时序数据建模、存储和聚合能力扩展到 Redis 之外。</p></li><li class="lvl-2"><p>该模块以 <code>Redis Module</code> 方式加载，可无缝集成到现有 Redis 实例中。</p></li><li class="lvl-2"><p>Redis8+，RedisTimeSeries 已经内置在 Redis 中，可以在安装redis同时安装全部 Stack 模块。</p></li></ul><h2 id="安装-RedisTimeSeries">安装 RedisTimeSeries</h2><ul class="lvl-0"><li class="lvl-2"><p>虽然<a href="https://cloud.redis.io">Redis Cloud</a>的<code>Download Center</code>中提供了所有Redis模块编译后的<code>.so</code>文件，但是并不保证一定兼容，所以最稳妥的方式是通过源码自己编译。</p></li></ul><blockquote><p>安装时需要科学上网，主要是安装依赖时需要从海外网下载，如果要部署在国内服务器，可能会连接失败。<br>可以在海外的<code>相同配置</code>的服务器上进行编译，之后将编译好的<code>redistimeseries.so</code>上传到国内服务器即可。</p></blockquote><ul class="lvl-0"><li class="lvl-2"><p>安装依赖</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> dnf install -y \</span><br><span class="line">  gcc \</span><br><span class="line">  gcc-c++ \</span><br><span class="line">  make \</span><br><span class="line">  cmake \</span><br><span class="line">  autoconf \</span><br><span class="line">  automake \</span><br><span class="line">  libtool \</span><br><span class="line">  pkgconfig \</span><br><span class="line">  openssl-devel</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>编译 RedisTimeSeries</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p /usr/local/soft/modules/</span><br><span class="line"><span class="built_in">cd</span> /usr/local/soft/modules</span><br><span class="line"><span class="comment"># clone 代码，这里 --recursive 是为了拉取子模块</span></span><br><span class="line">git <span class="built_in">clone</span> --recursive https://github.com/RedisTimeSeries/RedisTimeSeries.git</span><br><span class="line"><span class="built_in">cd</span> RedisTimeSeries</span><br><span class="line"><span class="comment"># 推荐切换到稳定的release版本</span></span><br><span class="line">git checkout v1.12.9</span><br><span class="line"><span class="comment"># 更新子模块，非必须，如果上面 clone 时没有加上 --recursive ，这个步骤就不能省略</span></span><br><span class="line">git submodule update --init --recursive</span><br><span class="line"></span><br><span class="line"><span class="comment"># 检查并安装需要的依赖</span></span><br><span class="line">./sbin/setup</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line"><span class="comment"># readies version: af92230</span></span><br><span class="line">dnf install -q -y ca-certificates</span><br><span class="line">dnf install -q -y wget unzip</span><br><span class="line">dnf install -q -y git jq</span><br><span class="line">/usr/local/soft/modules/RedisTimeSeries/deps/readies/bin/enable-utf8</span><br><span class="line">dnf install -q -y autoconf libtool m4 automake</span><br><span class="line">dnf install -q -y openssl</span><br><span class="line">dnf install -q -y <span class="built_in">which</span></span><br><span class="line">/usr/local/soft/modules/RedisTimeSeries/deps/readies/bin/getepel</span><br><span class="line">dnf install -q -y openssl-devel</span><br><span class="line">/usr/local/soft/modules/RedisTimeSeries/deps/readies/bin/getgcc --modern</span><br><span class="line">dnf install -q -y valgrind</span><br><span class="line">/usr/bin/python3 /usr/local/soft/modules/RedisTimeSeries/deps/readies/bin/getcmake --usr</span><br><span class="line">dnf install -q -y lcov</span><br><span class="line">/usr/local/soft/modules/RedisTimeSeries/deps/readies/bin/getaws</span><br><span class="line">/usr/bin/python3 /usr/local/soft/modules/RedisTimeSeries/deps/readies/bin/getrmpytools --reinstall --modern</span><br><span class="line">/usr/bin/python3 -m pip install --disable-pip-version-check --user  -r /usr/local/soft/modules/RedisTimeSeries/tests/flow/requirements.txt</span><br><span class="line">NO_PY2=1 /usr/local/soft/modules/RedisTimeSeries/deps/readies/bin/getpudb</span><br><span class="line">amazon_linux_2023</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译 RedisTimeSeries</span></span><br><span class="line">make</span><br><span class="line"><span class="comment"># 编译过程未报错说明编译成功，编译后的文件位于 `bin/linux-x64-release/redistimeseries.so`</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><div class="tips"><p><em><strong><code>./sbin/setup</code> 报错</strong></em></p><ul class="lvl-1"><li class="lvl-2">本人使用的是 Amazon Linux 2023(内核 6.1)，即 <code>EL9</code>，类似于CentOS 9，第一次运行会报错，大致报错信息如下：</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">./sbin/setup</span><br><span class="line"><span class="comment">## 错误信息</span></span><br><span class="line">……</span><br><span class="line">Status code: 403 <span class="keyword">for</span> https://dyn.su/el9/base/x86_64/raven-release.el9.noarch.rpm (IP: 172.67.157.246)</span><br><span class="line"></span><br><span class="line">In /usr/local/soft/modules/RedisTimeSeries/deps/readies/bin/getepel:</span><br><span class="line">346      <span class="comment"># xinstall --allowerasing https://dl.fedoraproject.org/pub/epel/epel-release-latest-$&#123;EPEL&#125;.noarch.rpm</span></span><br><span class="line">347      <span class="keyword">fi</span></span><br><span class="line">348</span><br><span class="line">349  &gt;&gt;&gt; install_raven</span><br><span class="line">350      install_remi</span><br><span class="line">351      <span class="comment"># install_centos_stream_repos</span></span><br><span class="line">352</span><br><span class="line"></span><br><span class="line"><span class="built_in">command</span> failed: /usr/local/soft/modules/RedisTimeSeries/deps/readies/bin/getepel</span><br><span class="line"></span><br><span class="line">In /usr/local/soft/modules/RedisTimeSeries/sbin/setup:</span><br><span class="line">18       python3 -m pip list</span><br><span class="line">19       <span class="keyword">fi</span></span><br><span class="line">20</span><br><span class="line">21   &gt;&gt;&gt; <span class="variable">$ROOT</span>/sbin/system-setup.py <span class="variable">$SETUP_ARGS</span></span><br><span class="line">22       <span class="keyword">if</span> [[ <span class="variable">$VERBOSE</span> == 1 ]]; <span class="keyword">then</span></span><br><span class="line">23       python3 -m pip list</span><br><span class="line">24       <span class="keyword">fi</span></span><br></pre></td></tr></table></figure><ul class="lvl-1"><li class="lvl-2">错误分析与解决方法：<ul class="lvl-3"><li class="lvl-6">这个错误与安装 RedisBloom 时一样，禁用掉 <code>install_raven</code> 即可，具体参见 <a href="/2025/12/21/redis7-module-RedisBloom/" title="Redis 扩展模块 -- RedisBloom 的安装方法">Redis 扩展模块 -- RedisBloom 的安装方法</a> 进行修改。</li></ul></li></ul></div><h2 id="Redis-启用模块">Redis 启用模块</h2><ul class="lvl-0"><li class="lvl-2"><p>将生成的 <code>redistimeseries.so</code> 拷贝到 redis 的 modules 目录下（非必须），目录不存在则创建</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 注意 .so 文件需要包含可执行权限</span></span><br><span class="line"><span class="built_in">cp</span> bin/linux-x64-release/redistimeseries.so /usr/local/soft/redis-7.4.7/modules/redistimeseries.so</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>本文采用 <code>loadmodule</code> 加载模块</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 将 redistimeseries.so 添加到 redis.conf 中，需要重启 redis</span></span><br><span class="line">loadmodule /usr/local/soft/redis-7.4.7/modules/redistimeseries.so</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动redis</span></span><br><span class="line">redis-server redis.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># 登录测试</span></span><br><span class="line">redis-cli --user admin --pass 123456</span><br><span class="line"><span class="comment"># 查看模块</span></span><br><span class="line">127.0.0.1:6379&gt; MODULE LIST</span><br><span class="line"><span class="comment"># 输出</span></span><br><span class="line">1) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;timeseries&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 11209</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/local/soft/redis-7.4.7/modules/redistimeseries.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">2) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;ReJSON&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 20816</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/local/soft/redis-7.4.7/modules/rejson.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">3) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;bf&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 20817</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/local/soft/redis-7.4.7/modules/redisbloom.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">4) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;search&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 21025</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/local/soft/redis-7.4.7/modules/redisearch.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/07/redis7-module-RedisTimeSeries/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/07/redis7-module-RedisTimeSeries/"/>
    <published>2026-01-07T13:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 Redis 扩展模块 – RedisTimeSeries 的安装方法</li>
<li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li>
<li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li>
</ul>]]>
    </summary>
    <title>Redis 扩展模块 -- RedisTimeSeries 的安装方法</title>
    <updated>2026-01-07T06:46:17.065Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 Redis 扩展模块 – RediSearch 中 AutoSuggest 数据类型</li><li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li><li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li><li class="lvl-2">RediSearch 的安装参见 <a href="/2025/12/26/redis7-module-RediSearch/" title="Redis 扩展模块 -- RediSearch 的安装方法">Redis 扩展模块 -- RediSearch 的安装方法</a></li><li class="lvl-2">示例代码：<a href="https://github.com/hanqunfeng/springbootchapter/tree/master/springboot3-demo/redis-demo/redisson-demo">GitHub</a></li></ul><span id="more"></span><h2 id="AutoSuggest-搜索建议（自动补全）">AutoSuggest 搜索建议（自动补全）</h2><ul class="lvl-0"><li class="lvl-2"><p>Suggest 本质解决的问题：“当用户只输入部分前缀时，如何高性能地给出候选词，而不是全文搜索结果。”</p></li><li class="lvl-2"><p>Suggest 能解决什么业务问题?</p></li></ul><table><thead><tr><th>业务痛点</th><th>传统方案问题</th><th>Suggest 的价值</th></tr></thead><tbody><tr><td>搜索框自动补全</td><td>LIKE / 模糊查询性能差</td><td>O(logN) 级前缀匹配</td></tr><tr><td>热门词推荐</td><td>需要额外统计系统</td><td>内置 score 排序</td></tr><tr><td>拼写不准确</td><td>普通前缀无法命中</td><td>FUZZY 模糊匹配</td></tr><tr><td>联想提示</td><td>搜索索引过重</td><td>Suggest 独立结构，轻量</td></tr><tr><td>高并发提示</td><td>数据库压力大</td><td>Redis 内存级吞吐</td></tr><tr><td>实时更新</td><td>离线词库复杂</td><td>动态增删</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>Suggest 命令与使用场景对照表</p></li></ul><table><thead><tr><th>命令</th><th>作用</th><th>典型使用场景</th><th>是否高频</th></tr></thead><tbody><tr><td><strong>FT.SUGADD</strong></td><td>添加 / 更新补全词</td><td>构建词库、动态热词更新</td><td>⭐⭐⭐⭐</td></tr><tr><td><strong>FT.SUGGET</strong></td><td>查询补全建议</td><td>用户输入联想提示</td><td>⭐⭐⭐⭐⭐</td></tr><tr><td><strong>FT.SUGDEL</strong></td><td>删除补全词</td><td>词下架、清理脏数据</td><td>⭐⭐</td></tr><tr><td><strong>FT.SUGLEN</strong></td><td>统计补全词数量</td><td>监控、容量评估</td><td>⭐</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>场景 → 命令映射总结表（推荐收藏）</p></li></ul><table><thead><tr><th>业务目标</th><th>推荐命令组合</th><th>说明</th></tr></thead><tbody><tr><td>自动补全</td><td>SUGADD + SUGGET</td><td>基础能力</td></tr><tr><td>热词排行</td><td>SUGADD(INCR) + SUGGET(WITHSCORES)</td><td>权重驱动</td></tr><tr><td>拼写纠错</td><td>SUGGET(FUZZY)</td><td>容错</td></tr><tr><td>分类推荐</td><td>SUGADD(PAYLOAD) + SUGGET(WITHPAYLOADS)</td><td>携带业务信息</td></tr><tr><td>词库治理</td><td>SUGDEL + SUGLEN</td><td>运维</td></tr><tr><td>容量监控</td><td>SUGLEN</td><td>规模评估</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>👉 原则：输入框提示用 Suggest，搜索结果用 FT.SEARCH。</p></li></ul><h3 id="1️⃣-FT-SUGADD-——-添加-更新补全词">1️⃣ FT.SUGADD —— 添加 / 更新补全词</h3><ul class="lvl-0"><li class="lvl-2"><p>向补全词库中添加一个候选词</p></li><li class="lvl-2"><p>可设置 权重、payload</p></li><li class="lvl-2"><p>可用于 热词排序</p></li><li class="lvl-2"><p>语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">FT.SUGADD key string score</span><br><span class="line">  [INCR]</span><br><span class="line">  [PAYLOAD payload]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>参数说明</p></li></ul><table><thead><tr><th>参数</th><th>含义</th></tr></thead><tbody><tr><td><code>key</code></td><td>Suggestion 词库 Key，索引名称</td></tr><tr><td><code>string</code></td><td>补全文本</td></tr><tr><td><code>score</code></td><td>权重（越大越靠前）</td></tr><tr><td><code>INCR</code></td><td>递增，累加权重</td></tr><tr><td><code>PAYLOAD</code></td><td>附加元数据</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">FT.SUGADD sug:search <span class="string">&quot;iphone&quot;</span> 100</span><br><span class="line">FT.SUGADD sug:search <span class="string">&quot;iphone 15&quot;</span> 200</span><br><span class="line">FT.SUGADD sug:search <span class="string">&quot;ipad&quot;</span> 50 PAYLOAD <span class="string">&quot;category=tablet&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看类型</span></span><br><span class="line">127.0.0.1:6379&gt; <span class="built_in">type</span> sug:search</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">trietype0</span><br></pre></td></tr></table></figure><h3 id="2️⃣-FT-SUGGET-——-获取补全建议">2️⃣ FT.SUGGET —— 获取补全建议</h3><ul class="lvl-0"><li class="lvl-2"><p>根据 前缀 返回最相关的候选词</p></li><li class="lvl-2"><p>支持模糊匹配</p></li><li class="lvl-2"><p>可返回 payload / score</p></li><li class="lvl-2"><p>可用于 搜索框输入联想、拼写纠错</p></li><li class="lvl-2"><p>语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">FT.SUGGET key prefix</span><br><span class="line">  [FUZZY]</span><br><span class="line">  [WITHSCORES]</span><br><span class="line">  [WITHPAYLOADS]</span><br><span class="line">  [MAX num]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>参数说明</p></li></ul><table><thead><tr><th>参数</th><th>含义</th></tr></thead><tbody><tr><td><code>prefix</code></td><td>用户输入前缀</td></tr><tr><td><code>FUZZY</code></td><td>模糊匹配（允许拼写错误）</td></tr><tr><td><code>WITHSCORES</code></td><td>返回权重</td></tr><tr><td><code>WITHPAYLOADS</code></td><td>返回 payload</td></tr><tr><td><code>MAX</code></td><td>最大返回数量</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">FT.SUGGET sug:search <span class="string">&quot;ip&quot;</span> MAX 5</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) <span class="string">&quot;iphone 15&quot;</span></span><br><span class="line">2) <span class="string">&quot;iphone&quot;</span></span><br><span class="line">3) <span class="string">&quot;ipad&quot;</span></span><br><span class="line"></span><br><span class="line">FT.SUGGET sug:search <span class="string">&quot;iphne&quot;</span> FUZZY</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) <span class="string">&quot;iphone 15&quot;</span></span><br><span class="line">2) <span class="string">&quot;iphone&quot;</span></span><br><span class="line"></span><br><span class="line">FT.SUGGET sug:search <span class="string">&quot;ip&quot;</span> WITHSCORES WITHPAYLOADS</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) <span class="string">&quot;iphone 15&quot;</span></span><br><span class="line">2) <span class="string">&quot;70.71067810058594&quot;</span></span><br><span class="line">3) (nil)</span><br><span class="line">4) <span class="string">&quot;iphone&quot;</span></span><br><span class="line">5) <span class="string">&quot;44.72135925292969&quot;</span></span><br><span class="line">6) (nil)</span><br><span class="line">7) <span class="string">&quot;ipad&quot;</span></span><br><span class="line">8) <span class="string">&quot;28.86751365661621&quot;</span></span><br><span class="line">9) <span class="string">&quot;category=tablet&quot;</span></span><br></pre></td></tr></table></figure><h3 id="3️⃣-FT-SUGDEL-——-删除补全词">3️⃣ FT.SUGDEL —— 删除补全词</h3><ul class="lvl-0"><li class="lvl-2"><p>从补全词库中移除指定词条</p></li><li class="lvl-2"><p>可用于 商品下架、敏感词移除、过期关键词清理</p></li><li class="lvl-2"><p>语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FT.SUGDEL key string</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FT.SUGDEL sug:search <span class="string">&quot;iphone&quot;</span></span><br></pre></td></tr></table></figure><h3 id="4️⃣-FT-SUGLEN-——-查看词库规模">4️⃣ FT.SUGLEN —— 查看词库规模</h3><ul class="lvl-0"><li class="lvl-2"><p>获取当前词库的词条数量</p></li><li class="lvl-2"><p>语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FT.SUGLEN key</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">FT.SUGLEN sug:search</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">(<span class="built_in">integer</span>) 3</span><br></pre></td></tr></table></figure><h2 id="示例代码">示例代码</h2><ul class="lvl-0"><li class="lvl-2"><p>SpringBoot 的 RedisTemplate 中没有提供对<code>AutoSuggest</code>的封装，需要自己封装，我这里封装了一个简易的<code>RedisSuggestTool</code></p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.demo.redissug;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.core.StringRedisTemplate;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.core.script.DefaultRedisScript;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.Arrays;</span><br><span class="line"><span class="keyword">import</span> java.util.Collections;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 补全建议工具类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisSuggestTool</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> StringRedisTemplate stringRedisTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> &lt;T&gt; T <span class="title function_">executeLua</span><span class="params">(String script, List&lt;String&gt; keys, Object... args)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (T) stringRedisTemplate.execute(</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">DefaultRedisScript</span>&lt;&gt;(script, Object.class),</span><br><span class="line">                keys,</span><br><span class="line">                Arrays.stream(args)</span><br><span class="line">                        .map(String::valueOf)</span><br><span class="line">                        .toArray(String[]::<span class="keyword">new</span>)</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 添加 / 更新补全词</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;</span></span><br><span class="line"><span class="comment">     * FT.SUGADD key string score</span></span><br><span class="line"><span class="comment">     * [INCR]</span></span><br><span class="line"><span class="comment">     * [PAYLOAD payload]</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key     索引名称</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value   补全文本</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> score   分数</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> incr    是否递增，默认为false</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> payload 补全词的附加信息，默认为空</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> Long <span class="title function_">sugAdd</span><span class="params">(String key,</span></span><br><span class="line"><span class="params">                       String value,</span></span><br><span class="line"><span class="params">                       <span class="type">double</span> score,</span></span><br><span class="line"><span class="params">                       <span class="type">boolean</span> incr,</span></span><br><span class="line"><span class="params">                       String payload)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">lua</span> <span class="operator">=</span> <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">                local args = &#123;&#x27;FT.SUGADD&#x27;, KEYS[1], ARGV[1], ARGV[2]&#125;</span></span><br><span class="line"><span class="string">                if ARGV[3] == &#x27;1&#x27; then table.insert(args, &#x27;INCR&#x27;) end</span></span><br><span class="line"><span class="string">                if ARGV[4] ~= &#x27;&#x27; then table.insert(args, &#x27;PAYLOAD&#x27;); table.insert(args, ARGV[4]) end</span></span><br><span class="line"><span class="string">                return redis.call(unpack(args))</span></span><br><span class="line"><span class="string">                &quot;&quot;&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> executeLua(</span><br><span class="line">                lua,</span><br><span class="line">                Collections.singletonList(key),</span><br><span class="line">                value,</span><br><span class="line">                score,</span><br><span class="line">                incr ? <span class="string">&quot;1&quot;</span> : <span class="string">&quot;0&quot;</span>,</span><br><span class="line">                payload == <span class="literal">null</span> ? <span class="string">&quot;&quot;</span> : payload</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Long <span class="title function_">sugAdd</span><span class="params">(String key,</span></span><br><span class="line"><span class="params">                       String value,</span></span><br><span class="line"><span class="params">                       <span class="type">double</span> score)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sugAdd(key, value, score, <span class="literal">false</span>, <span class="string">&quot;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查询补全建议</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;</span></span><br><span class="line"><span class="comment">     * FT.SUGGET key prefix</span></span><br><span class="line"><span class="comment">     * [FUZZY]</span></span><br><span class="line"><span class="comment">     * [WITHSCORES]</span></span><br><span class="line"><span class="comment">     * [WITHPAYLOADS]</span></span><br><span class="line"><span class="comment">     * [MAX num]</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key          索引名称</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> prefix       前缀</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> fuzzy        是否模糊匹配，默认为false</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> withScores   是否返回分数，默认为false</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> withPayloads 是否返回附加信息，默认为false</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> max           最大返回数量，默认为5</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;Suggestion&gt; <span class="title function_">sugGet</span><span class="params">(String key,</span></span><br><span class="line"><span class="params">                                   String prefix,</span></span><br><span class="line"><span class="params">                                   <span class="type">boolean</span> fuzzy,</span></span><br><span class="line"><span class="params">                                   <span class="type">boolean</span> withScores,</span></span><br><span class="line"><span class="params">                                   <span class="type">boolean</span> withPayloads,</span></span><br><span class="line"><span class="params">                                   <span class="type">int</span> max)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">lua</span> <span class="operator">=</span> <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">                local args = &#123;&#x27;FT.SUGGET&#x27;, KEYS[1], ARGV[1]&#125;</span></span><br><span class="line"><span class="string">                if ARGV[2] == &#x27;1&#x27; then table.insert(args, &#x27;FUZZY&#x27;) end</span></span><br><span class="line"><span class="string">                if ARGV[3] == &#x27;1&#x27; then table.insert(args, &#x27;WITHSCORES&#x27;) end</span></span><br><span class="line"><span class="string">                if ARGV[4] == &#x27;1&#x27; then table.insert(args, &#x27;WITHPAYLOADS&#x27;) end</span></span><br><span class="line"><span class="string">                if tonumber(ARGV[5]) &gt; 0 then table.insert(args, &#x27;MAX&#x27;); table.insert(args, ARGV[5]) end</span></span><br><span class="line"><span class="string">                return redis.call(unpack(args))</span></span><br><span class="line"><span class="string">                &quot;&quot;&quot;</span>;</span><br><span class="line"></span><br><span class="line">        List&lt;Object&gt; raw = executeLua(</span><br><span class="line">                lua,</span><br><span class="line">                Collections.singletonList(key),</span><br><span class="line">                prefix,</span><br><span class="line">                fuzzy ? <span class="string">&quot;1&quot;</span> : <span class="string">&quot;0&quot;</span>,</span><br><span class="line">                withScores ? <span class="string">&quot;1&quot;</span> : <span class="string">&quot;0&quot;</span>,</span><br><span class="line">                withPayloads ? <span class="string">&quot;1&quot;</span> : <span class="string">&quot;0&quot;</span>,</span><br><span class="line">                max</span><br><span class="line">        );</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> parseSuggestionResult(raw, withScores, withPayloads);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 结果解析器</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;Suggestion&gt; <span class="title function_">parseSuggestionResult</span><span class="params">(List&lt;Object&gt; raw,</span></span><br><span class="line"><span class="params">                                                   <span class="type">boolean</span> withScores,</span></span><br><span class="line"><span class="params">                                                   <span class="type">boolean</span> withPayloads)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (raw == <span class="literal">null</span> || raw.isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> Collections.emptyList();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        List&lt;Suggestion&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="type">int</span> <span class="variable">step</span> <span class="operator">=</span> <span class="number">1</span></span><br><span class="line">                + (withScores ? <span class="number">1</span> : <span class="number">0</span>)</span><br><span class="line">                + (withPayloads ? <span class="number">1</span> : <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; raw.size(); i += step) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">idx</span> <span class="operator">=</span> i;</span><br><span class="line"></span><br><span class="line">            <span class="type">String</span> <span class="variable">value</span> <span class="operator">=</span> String.valueOf(raw.get(idx++));</span><br><span class="line">            <span class="type">Double</span> <span class="variable">score</span> <span class="operator">=</span> withScores ? Double.valueOf(String.valueOf(raw.get(idx++))) : <span class="literal">null</span>;</span><br><span class="line">            <span class="type">String</span> <span class="variable">payload</span> <span class="operator">=</span> withPayloads ? String.valueOf(raw.get(idx++)) : <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">            list.add(<span class="keyword">new</span> <span class="title class_">Suggestion</span>(value, score, payload));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> list;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除补全词</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;</span></span><br><span class="line"><span class="comment">     * FT.SUGDEL key string</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   索引名称</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 补全词</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> Boolean <span class="title function_">sugDel</span><span class="params">(String key, String value)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">lua</span> <span class="operator">=</span> <span class="string">&quot;return redis.call(&#x27;FT.SUGDEL&#x27;, KEYS[1], ARGV[1])&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> executeLua(</span><br><span class="line">                lua,</span><br><span class="line">                Collections.singletonList(key),</span><br><span class="line">                value</span><br><span class="line">        );</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> Long.valueOf(<span class="number">1</span>).equals(result);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取补全词数量</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;</span></span><br><span class="line"><span class="comment">     * FT.SUGLEN key</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 索引名称</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> Long <span class="title function_">sugLen</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">lua</span> <span class="operator">=</span> <span class="string">&quot;return redis.call(&#x27;FT.SUGLEN&#x27;, KEYS[1])&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> executeLua(</span><br><span class="line">                lua,</span><br><span class="line">                Collections.singletonList(key)</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Suggestion</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String value;</span><br><span class="line">    <span class="keyword">private</span> Double score;</span><br><span class="line">    <span class="keyword">private</span> String payload;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/06/redis7-datatype-17-AutoSuggest/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/06/redis7-datatype-17-AutoSuggest/"/>
    <published>2026-01-06T13:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 Redis 扩展模块 – RediSearch 中 AutoSuggest 数据类型</li>
<li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li>
<li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li>
<li class="lvl-2">RediSearch 的安装参见 <a href="/2025/12/26/redis7-module-RediSearch/" title="Redis 扩展模块 -- RediSearch 的安装方法">Redis 扩展模块 -- RediSearch 的安装方法</a></li>
<li class="lvl-2">示例代码：<a href="https://github.com/hanqunfeng/springbootchapter/tree/master/springboot3-demo/redis-demo/redisson-demo">GitHub</a></li>
</ul>]]>
    </summary>
    <title>Redis 命令及数据类型 -- AutoSuggest</title>
    <updated>2026-01-07T07:42:03.485Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 Redis 扩展模块 – RediSearch 的使用方法</li><li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li><li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li><li class="lvl-2">RediSearch 的安装参见 <a href="/2025/12/26/redis7-module-RediSearch/" title="Redis 扩展模块 -- RediSearch 的安装方法">Redis 扩展模块 -- RediSearch 的安装方法</a></li><li class="lvl-2">示例代码：<a href="https://github.com/hanqunfeng/springbootchapter/tree/master/springboot3-demo/redis-demo/redisson-demo">GitHub</a></li></ul><span id="more"></span><h2 id="RediSearch-命令">RediSearch 命令</h2><ul class="lvl-0"><li class="lvl-2"><p>为了展示命令的使用方法，这里以JSON文档进行索引，初始化数据如下：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">JSON.SET user:10001 $ <span class="string">&#x27;&#123;&quot;name&quot;:&quot;Alice Bob&quot;,&quot;age&quot;:28,&quot;vip&quot;:&quot;yes&quot;,&quot;orders&quot;:[&#123;&quot;amount&quot;:199.99,&quot;status&quot;:&quot;PAID&quot;&#125;,&#123;&quot;amount&quot;:59.9,&quot;status&quot;:&quot;CREATED&quot;&#125;],&quot;comment&quot;: &quot;I have a phone&quot;,&quot;items&quot;:[&quot;SpringCloud技术指南&quot;,&quot;Shell脚本基础&quot;]&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line">JSON.SET user:10002 $ <span class="string">&#x27;&#123;&quot;name&quot;:&quot;Bob Frank&quot;,&quot;age&quot;:35,&quot;vip&quot;:&quot;no&quot;,&quot;orders&quot;:[&#123;&quot;amount&quot;:899.0,&quot;status&quot;:&quot;PAID&quot;&#125;],&quot;comment&quot;: &quot;I have a iphone&quot;,&quot;items&quot;:[&quot;Linux必知必会&quot;,&quot;Java多线程详解&quot;]&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line">JSON.SET user:10003 $ <span class="string">&#x27;&#123;&quot;name&quot;:&quot;Carol&quot;,&quot;age&quot;:22,&quot;vip&quot;:&quot;no&quot;,&quot;orders&quot;:[&#123;&quot;amount&quot;:19.9,&quot;status&quot;:&quot;CANCELLED&quot;&#125;],&quot;comment&quot;: &quot;I have a mobile&quot;,&quot;items&quot;:[&quot;MySQL从删库到跑路&quot;,&quot;Oracle开发实践&quot;]&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line">JSON.SET user:10004 $ <span class="string">&#x27;&#123;&quot;name&quot;:&quot;David Bob&quot;,&quot;age&quot;:41,&quot;vip&quot;:&quot;yes&quot;,&quot;orders&quot;:[&#123;&quot;amount&quot;:1200,&quot;status&quot;:&quot;PAID&quot;&#125;,&#123;&quot;amount&quot;:300,&quot;status&quot;:&quot;PAID&quot;&#125;],&quot;comment&quot;: &quot;I have a car&quot;,&quot;items&quot;:[&quot;Spring技术指南&quot;,&quot;RocketMQ由浅入深&quot;]&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line">JSON.SET user:10005 $ <span class="string">&#x27;&#123;&quot;name&quot;:&quot;Eve&quot;,&quot;age&quot;:30,&quot;vip&quot;:&quot;no&quot;,&quot;orders&quot;:[&#123;&quot;amount&quot;:88.8,&quot;status&quot;:&quot;CREATED&quot;&#125;],&quot;comment&quot;: &quot;I have a pencil&quot;,&quot;items&quot;:[&quot;Kafka从零开始&quot;,&quot;Java由浅入深&quot;]&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line">JSON.SET user:10006 $ <span class="string">&#x27;&#123;&quot;name&quot;:&quot;Frank&quot;,&quot;age&quot;:27,&quot;vip&quot;:&quot;no&quot;,&quot;orders&quot;:[&#123;&quot;amount&quot;:499,&quot;status&quot;:&quot;PAID&quot;&#125;,&#123;&quot;amount&quot;:129,&quot;status&quot;:&quot;CREATED&quot;&#125;],&quot;comment&quot;: &quot;I have a phone&quot;,&quot;items&quot;:[&quot;Redis开发实战&quot;,&quot;MongoDB从入门到实战&quot;]&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line">JSON.SET user:10007 $ <span class="string">&#x27;&#123;&quot;name&quot;:&quot;Grace&quot;,&quot;age&quot;:33,&quot;vip&quot;:&quot;yes&quot;,&quot;orders&quot;:[&#123;&quot;amount&quot;:999.9,&quot;status&quot;:&quot;PAID&quot;&#125;],&quot;comment&quot;: &quot;I have a book&quot;,&quot;items&quot;:[&quot;Android开发手册&quot;,&quot;Gradle从零开始&quot;]&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line">JSON.SET user:10008 $ <span class="string">&#x27;&#123;&quot;name&quot;:&quot;Henry&quot;,&quot;age&quot;:45,&quot;vip&quot;:&quot;no&quot;,&quot;orders&quot;:[&#123;&quot;amount&quot;:59.9,&quot;status&quot;:&quot;CANCELLED&quot;&#125;],&quot;comment&quot;: &quot;I have a macbook&quot;,&quot;items&quot;:[&quot;SpringBoot技术指南&quot;,&quot;Maven由浅入深&quot;]&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line">JSON.SET user:10009 $ <span class="string">&#x27;&#123;&quot;name&quot;:&quot;Ivy&quot;,&quot;age&quot;:26,&quot;vip&quot;:&quot;yes&quot;,&quot;orders&quot;:[&#123;&quot;amount&quot;:299,&quot;status&quot;:&quot;PAID&quot;&#125;,&#123;&quot;amount&quot;:199,&quot;status&quot;:&quot;PAID&quot;&#125;],&quot;comment&quot;: &quot;I have a watch&quot;,&quot;items&quot;:[&quot;Spring融会贵通&quot;,&quot;Java技术开发指南&quot;]&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line">JSON.SET user:10010 $ <span class="string">&#x27;&#123;&quot;name&quot;:&quot;Jack&quot;,&quot;age&quot;:38,&quot;vip&quot;:&quot;no&quot;,&quot;orders&quot;:[&#123;&quot;amount&quot;:150,&quot;status&quot;:&quot;CREATED&quot;&#125;],&quot;comment&quot;: &quot;I have a apple&quot;,&quot;items&quot;:[&quot;Spring技术指南&quot;,&quot;Java由浅入深&quot;]&#125;&#x27;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>SpringBoot暂时没有支持 RediSearch ，你可以编写Lua脚本来实现相应的功能，另外 <a href="https://redisson.pro/docs/data-and-services/services/#redisearch-service">Redisson</a>已经提供了对 RediSearch 的支持，下面结合命令给出代码示例。</p></li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 引入 Redisson ，这里要注意，现在最新版是 4.0.0，需要 springboot 4.x --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.redisson<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>redisson-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.52.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>通用代码</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Autowired</span></span><br><span class="line"><span class="keyword">private</span> RedissonClient redissonClient;</span><br><span class="line"></span><br><span class="line"><span class="type">RSearch</span> <span class="variable">rSearch</span> <span class="operator">=</span> redissonClient.getSearch(StringCodec.INSTANCE);</span><br></pre></td></tr></table></figure><h3 id="一、索引生命周期管理类">一、索引生命周期管理类</h3><table><thead><tr><th>命令</th><th>作用</th><th>核心参数</th><th>示例</th></tr></thead><tbody><tr><td><code>FT.CREATE</code></td><td>创建索引</td><td>索引名、ON、PREFIX、SCHEMA</td><td>见下</td></tr><tr><td><code>FT.ALTER</code></td><td>给已有索引新增字段</td><td>索引名、SCHEMA ADD</td><td>见下</td></tr><tr><td><code>FT.DROPINDEX</code></td><td>删除索引</td><td>索引名、DD</td><td>见下</td></tr><tr><td><code>FT.INFO</code></td><td>查看索引信息</td><td>索引名</td><td>见下</td></tr><tr><td><code>FT._LIST</code></td><td>列出所有索引</td><td>无</td><td>见下</td></tr></tbody></table><h4 id="1️⃣-FT-CREATE">1️⃣ FT.CREATE</h4><ul class="lvl-0"><li class="lvl-2"><p>FT.CREATE 基本语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">FT.CREATE index_name</span><br><span class="line">[ON HASH | JSON]</span><br><span class="line">[PREFIX count prefix ...]</span><br><span class="line">[FILTER filter]</span><br><span class="line">[LANGUAGE default_lang]</span><br><span class="line">[LANGUAGE_FIELD lang_attribute]</span><br><span class="line">[SCORE default_score]</span><br><span class="line">[SCORE_FIELD score_attribute]</span><br><span class="line">[PAYLOAD_FIELD payload_attribute]</span><br><span class="line">[MAXTEXTFIELDS]</span><br><span class="line">[TEMPORARY seconds]</span><br><span class="line">[NOOFFSETS] [NOHL] [NOFIELDS] [NOFREQS]</span><br><span class="line">[STOPWORDS count stopword ...]</span><br><span class="line">[SKIPINITIALSCAN]</span><br><span class="line">SCHEMA</span><br><span class="line">  field_name [AS <span class="built_in">alias</span>] TEXT | TAG | NUMERIC | GEO | VECTOR</span><br><span class="line">  [SORTABLE [UNF]]</span><br><span class="line">  [NOINDEX]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>FT.CREATE 参数说明表</p></li></ul><table><thead><tr><th>参数</th><th>作用说明</th></tr></thead><tbody><tr><td><code>index_name</code></td><td>索引名称</td></tr><tr><td><code>ON HASH | JSON</code></td><td>指定索引数据来源类型，默认 <code>HASH</code></td></tr><tr><td><code>PREFIX count prefix...</code></td><td>指定索引 Key 前缀</td></tr><tr><td><code>FILTER filter</code></td><td>对索引数据设置过滤表达式</td></tr><tr><td><code>LANGUAGE</code></td><td>指定默认分词语言（默认 english，中文是 chinese）</td></tr><tr><td><code>LANGUAGE_FIELD</code></td><td>指定文档中的语言字段</td></tr><tr><td><code>SCORE</code></td><td>设置文档默认评分</td></tr><tr><td><code>SCORE_FIELD</code></td><td>从字段中读取评分</td></tr><tr><td><code>PAYLOAD_FIELD</code></td><td>指定存储的二进制负载字段</td></tr><tr><td><code>MAXTEXTFIELDS</code></td><td>允许更多 TEXT 字段（消耗更多内存）</td></tr><tr><td><code>TEMPORARY seconds</code></td><td>创建临时索引，超时后自动删除</td></tr><tr><td><code>NOOFFSETS</code></td><td>不存储文本偏移量（节省内存）</td></tr><tr><td><code>NOHL</code></td><td>禁用高亮</td></tr><tr><td><code>NOFIELDS</code></td><td>不保存字段内容</td></tr><tr><td><code>NOFREQS</code></td><td>不保存词频信息</td></tr><tr><td><code>STOPWORDS count ...</code></td><td>指定停用词</td></tr><tr><td><code>SKIPINITIALSCAN</code></td><td>创建索引时不扫描已有数据</td></tr><tr><td><code>SCHEMA</code></td><td>索引字段定义起始</td></tr><tr><td><code>AS alias</code></td><td>字段别名</td></tr><tr><td><code>TEXT</code></td><td>全文索引字段</td></tr><tr><td><code>TAG</code></td><td>精确匹配字段</td></tr><tr><td><code>NUMERIC</code></td><td>数值字段</td></tr><tr><td><code>GEO</code></td><td>地理位置字段</td></tr><tr><td><code>VECTOR</code></td><td>向量字段</td></tr><tr><td><code>SORTABLE</code></td><td>允许排序</td></tr><tr><td><code>UNF</code></td><td>不规范化排序</td></tr><tr><td><code>NOINDEX</code></td><td>字段不参与索引</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>FT.CREATE 的核心语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">FT.CREATE &lt;index&gt;</span><br><span class="line">[ON HASH | JSON]</span><br><span class="line">[PREFIX count prefix ...]</span><br><span class="line">[LANGUAGE default_lang]</span><br><span class="line">SCHEMA</span><br><span class="line">  field_name [AS <span class="built_in">alias</span>] TEXT | TAG | NUMERIC | GEO | VECTOR</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>创建索引示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">FT.CREATE idx:user</span><br><span class="line">  ON JSON</span><br><span class="line">  PREFIX 1 user:</span><br><span class="line">  LANGUAGE chinese</span><br><span class="line">  SCHEMA</span><br><span class="line">    $.name AS name TEXT</span><br><span class="line">    $.age AS age NUMERIC SORTABLE</span><br><span class="line">    $.orders[*].amount AS amount NUMERIC</span><br><span class="line">    $.orders[*].status AS status TAG</span><br><span class="line">    $.comment AS comment TEXT</span><br><span class="line">    $.items[*] AS items TEXT</span><br><span class="line"></span><br><span class="line"><span class="comment"># 参数说明</span></span><br><span class="line">  <span class="comment"># idx:user：索引名</span></span><br><span class="line">  <span class="comment"># ON JSON：索引 JSON 文档，RediSearch 仅支持对 HASH 和 JSON 进行索引</span></span><br><span class="line">  <span class="comment"># PREFIX 1 user: ：索引指定 key 前缀，1 表示索引一个，user: 表示索引的 key 前缀，如果索引两个：PREFIX 2 user: order:</span></span><br><span class="line">  <span class="comment"># LANGUAGE chinese：指定默认分词语言，默认是英文 english，中文是 chinese，如果索引中有中文，则必须指定 LANGUAGE 为 chinese</span></span><br><span class="line">  <span class="comment"># SCHEMA：索引字段</span></span><br><span class="line">  <span class="comment"># $.name AS name TEXT：索引字段 $.name(注意：JSON类型以 $. 开头，Hash类型就不需要了)，字段别名为 name，字段类型为 TEXT</span></span><br><span class="line">  <span class="comment"># AS：字段别名（查询时使用）</span></span><br><span class="line">  <span class="comment"># TEXT / NUMERIC / TAG：字段类型，这几个是最常用的</span></span><br><span class="line">  <span class="comment"># SORTABLE：NUMERIC、TAG、TEXT 或 GEO 类型的字段可以带有一个可选的 SORTABLE 参数，表示为字段建立专门的排序数据结构（类似列式存储），从而避免在查询阶段对大量结果进行临时排序。</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">rSearch.createIndex(</span><br><span class="line">    <span class="string">&quot;idx:user&quot;</span>,</span><br><span class="line">    IndexOptions.defaults()</span><br><span class="line">            .on(IndexType.JSON)</span><br><span class="line">            .prefix(List.of(<span class="string">&quot;user:&quot;</span>))</span><br><span class="line">            .language(<span class="string">&quot;chinese&quot;</span>),</span><br><span class="line">    FieldIndex.text(<span class="string">&quot;$.name&quot;</span>).as(<span class="string">&quot;name&quot;</span>),</span><br><span class="line">    FieldIndex.numeric(<span class="string">&quot;$.age&quot;</span>).as(<span class="string">&quot;age&quot;</span>),</span><br><span class="line">    FieldIndex.numeric(<span class="string">&quot;$.orders[*].amount&quot;</span>).as(<span class="string">&quot;amount&quot;</span>),</span><br><span class="line">    FieldIndex.tag(<span class="string">&quot;$.orders[*].status&quot;</span>).as(<span class="string">&quot;status&quot;</span>),</span><br><span class="line">    FieldIndex.text(<span class="string">&quot;$.comment&quot;</span>).as(<span class="string">&quot;comment&quot;</span>),</span><br><span class="line">    FieldIndex.text(<span class="string">&quot;$.items[*]&quot;</span>).as(<span class="string">&quot;items&quot;</span>)</span><br><span class="line">);</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>索引字段的类型</p></li></ul><table><thead><tr><th>字段类型</th><th>中文名称</th><th>功能说明</th><th>典型使用场景</th><th>备注 / 限制</th></tr></thead><tbody><tr><td><strong>TEXT</strong></td><td>全文文本字段</td><td>支持对字段值进行全文检索（分词、相关度计算、模糊匹配等）</td><td>文章内容、用户名、描述信息</td><td>支持权重（WEIGHT）、排序（SORTABLE）等选项</td></tr><tr><td><strong>TAG</strong></td><td>标签字段 / 精确匹配字段</td><td>支持精确匹配查询，适用于枚举值或离散分类</td><td>分类、状态、主键、类型字段</td><td>不分词；可自定义分隔符</td></tr><tr><td><strong>NUMERIC</strong></td><td>数值字段</td><td>支持数值范围查询</td><td>年龄、价格、分数、时间戳</td><td>支持区间查询（<code>[min max]</code>）</td></tr><tr><td><strong>GEO</strong></td><td>地理位置字段（点）</td><td>支持以“点”为中心的半径范围查询</td><td>门店位置、用户位置</td><td>值格式必须为 <code>&quot;经度,纬度&quot;</code></td></tr><tr><td><strong>VECTOR</strong></td><td>向量字段</td><td>支持向量相似度搜索（KNN 等）</td><td>语义搜索、推荐系统、Embedding 向量</td><td>需要 <strong>Query Dialect ≥ 2</strong>（RediSearch ≥ 2.4）</td></tr><tr><td><strong>GEOSHAPE</strong></td><td>地理形状字段（多边形）</td><td>支持多边形空间查询</td><td>行政区、商圈、地图区域</td><td>使用 WKT 格式；不支持 JSON 多值和 SORTABLE</td></tr></tbody></table><blockquote><p>GEOSHAPE 字段补充说明</p></blockquote><table><thead><tr><th>项目</th><th>说明</th></tr></thead><tbody><tr><td>数据格式</td><td>WKT（Well-Known Text）格式，如：<code>POLYGON((x1 y1, x2 y2, ...))</code></td></tr><tr><td>坐标系统</td><td><code>SPHERICAL</code>（球面坐标，经纬度）<br><code>FLAT</code>（平面坐标，笛卡尔 X/Y）</td></tr><tr><td>默认坐标系统</td><td><code>SPHERICAL</code></td></tr><tr><td>当前限制</td><td>❌ 不支持 JSON 多值<br>❌ 不支持 <code>SORTABLE</code> 选项</td></tr></tbody></table><blockquote><p>索引字段类型选型建议</p></blockquote><table><thead><tr><th>使用场景</th><th>推荐字段类型</th><th>说明</th></tr></thead><tbody><tr><td>全文检索</td><td><strong>TEXT</strong></td><td>支持分词、相关度计算、模糊匹配等全文搜索能力</td></tr><tr><td>精确过滤 / 分类</td><td><strong>TAG</strong></td><td>精确匹配，不分词，适合枚举值、状态、类型等字段</td></tr><tr><td>区间筛选 / 排序</td><td><strong>NUMERIC</strong></td><td>支持数值区间查询与排序，适合价格、时间、分数等</td></tr><tr><td>附近的人 / 门店</td><td><strong>GEO</strong></td><td>基于经纬度点进行半径范围查询</td></tr><tr><td>语义搜索 / 向量召回</td><td><strong>VECTOR</strong></td><td>基于向量相似度（KNN）的语义搜索，需要 Dialect ≥ 2</td></tr><tr><td>复杂地理区域判断</td><td><strong>GEOSHAPE</strong></td><td>支持多边形（Polygon）区域查询，适合行政区、商圈等</td></tr></tbody></table><blockquote><p>哪些字段适合使用 SORTABLE</p></blockquote><table><thead><tr><th>字段类型</th><th>是否适合 SORTABLE</th><th>原因</th></tr></thead><tbody><tr><td>NUMERIC</td><td>✅ 强烈推荐</td><td>数值排序最常见，如价格、时间、评分</td></tr><tr><td>TAG</td><td>⚠️ 视情况</td><td>一般用于过滤，排序意义不大</td></tr><tr><td>TEXT</td><td>⚠️ 谨慎</td><td>文本可能很大，内存开销高</td></tr><tr><td>GEO</td><td>⚠️ 特殊场景</td><td>通常按距离排序，更多依赖 <code>GEOFILTER</code></td></tr></tbody></table><div class="tips"><p><em><strong>小贴士：如何在redis终端输入多行的命令？</strong></em></p><ul class="lvl-1"><li class="lvl-2">先说结论：redis终端 不支持多行输入，但可以通过如下方式输入多行命令：</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">redis-cli &lt;&lt;<span class="string">EOF</span></span><br><span class="line"><span class="string">HSET user:1 \</span></span><br><span class="line"><span class="string">name Tom \</span></span><br><span class="line"><span class="string">age 18 \</span></span><br><span class="line"><span class="string">city Beijing</span></span><br><span class="line"><span class="string">EOF</span></span><br></pre></td></tr></table></figure><ul class="lvl-1"><li class="lvl-2">这种方法是利用了 Bash 的能力，即 <code>&lt;&lt;EOF</code> 后面的内容会作为标准输入，直到 <code>EOF</code> 为止，并通过 <code>\</code> 符换行。</li><li class="lvl-2">如果在VSCode中，可以选中要合并的多行内容，然后通过 <code>Ctrl + Shift + J</code> 快捷键将多行合并到一行。</li></ul></div><h4 id="2️⃣-FT-ALTER">2️⃣ FT.ALTER</h4><blockquote><p>⚠️ 只能 新增字段，不能修改或删除已有字段</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FT.ALTER idx:user SCHEMA ADD $.vip AS vip TAG</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">rSearch.alter(</span><br><span class="line">        <span class="string">&quot;idx:user&quot;</span>,</span><br><span class="line">        <span class="literal">false</span>,</span><br><span class="line">        FieldIndex.tag(<span class="string">&quot;$.vip&quot;</span>).as(<span class="string">&quot;vip&quot;</span>)</span><br><span class="line">);</span><br></pre></td></tr></table></figure><h4 id="3️⃣-FT-DROPINDEX">3️⃣ FT.DROPINDEX</h4><blockquote><p>⚠️ 删除索引</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">FT.DROPINDEX idx:user</span><br><span class="line"><span class="comment"># ⚠️ DD：删除索引的同时删除被索引数据，谨慎使用</span></span><br><span class="line">FT.DROPINDEX idx:user DD</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">rSearch.dropIndex(<span class="string">&quot;idx:user&quot;</span>);</span><br><span class="line">rSearch.dropIndexAndDocuments(<span class="string">&quot;idx:user&quot;</span>);</span><br></pre></td></tr></table></figure><h4 id="4️⃣-FT-INFO">4️⃣ <a href="http://FT.INFO">FT.INFO</a></h4><blockquote><p>查看索引信息</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FT.INFO idx:user</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">IndexInfo</span> <span class="variable">info</span> <span class="operator">=</span> rSearch.info(<span class="string">&quot;idx:user&quot;</span>);</span><br></pre></td></tr></table></figure><blockquote><p>重点关注字段</p></blockquote><table><thead><tr><th>字段</th><th>含义</th><th>解释</th></tr></thead><tbody><tr><td><code>num_docs</code></td><td><strong>当前被索引、可被搜索的文档数量</strong></td><td>user:10001 ~ user:10010，10条文档</td></tr><tr><td><code>num_terms</code></td><td><strong>词典中唯一词项 term 的数量</strong><br><strong>主要来源于 TEXT（name） 和 TAG（vip, status） 字段去重之后的数量</strong></td><td>name: alice, bob, carol, …<br>vip: yes, no<br>status: PAID, CREATED, CANCELLED<br></td></tr><tr><td><code>num_records</code></td><td><strong>倒排索引中的记录条目总数</strong><br><strong>所有 term 在所有文档中的出现次数之和</strong></td><td>JSON 数组字段 orders[],每个 order 都会生成独立的索引 entry<br>多值字段 × 多文档 = record 爆炸式增长<br><br>num_records 真正影响：内存、查询速度、聚合成本</td></tr></tbody></table><h4 id="5️⃣-FT-LIST">5️⃣ FT._LIST</h4><blockquote><p>列出所有索引</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FT._LIST</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List&lt;String&gt; indexes = rSearch.getIndexes();</span><br></pre></td></tr></table></figure><h3 id="二、查询类（核心）">二、查询类（核心）</h3><table><thead><tr><th>命令</th><th>作用</th><th>适用场景</th></tr></thead><tbody><tr><td><code>FT.SEARCH</code></td><td>标准搜索</td><td>90% 场景</td></tr><tr><td><code>FT.AGGREGATE</code></td><td>分组 / 统计 / 聚合</td><td>报表、分析</td></tr><tr><td><code>FT.HYBRID</code></td><td>文本 + 向量混合</td><td>向量搜索</td></tr><tr><td><code>FT.EXPLAIN</code></td><td>查询执行计划</td><td>调优</td></tr><tr><td><code>FT.EXPLAINCLI</code></td><td>CLI 可读执行计划</td><td>调试</td></tr><tr><td><code>FT.PROFILE</code></td><td>性能分析</td><td>慢查询</td></tr></tbody></table><h4 id="1️⃣-FT-SEARCH">1️⃣ FT.SEARCH</h4><ul class="lvl-0"><li class="lvl-2"><p>FT.SEARCH 基本语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">FT.SEARCH index query</span><br><span class="line">[NOCOENTENT] [VERBATIM] [NOSTOPWORDS]</span><br><span class="line">[WITHSCORES] [WITHPAYLOADS] [WITHSORTKEYS]</span><br><span class="line">[FILTER numeric_field min max ...]</span><br><span class="line">[GEOFILTER geo_field lon lat radius m|km|mi|ft]</span><br><span class="line">[INKEYS count key ...]</span><br><span class="line">[INFIELDS count field ...]</span><br><span class="line">[RETURN count identifier [AS property] ...]</span><br><span class="line">[SUMMARIZE FIELDS count field ... FRAGS num LEN fragsize SEPARATOR sep]</span><br><span class="line">[HIGHLIGHT FIELDS count field ... TAGS open close]</span><br><span class="line">[SLOP slop] [TIMEOUT <span class="built_in">timeout</span>] [INORDER]</span><br><span class="line">[LANGUAGE language]</span><br><span class="line">[EXPANDER expander]</span><br><span class="line">[SCORER scorer]</span><br><span class="line">[EXPLAINSCORE]</span><br><span class="line">[PAYLOAD payload]</span><br><span class="line">[SORTBY sortby ASC|DESC]</span><br><span class="line">[LIMIT offset num]</span><br><span class="line">[PARAMS nargs name value ...]</span><br><span class="line">[DIALECT dialect]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>FT.SEARCH 参数说明表</p></li></ul><table><thead><tr><th>参数</th><th>作用说明</th></tr></thead><tbody><tr><td><code>index</code></td><td>索引名称</td></tr><tr><td><code>query</code></td><td>查询条件（类似 SQL WHERE）</td></tr><tr><td><code>NOCONTENT</code></td><td>仅返回文档 ID，不返回内容</td></tr><tr><td><code>VERBATIM</code></td><td>禁用查询优化，完全按原查询执行</td></tr><tr><td><code>NOSTOPWORDS</code></td><td>查询时不忽略停用词</td></tr><tr><td><code>WITHSCORES</code></td><td>返回匹配文档的相关性评分</td></tr><tr><td><code>WITHPAYLOADS</code></td><td>返回文档的 payload 数据</td></tr><tr><td><code>WITHSORTKEYS</code></td><td>返回排序使用的 key</td></tr><tr><td><code>FILTER</code></td><td>数值字段过滤（范围查询）</td></tr><tr><td><code>GEOFILTER</code></td><td>地理位置范围查询</td></tr><tr><td><code>INKEYS</code></td><td>仅在指定的 key 集合中搜索</td></tr><tr><td><code>INFIELDS</code></td><td>仅在指定字段中搜索</td></tr><tr><td><code>RETURN</code></td><td>指定返回的字段</td></tr><tr><td><code>SUMMARIZE</code></td><td>返回字段内容摘要</td></tr><tr><td><code>HIGHLIGHT</code></td><td>高亮匹配的关键词</td></tr><tr><td><code>SLOP</code></td><td>设置短语查询中允许的词距</td></tr><tr><td><code>TIMEOUT</code></td><td>设置查询超时时间</td></tr><tr><td><code>INORDER</code></td><td>短语必须按顺序匹配</td></tr><tr><td><code>LANGUAGE</code></td><td>指定查询语言</td></tr><tr><td><code>EXPANDER</code></td><td>使用自定义查询扩展器</td></tr><tr><td><code>SCORER</code></td><td>使用自定义评分函数</td></tr><tr><td><code>EXPLAINSCORE</code></td><td>返回评分计算详情</td></tr><tr><td><code>PAYLOAD</code></td><td>给评分函数传入自定义参数</td></tr><tr><td><code>SORTBY</code></td><td>按指定字段排序</td></tr><tr><td><code>ASC / DESC</code></td><td>排序方向</td></tr><tr><td><code>LIMIT</code></td><td>分页控制</td></tr><tr><td><code>PARAMS</code></td><td>参数化查询</td></tr><tr><td><code>DIALECT</code></td><td>指定查询语法版本</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>FT.SEARCH 核心语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">FT.SEARCH index</span><br><span class="line">query</span><br><span class="line">[RETURN count identifier [AS property] ...]</span><br><span class="line">[SORTBY sortby ASC|DESC]</span><br><span class="line">[LIMIT offset num]</span><br></pre></td></tr></table></figure><blockquote><p>标准搜索</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">FT.SEARCH idx:user</span><br><span class="line">  <span class="string">&#x27;@status:&#123;PAID&#125; @amount:[50 +inf]&#x27;</span></span><br><span class="line">  RETURN 3 name status amount</span><br><span class="line">  SORTBY amount DESC</span><br><span class="line">  LIMIT 0 10</span><br><span class="line"><span class="comment">## 参数说明</span></span><br><span class="line">  <span class="comment"># 查询条件：&#x27;@status:&#123;PAID&#125; @amount:[50 +inf]&#x27;</span></span><br><span class="line">  <span class="comment"># 查询条件语法：</span></span><br><span class="line">  <span class="comment"># &#x27;xxx&#x27;: 全文检索包含 xxx 的数据，会从 TEXT 字段中匹配</span></span><br><span class="line">  <span class="comment"># &#x27;@field:value&#x27;: 匹配字段 field 的值是 value，要求 field 的类型为 TEXT，全文检索</span></span><br><span class="line">  <span class="comment"># &#x27;@field:&#123;xxx&#125;&#x27;:精确匹配，&#123;xxx*&#125;:匹配前缀，&#123;xxx|yyy&#125;:匹配任意一个，要求 field 的类型为 TAG</span></span><br><span class="line">  <span class="comment"># &#x27;@field:[min max]&#x27;: 范围匹配，这里 +inf 表示正无穷大，要求 field 的类型为 NUMERIC</span></span><br><span class="line">  <span class="comment"># 多个条件空格分隔</span></span><br><span class="line">  <span class="comment"># 模糊匹配：* 表示任意字符，即匹配所有</span></span><br><span class="line"><span class="comment"># RETURN 3 name status amount 返回字段，3:表示返回 3 个字段</span></span><br><span class="line"><span class="comment"># LIMIT 0 10 分页，0:表示从第 0 条开始，10:表示返回 10 条数据</span></span><br><span class="line"><span class="comment"># SORTBY amount DESC 排序，amount:表示排序字段，DESC:表示降序</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">SearchResult</span> <span class="variable">result</span> <span class="operator">=</span> rSearch.search(</span><br><span class="line">        <span class="string">&quot;idx:user&quot;</span>,</span><br><span class="line">        <span class="string">&quot;@status:&#123;PAID&#125; @amount:[50 +inf]&quot;</span>,</span><br><span class="line">        QueryOptions.defaults()</span><br><span class="line">        .returnAttributes(<span class="keyword">new</span> <span class="title class_">ReturnAttribute</span>(<span class="string">&quot;name&quot;</span>), <span class="keyword">new</span> <span class="title class_">ReturnAttribute</span>(<span class="string">&quot;status&quot;</span>),<span class="keyword">new</span> <span class="title class_">ReturnAttribute</span>(<span class="string">&quot;amount&quot;</span>))</span><br><span class="line">                .sortBy(<span class="string">&quot;amount&quot;</span>)</span><br><span class="line">                .sortOrder(SortOrder.DESC)</span><br><span class="line">                .limit(<span class="number">0</span>, <span class="number">10</span>));</span><br><span class="line"></span><br><span class="line"><span class="type">long</span> <span class="variable">total</span> <span class="operator">=</span> result.getTotal();</span><br><span class="line">System.out.println(<span class="string">&quot;total = &quot;</span> + total);</span><br><span class="line">List&lt;Document&gt; docs = result.getDocuments();</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (Document doc: docs) &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> doc.getId();</span><br><span class="line">    Map&lt;String, Object&gt; attrs = doc.getAttributes();</span><br><span class="line">    System.out.println(<span class="string">&quot;id = &quot;</span> + id + <span class="string">&quot; attrs = &quot;</span> + attrs);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 全文检索，从 所有 TEXT 字段中匹配，不区分大小写，即 phone/Phone/PHONE 都匹配</span></span><br><span class="line">FT.SEARCH idx:user <span class="string">&#x27;phone&#x27;</span></span><br><span class="line"><span class="comment">## 示例结果</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 2</span><br><span class="line">2) <span class="string">&quot;user:10006&quot;</span></span><br><span class="line">3) 1) <span class="string">&quot;$&quot;</span></span><br><span class="line">   2) <span class="string">&quot;&#123;\&quot;name\&quot;:\&quot;Frank\&quot;,\&quot;age\&quot;:27,\&quot;vip\&quot;:\&quot;no\&quot;,\&quot;orders\&quot;:[&#123;\&quot;amount\&quot;:499,\&quot;status\&quot;:\&quot;PAID\&quot;&#125;,&#123;\&quot;amount\&quot;:129,\&quot;status\&quot;:\&quot;CREATED\&quot;&#125;],\&quot;comment\&quot;:\&quot;I have a phone\&quot;,\&quot;items\&quot;:[\&quot;Redis\xe5\xbc\x80\xe5\x8f\x91\xe5\xae\x9e\xe6\x88\x98\&quot;,\&quot;MongoDB\xe4\xbb\x8e\xe5\x85\xa5\xe9\x97\xa8\xe5\x88\xb0\xe5\xae\x9e\xe6\x88\x98\&quot;]&#125;&quot;</span></span><br><span class="line">4) <span class="string">&quot;user:10001&quot;</span></span><br><span class="line">5) 1) <span class="string">&quot;$&quot;</span></span><br><span class="line">   2) <span class="string">&quot;&#123;\&quot;name\&quot;:\&quot;Alice Bob\&quot;,\&quot;age\&quot;:28,\&quot;vip\&quot;:\&quot;yes\&quot;,\&quot;orders\&quot;:[&#123;\&quot;amount\&quot;:199.99,\&quot;status\&quot;:\&quot;PAID\&quot;&#125;,&#123;\&quot;amount\&quot;:59.9,\&quot;status\&quot;:\&quot;CREATED\&quot;&#125;],\&quot;comment\&quot;:\&quot;I have a phone\&quot;,\&quot;items\&quot;:[\&quot;SpringCloud\xe6\x8a\x80\xe6\x9c\xaf\xe6\x8c\x87\xe5\x8d\x97\&quot;,\&quot;Shell\xe8\x84\x9a\xe6\x9c\xac\xe5\x9f\xba\xe7\xa1\x80\&quot;]&#125;&quot;</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">SearchResult</span> <span class="variable">result</span> <span class="operator">=</span> rSearch.search(</span><br><span class="line">        <span class="string">&quot;idx:user&quot;</span>,</span><br><span class="line">        <span class="string">&quot;phone&quot;</span>,</span><br><span class="line">        QueryOptions.defaults()</span><br><span class="line">);</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 中文检索</span></span><br><span class="line">FT.SEARCH idx:user <span class="string">&#x27;开始&#x27;</span></span><br><span class="line"><span class="comment">## 示例结果</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 2</span><br><span class="line">2) <span class="string">&quot;user:10005&quot;</span></span><br><span class="line">3) 1) <span class="string">&quot;$&quot;</span></span><br><span class="line">   2) <span class="string">&quot;&#123;\&quot;name\&quot;:\&quot;Eve\&quot;,\&quot;age\&quot;:30,\&quot;vip\&quot;:\&quot;no\&quot;,\&quot;orders\&quot;:[&#123;\&quot;amount\&quot;:88.8,\&quot;status\&quot;:\&quot;CREATED\&quot;&#125;],\&quot;comment\&quot;:\&quot;I have a pencil\&quot;,\&quot;items\&quot;:[\&quot;Kafka\xe4\xbb\x8e\xe9\x9b\xb6\xe5\xbc\x80\xe5\xa7\x8b\&quot;,\&quot;Java\xe7\x94\xb1\xe6\xb5\x85\xe5\x85\xa5\xe6\xb7\xb1\&quot;]&#125;&quot;</span></span><br><span class="line">4) <span class="string">&quot;user:10007&quot;</span></span><br><span class="line">5) 1) <span class="string">&quot;$&quot;</span></span><br><span class="line">   2) <span class="string">&quot;&#123;\&quot;name\&quot;:\&quot;Grace\&quot;,\&quot;age\&quot;:33,\&quot;vip\&quot;:\&quot;yes\&quot;,\&quot;orders\&quot;:[&#123;\&quot;amount\&quot;:999.9,\&quot;status\&quot;:\&quot;PAID\&quot;&#125;],\&quot;comment\&quot;:\&quot;I have a book\&quot;,\&quot;items\&quot;:[\&quot;Android\xe5\xbc\x80\xe5\x8f\x91\xe6\x89\x8b\xe5\x86\x8c\&quot;,\&quot;Gradle\xe4\xbb\x8e\xe9\x9b\xb6\xe5\xbc\x80\xe5\xa7\x8b\&quot;]&#125;&quot;</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">SearchResult</span> <span class="variable">result</span> <span class="operator">=</span> rSearch.search(</span><br><span class="line">        <span class="string">&quot;idx:user&quot;</span>,</span><br><span class="line">        <span class="string">&quot;开始&quot;</span>,</span><br><span class="line">        QueryOptions.defaults()</span><br><span class="line">);</span><br></pre></td></tr></table></figure><div class="tips"><p><em><strong>小贴士</strong></em></p><ul class="lvl-1"><li class="lvl-2">看到中文输出是乱码，可以在登录客户端时加上 --raw</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">redis-cli --user admin -a 123456 --raw</span><br><span class="line">127.0.0.1:6379&gt; FT.SEARCH idx:user <span class="string">&#x27;开始&#x27;</span></span><br><span class="line">2</span><br><span class="line">user:10005</span><br><span class="line">$</span><br><span class="line">&#123;<span class="string">&quot;name&quot;</span>:<span class="string">&quot;Eve&quot;</span>,<span class="string">&quot;age&quot;</span>:30,<span class="string">&quot;vip&quot;</span>:<span class="string">&quot;no&quot;</span>,<span class="string">&quot;orders&quot;</span>:[&#123;<span class="string">&quot;amount&quot;</span>:88.8,<span class="string">&quot;status&quot;</span>:<span class="string">&quot;CREATED&quot;</span>&#125;],<span class="string">&quot;comment&quot;</span>:<span class="string">&quot;I have a pencil&quot;</span>,<span class="string">&quot;items&quot;</span>:[<span class="string">&quot;Kafka从零开始&quot;</span>,<span class="string">&quot;Java由浅入深&quot;</span>]&#125;</span><br><span class="line">user:10007</span><br><span class="line">$</span><br><span class="line">&#123;<span class="string">&quot;name&quot;</span>:<span class="string">&quot;Grace&quot;</span>,<span class="string">&quot;age&quot;</span>:33,<span class="string">&quot;vip&quot;</span>:<span class="string">&quot;yes&quot;</span>,<span class="string">&quot;orders&quot;</span>:[&#123;<span class="string">&quot;amount&quot;</span>:999.9,<span class="string">&quot;status&quot;</span>:<span class="string">&quot;PAID&quot;</span>&#125;],<span class="string">&quot;comment&quot;</span>:<span class="string">&quot;I have a book&quot;</span>,<span class="string">&quot;items&quot;</span>:[<span class="string">&quot;Android开发手册&quot;</span>,<span class="string">&quot;Gradle从零开始&quot;</span>]&#125;</span><br></pre></td></tr></table></figure></div><h5 id="SQL-WHERE-与-RediSearch-查询语法对照表">SQL WHERE 与 RediSearch 查询语法对照表</h5><ul class="lvl-0"><li class="lvl-2"><p><code>x/y/name</code> → <code>TEXT/TAG(要加上&#123;&#125;)</code> 字段，<code>num</code>→<code>NUMERIC</code> 字段，查询语法用于 <code>FT.SEARCH</code>/<code>FT.AGGREGATE</code> 的查询字符串部分</p></li></ul><table><thead><tr><th>ID</th><th>SQL 条件</th><th>RediSearch 对等写法</th><th>说明</th></tr></thead><tbody><tr><td>1</td><td><code>WHERE x = 'book' AND y = 'phone'</code></td><td><code>@x:book @y:phone</code></td><td>AND 为默认关系（空格）</td></tr><tr><td>2</td><td><code>WHERE x = 'book' AND y != 'phone'</code></td><td><code>@x:book -@y:phone</code></td><td><code>-</code> 表示 NOT</td></tr><tr><td>3</td><td><code>WHERE x = 'book' OR y = 'phone'</code></td><td><code>(@x:book) | (@y:phone)</code></td><td>OR 必须显式写 <code>|</code></td></tr><tr><td>4</td><td><code>WHERE x IN ('book','phone','hello world')</code></td><td><code>@x:(book|phone|&quot;hello world&quot;)</code></td><td>多值 OR，短语需加引号</td></tr><tr><td>5</td><td><code>WHERE y='book' AND x NOT IN ('book','phone')</code></td><td><code>@y:book -(@x:book|@x:phone)</code></td><td>NOT IN = NOT + OR</td></tr><tr><td>6</td><td><code>WHERE x NOT IN ('book','phone')</code></td><td><code>-@x:(book|phone)</code></td><td>整体否定</td></tr><tr><td>7</td><td><code>WHERE num BETWEEN 10 AND 20</code></td><td><code>@num:[10 20]</code></td><td>数值区间（闭区间）</td></tr><tr><td>8</td><td><code>WHERE num &gt;= 10</code></td><td><code>@num:[10 +inf]</code></td><td><code>+inf</code> 表示正无穷</td></tr><tr><td>9</td><td><code>WHERE num &gt; 10</code></td><td><code>@num:[(10 +inf]</code></td><td><code>(</code> 表示不包含</td></tr><tr><td>10</td><td><code>WHERE num &lt; 10</code></td><td><code>@num:[-inf (10]</code></td><td>小于（不含 10）</td></tr><tr><td>11</td><td><code>WHERE num &lt;= 10</code></td><td><code>@num:[-inf 10]</code></td><td>小于等于</td></tr><tr><td>12</td><td><code>WHERE num &lt; 10 OR num &gt; 20</code></td><td><code>@num:[-inf (10] | @num:[(20 +inf]</code></td><td>数值 OR</td></tr><tr><td>13</td><td><code>WHERE name LIKE 'Li%'</code></td><td><code>@name:Li*</code></td><td>前缀匹配</td></tr></tbody></table><h4 id="2️⃣-FT-AGGREGATE">2️⃣ FT.AGGREGATE</h4><blockquote><p>分组 / 统计 / 聚合，语法</p></blockquote><ul class="lvl-0"><li class="lvl-2"><p>FT.AGGREGATE 基本语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">FT.AGGREGATE index query</span><br><span class="line">[VERBATIM]</span><br><span class="line">[LOAD count field ... | LOAD *]</span><br><span class="line">[TIMEOUT <span class="built_in">timeout</span>]</span><br><span class="line">[GROUPBY nargs property ...</span><br><span class="line">   REDUCE <span class="keyword">function</span> nargs arg ... [AS name] ...]</span><br><span class="line">[SORTBY nargs property ASC|DESC ... [MAX num]]</span><br><span class="line">[APPLY expression AS name]</span><br><span class="line">[LIMIT offset num]</span><br><span class="line">[FILTER filter]</span><br><span class="line">[WITHCURSOR [COUNT read_size]]</span><br><span class="line">[MAXIDLE idle_time]</span><br><span class="line">[PARAMS nargs name value ...]</span><br><span class="line">[DIALECT dialect]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>FT.AGGREGATE 参数说明表</p></li></ul><table><thead><tr><th>参数</th><th>作用说明</th></tr></thead><tbody><tr><td><code>index</code></td><td>索引名称</td></tr><tr><td><code>query</code></td><td>查询条件（类似 WHERE）</td></tr><tr><td><code>VERBATIM</code></td><td>禁用查询优化</td></tr><tr><td><code>LOAD</code></td><td>加载原始字段</td></tr><tr><td><code>TIMEOUT</code></td><td>查询超时时间</td></tr><tr><td><code>GROUPBY</code></td><td>分组字段（类似 SQL GROUP BY）</td></tr><tr><td><code>REDUCE</code></td><td>聚合函数（COUNT / SUM / AVG / MIN / MAX 等）</td></tr><tr><td><code>AS</code></td><td>聚合结果别名</td></tr><tr><td><code>SORTBY</code></td><td>对聚合结果排序</td></tr><tr><td><code>ASC / DESC</code></td><td>排序方向</td></tr><tr><td><code>MAX</code></td><td>最大返回条数</td></tr><tr><td><code>APPLY</code></td><td>对结果字段进行表达式计算</td></tr><tr><td><code>LIMIT</code></td><td>分页控制</td></tr><tr><td><code>FILTER</code></td><td>结果过滤（WHERE / HAVING）</td></tr><tr><td><code>WITHCURSOR</code></td><td>使用游标（大结果集）</td></tr><tr><td><code>COUNT</code></td><td>每次读取数量</td></tr><tr><td><code>MAXIDLE</code></td><td>游标最大空闲时间</td></tr><tr><td><code>PARAMS</code></td><td>参数化查询</td></tr><tr><td><code>DIALECT</code></td><td>查询语法版本</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>FT.AGGREGATE 的核心语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">FT.AGGREGATE &lt;index&gt; &lt;query&gt;</span><br><span class="line">  [GROUPBY ...]</span><br><span class="line">  [REDUCE ...]</span><br><span class="line">  [SORTBY ...]</span><br><span class="line">  [LIMIT ...]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 1：按 status 分组统计数量</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">FT.AGGREGATE idx:user</span><br><span class="line">  <span class="string">&#x27;*&#x27;</span></span><br><span class="line">  GROUPBY 1 @status</span><br><span class="line">  REDUCE COUNT 0 AS cnt</span><br><span class="line"><span class="comment">## 参数说明</span></span><br><span class="line"><span class="comment"># *: 查询条件，* 表示匹配索引中的 所有文档，也可以是 FT.SEARCH 中的查询条件，比如：&#x27;@status:&#123;PAID&#125; @amount:[50 +inf]&#x27;</span></span><br><span class="line"><span class="comment"># GROUPBY 1 @status: 分组字段</span></span><br><span class="line">  <span class="comment"># 1:表示分组字段数量</span></span><br><span class="line">  <span class="comment"># @status:表示分组字段</span></span><br><span class="line"><span class="comment"># REDUCE COUNT 0 AS cnt: 聚合字段</span></span><br><span class="line">  <span class="comment"># COUNT:聚合函数：计数</span></span><br><span class="line">  <span class="comment"># 0:参数个数（COUNT 不需要参数）</span></span><br><span class="line">  <span class="comment"># AS cnt:表示聚合字段别名</span></span><br><span class="line"><span class="comment">## 返回结果示例</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 3</span><br><span class="line">2) 1) <span class="string">&quot;status&quot;</span></span><br><span class="line">   2) <span class="string">&quot;CANCELLED&quot;</span></span><br><span class="line">   3) <span class="string">&quot;cnt&quot;</span></span><br><span class="line">   4) <span class="string">&quot;2&quot;</span></span><br><span class="line">3) 1) <span class="string">&quot;status&quot;</span></span><br><span class="line">   2) <span class="string">&quot;PAID&quot;</span></span><br><span class="line">   3) <span class="string">&quot;cnt&quot;</span></span><br><span class="line">   4) <span class="string">&quot;6&quot;</span></span><br><span class="line">4) 1) <span class="string">&quot;status&quot;</span></span><br><span class="line">   2) <span class="string">&quot;CREATED&quot;</span></span><br><span class="line">   3) <span class="string">&quot;cnt&quot;</span></span><br><span class="line">   4) <span class="string">&quot;2&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 对应SQL 语句：</span></span><br><span class="line">SELECT status, COUNT(*) AS cnt</span><br><span class="line">  FROM user</span><br><span class="line">  GROUP BY status;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">AggregationResult</span> <span class="variable">result</span> <span class="operator">=</span> rSearch.aggregate(</span><br><span class="line">        <span class="string">&quot;idx:user&quot;</span>,</span><br><span class="line">        <span class="string">&quot;*&quot;</span>,</span><br><span class="line">        AggregationOptions.defaults()</span><br><span class="line">                .groupBy(GroupBy.fieldNames(<span class="string">&quot;@status&quot;</span>).reducers(Reducer.count().as(<span class="string">&quot;cnt&quot;</span>)))</span><br><span class="line">);</span><br><span class="line"><span class="keyword">final</span> <span class="type">long</span> <span class="variable">total</span> <span class="operator">=</span> result.getTotal();</span><br><span class="line">System.out.println(<span class="string">&quot;total = &quot;</span> + total);</span><br><span class="line"><span class="keyword">final</span> List&lt;Map&lt;String, Object&gt;&gt; attributes = result.getAttributes();</span><br><span class="line"><span class="keyword">for</span> (Map&lt;String, Object&gt; attribute : attributes) &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;attribute = &quot;</span> + attribute);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 2：按 status 分组统计金额总和</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">FT.AGGREGATE idx:user <span class="string">&#x27;*&#x27;</span></span><br><span class="line">  GROUPBY 1 @status</span><br><span class="line">  REDUCE SUM 1 @amount AS total_amount</span><br><span class="line">  SORTBY 2 @total_amount DESC</span><br><span class="line">  LIMIT 0 3</span><br><span class="line"></span><br><span class="line"><span class="comment">## 参数说明</span></span><br><span class="line"><span class="comment"># SUM:聚合函数：求和</span></span><br><span class="line"><span class="comment"># 1:参数个数</span></span><br><span class="line"><span class="comment"># @amount:要参与求和的字段</span></span><br><span class="line"><span class="comment"># total_amount:表示聚合字段别名</span></span><br><span class="line"><span class="comment"># SORTBY 2 @total_amount DESC: 排序</span></span><br><span class="line">  <span class="comment"># 2: 表示后面跟 2 个参数（字段 + 排序方向）</span></span><br><span class="line">  <span class="comment"># @total_amount: 表示排序字段（REDUCE 产生的别名），DESC:表示降序</span></span><br><span class="line"><span class="comment"># LIMIT 0 3: 分页，0:表示从第 0 条开始，3:表示返回 3 条数据</span></span><br><span class="line"></span><br><span class="line"><span class="comment">## 示例结果</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 3</span><br><span class="line">2) 1) <span class="string">&quot;status&quot;</span></span><br><span class="line">   2) <span class="string">&quot;PAID&quot;</span></span><br><span class="line">   3) <span class="string">&quot;total_amount&quot;</span></span><br><span class="line">   4) <span class="string">&quot;4096.89&quot;</span></span><br><span class="line">3) 1) <span class="string">&quot;status&quot;</span></span><br><span class="line">   2) <span class="string">&quot;CREATED&quot;</span></span><br><span class="line">   3) <span class="string">&quot;total_amount&quot;</span></span><br><span class="line">   4) <span class="string">&quot;238.8&quot;</span></span><br><span class="line">4) 1) <span class="string">&quot;status&quot;</span></span><br><span class="line">   2) <span class="string">&quot;CANCELLED&quot;</span></span><br><span class="line">   3) <span class="string">&quot;total_amount&quot;</span></span><br><span class="line">   4) <span class="string">&quot;79.8&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 对应SQL 语句：</span></span><br><span class="line">SELECT status, SUM(amount) AS total_amount</span><br><span class="line">  FROM user</span><br><span class="line">  GROUP BY status</span><br><span class="line">  ORDER BY total_amount DESC</span><br><span class="line">  LIMIT 0 3;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">AggregationResult</span> <span class="variable">result</span> <span class="operator">=</span> rSearch.aggregate(</span><br><span class="line">        <span class="string">&quot;idx:user&quot;</span>,</span><br><span class="line">        <span class="string">&quot;*&quot;</span>,</span><br><span class="line">        AggregationOptions.defaults()</span><br><span class="line">                .groupBy(GroupBy.fieldNames(<span class="string">&quot;@status&quot;</span>)</span><br><span class="line">                        .reducers(</span><br><span class="line">                                Reducer.sum(<span class="string">&quot;@amount&quot;</span>).as(<span class="string">&quot;total_amount&quot;</span>)</span><br><span class="line">                        )</span><br><span class="line">                )</span><br><span class="line">                .sortBy(<span class="keyword">new</span> <span class="title class_">SortedField</span>(<span class="string">&quot;@total_amount&quot;</span>, SortOrder.DESC))</span><br><span class="line">                .limit(<span class="number">0</span>, <span class="number">3</span>)</span><br><span class="line">);</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 3：同时输出数量 + 金额，并按金额降序，数量升序</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">FT.AGGREGATE idx:user <span class="string">&#x27;*&#x27;</span></span><br><span class="line">  GROUPBY 1 @status</span><br><span class="line">  REDUCE COUNT 0 AS cnt</span><br><span class="line">  REDUCE SUM 1 @amount AS total_amount</span><br><span class="line">  SORTBY 4 @total_amount DESC @cnt ASC</span><br><span class="line"></span><br><span class="line"><span class="comment">## 示例结果</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 3</span><br><span class="line">2) 1) <span class="string">&quot;status&quot;</span></span><br><span class="line">   2) <span class="string">&quot;PAID&quot;</span></span><br><span class="line">   3) <span class="string">&quot;cnt&quot;</span></span><br><span class="line">   4) <span class="string">&quot;6&quot;</span></span><br><span class="line">   5) <span class="string">&quot;total_amount&quot;</span></span><br><span class="line">   6) <span class="string">&quot;4096.89&quot;</span></span><br><span class="line">3) 1) <span class="string">&quot;status&quot;</span></span><br><span class="line">   2) <span class="string">&quot;CREATED&quot;</span></span><br><span class="line">   3) <span class="string">&quot;cnt&quot;</span></span><br><span class="line">   4) <span class="string">&quot;2&quot;</span></span><br><span class="line">   5) <span class="string">&quot;total_amount&quot;</span></span><br><span class="line">   6) <span class="string">&quot;238.8&quot;</span></span><br><span class="line">4) 1) <span class="string">&quot;status&quot;</span></span><br><span class="line">   2) <span class="string">&quot;CANCELLED&quot;</span></span><br><span class="line">   3) <span class="string">&quot;cnt&quot;</span></span><br><span class="line">   4) <span class="string">&quot;2&quot;</span></span><br><span class="line">   5) <span class="string">&quot;total_amount&quot;</span></span><br><span class="line">   6) <span class="string">&quot;79.8&quot;</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">AggregationResult</span> <span class="variable">result</span> <span class="operator">=</span> rSearch.aggregate(</span><br><span class="line">        <span class="string">&quot;idx:user&quot;</span>,</span><br><span class="line">        <span class="string">&quot;*&quot;</span>,</span><br><span class="line">        AggregationOptions.defaults()</span><br><span class="line">                .groupBy(GroupBy.fieldNames(<span class="string">&quot;@status&quot;</span>)</span><br><span class="line">                        .reducers(</span><br><span class="line">                                Reducer.count().as(<span class="string">&quot;cnt&quot;</span>),</span><br><span class="line">                                Reducer.sum(<span class="string">&quot;@amount&quot;</span>).as(<span class="string">&quot;total_amount&quot;</span>)</span><br><span class="line">                        )</span><br><span class="line">                )</span><br><span class="line">                .sortBy(<span class="keyword">new</span> <span class="title class_">SortedField</span>(<span class="string">&quot;@total_amount&quot;</span>, SortOrder.DESC))</span><br><span class="line">                .sortBy(<span class="keyword">new</span> <span class="title class_">SortedField</span>(<span class="string">&quot;@cnt&quot;</span>, SortOrder.ASC))</span><br><span class="line">);</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>示例 4：按状态分组，筛选总金额 &gt; 200</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">FT.AGGREGATE idx:user <span class="string">&#x27;*&#x27;</span></span><br><span class="line">  GROUPBY 1 @status</span><br><span class="line">  REDUCE SUM 1 @amount AS total_amount</span><br><span class="line">  FILTER @total_amount &gt; 200</span><br><span class="line">  SORTBY 2 @total_amount DESC</span><br><span class="line"><span class="comment">## 参数说明</span></span><br><span class="line"><span class="comment"># FILTER: 过滤器，作用在 GROUPBY + REDUCE 之后，语义等价于 SQL 的：HAVING SUM(amount) &gt; 1000</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">AggregationResult</span> <span class="variable">result</span> <span class="operator">=</span> rSearch.aggregate(</span><br><span class="line">        <span class="string">&quot;idx:user&quot;</span>,</span><br><span class="line">        <span class="string">&quot;*&quot;</span>,</span><br><span class="line">        AggregationOptions.defaults()</span><br><span class="line">                .groupBy(GroupBy.fieldNames(<span class="string">&quot;@status&quot;</span>)</span><br><span class="line">                        .reducers(</span><br><span class="line">                                Reducer.sum(<span class="string">&quot;@amount&quot;</span>).as(<span class="string">&quot;total_amount&quot;</span>)</span><br><span class="line">                        )</span><br><span class="line">                )</span><br><span class="line">                .filter(<span class="string">&quot;@total_amount &gt; 200&quot;</span>)</span><br><span class="line">                .sortBy(<span class="keyword">new</span> <span class="title class_">SortedField</span>(<span class="string">&quot;@total_amount&quot;</span>, SortOrder.DESC))</span><br><span class="line">);</span><br></pre></td></tr></table></figure><blockquote><p>语义执行顺序： SCAN → GROUPBY → REDUCE → FILTER (HAVING) → SORTBY → LIMIT</p></blockquote><ul class="lvl-0"><li class="lvl-2"><p>常见可用 REDUCE 函数</p></li></ul><table><thead><tr><th>函数</th><th>用途</th></tr></thead><tbody><tr><td>COUNT</td><td>行数</td></tr><tr><td>SUM</td><td>求和</td></tr><tr><td>AVG</td><td>平均</td></tr><tr><td>MIN / MAX</td><td>极值</td></tr><tr><td>TO_LIST</td><td>收集字段</td></tr></tbody></table><h4 id="3️⃣-FT-EXPLAIN-FT-EXPLAINCLI">3️⃣ FT.EXPLAIN/FT.EXPLAINCLI</h4><blockquote><p>查询执行计划， 不会考虑分组聚合，只看查询条件，它的定位是：看“会不会用索引、怎么用索引”，而不是看“跑得慢不慢”。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">FT.EXPLAINCLI idx:user <span class="string">&#x27;@status:&#123;PAID&#125;  @amount:[100 +inf]&#x27;</span></span><br><span class="line"><span class="comment">## 示例结果</span></span><br><span class="line">1) INTERSECT &#123;</span><br><span class="line">2)   TAG:@status &#123;</span><br><span class="line">3)     paid</span><br><span class="line">4)   &#125;</span><br><span class="line">5)   NUMERIC &#123;100.000000 &lt;= @amount &lt;= inf&#125;</span><br><span class="line">6) &#125;</span><br><span class="line">7)</span><br></pre></td></tr></table></figure><h4 id="4️⃣-FT-PROFILE">4️⃣ FT.PROFILE</h4><blockquote><p>性能分析，看“跑得慢不慢”。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">FT.PROFILE idx:user</span><br><span class="line">  SEARCH</span><br><span class="line">  QUERY <span class="string">&#x27;@status:&#123;PAID&#125;  @amount:[100 +inf]&#x27;</span></span><br><span class="line"><span class="comment">## 参数说明</span></span><br><span class="line"><span class="comment"># SEARCH: 标准查询，AGGREGATE: 聚合查询</span></span><br><span class="line"><span class="comment"># QUERY: 查询条件</span></span><br><span class="line"></span><br><span class="line">FT.PROFILE idx:user</span><br><span class="line">  AGGREGATE</span><br><span class="line">  QUERY <span class="string">&quot;@status:&#123;PAID&#125; @amount:[100 +inf]&quot;</span></span><br><span class="line">  GROUPBY 1 @status</span><br><span class="line">  REDUCE SUM 1 @amount AS total_amount</span><br></pre></td></tr></table></figure><h5 id="关注指标">关注指标</h5><ul class="lvl-0"><li class="lvl-2"><p>第一优先级指标（直接决定查询是否慢）</p></li></ul><table><thead><tr><th>指标</th><th>所在位置</th><th>含义</th><th>健康阈值</th><th>需要警惕</th><th>常见优化手段</th></tr></thead><tbody><tr><td><strong>Total profile time</strong></td><td>Shards → Total profile time</td><td>查询在搜索引擎层面的总耗时（不含网络）</td><td><strong>&lt; 1 ms：优秀</strong><br>1–10 ms：可接受</td><td>&gt; 20 ms：需要优化<br>&gt; 50 ms：结构性问题</td><td>减少结果集、拆分查询、优化索引字段</td></tr><tr><td><strong>Iterator Type</strong></td><td>Iterators profile → Type</td><td>查询执行策略</td><td><strong>INTERSECT / UNION</strong></td><td><strong>SCAN / WILDCARD</strong></td><td>增加可索引字段、避免模糊前缀</td></tr><tr><td><strong>Estimated number of matches</strong></td><td>Child iterators</td><td>索引层估算的候选文档数</td><td><strong>&lt; 1k：理想</strong><br>1k–10k：可接受</td><td>&gt; 50k：过滤条件过宽</td><td>改用 TAG / NUMERIC / 冗余字段</td></tr><tr><td><strong>Loader Time</strong></td><td>Result processors → Loader</td><td>从 Redis 加载并反序列化文档</td><td>&lt; 30% Total time</td><td>&gt; 40% Total time</td><td>RETURN 精简字段、NOCONTENT、缩小 JSON</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>第二优先级指标（判断索引或模型是否合理）</p></li></ul><table><thead><tr><th>指标</th><th>所在位置</th><th>含义</th><th>健康阈值</th><th>需要警惕</th><th>常见优化手段</th></tr></thead><tbody><tr><td><strong>Number of reading operations</strong></td><td>Iterators profile</td><td>实际扫描的倒排表条目数</td><td>≈ Estimated matches</td><td>明显大于结果数</td><td>提高字段选择性、拆分条件</td></tr><tr><td><strong>Estimated matches vs 实际结果数</strong></td><td>Iterator + Result</td><td>索引估算精度</td><td>估算值略大于实际</td><td>估算 &gt;&gt; 实际（10x+）</td><td>调整 schema，避免低基数字段</td></tr><tr><td><strong>Parsing time</strong></td><td>Shards → Parsing time</td><td>查询语法解析耗时</td><td>&lt; 0.1 ms</td><td>&gt; 1 ms</td><td>减少动态拼接、简化语法</td></tr><tr><td><strong>Pipeline creation time</strong></td><td>Shards → Pipeline creation</td><td>执行计划构建时间</td><td>&lt; 0.05 ms</td><td>&gt; 0.5 ms</td><td>减少子句、避免复杂嵌套</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>经验型“红线判断”速查表（非常实用）</p></li></ul><table><thead><tr><th>现象</th><th>含义</th><th>是否必须处理</th></tr></thead><tbody><tr><td>Estimated matches &gt; 100k</td><td>索引字段选择错误</td><td>必须</td></tr><tr><td>Iterator Type = SCAN</td><td>全表扫描</td><td>必须</td></tr><tr><td>Loader &gt; 50% 总耗时</td><td>返回数据过重</td><td>强烈建议</td></tr><tr><td>reading ops ≫ results</td><td>倒排效率低</td><td>建议</td></tr><tr><td>Total time &gt; 20 ms</td><td>在线查询风险</td><td>必须</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>核心执行策略型 Iterator（最重要）</p></li></ul><table><thead><tr><th>Type</th><th>语义</th><th>典型触发场景</th><th>性能特征</th><th>调优结论</th></tr></thead><tbody><tr><td><strong>INTERSECT</strong></td><td>多条件取交集（AND）</td><td><code>@a:&#123;x&#125; @b:[1 10]</code></td><td>基于最小倒排表，效率最高</td><td><strong>理想状态，优先使用</strong></td></tr><tr><td><strong>UNION</strong></td><td>多条件取并集（OR）</td><td><code>@a:&#123;x|y&#125;</code></td><td>候选集扩大，随 OR 数量线性增长</td><td>可接受，避免过多 OR</td></tr><tr><td><strong>SCAN</strong></td><td>全量扫描</td><td>字段未索引 / JSON Path 不可索引</td><td>O(N)，文档越多越慢</td><td><strong>必须消灭</strong></td></tr><tr><td><strong>WILDCARD</strong></td><td>无过滤条件</td><td><code>&quot;*&quot;</code>、<code>@field:*</code></td><td>候选集=全部文档</td><td>仅限分析，线上慎用</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>索引访问型 Iterator（INTERSECT / UNION 的子节点）</p></li></ul><table><thead><tr><th>Type</th><th>对应字段类型</th><th>示例查询</th><th>性能特征</th><th>备注</th></tr></thead><tbody><tr><td><strong>TAG</strong></td><td>TAG</td><td><code>@status:&#123;PAID&#125;</code></td><td>哈希倒排，最快</td><td>强烈推荐</td></tr><tr><td><strong>NUMERIC</strong></td><td>NUMERIC</td><td><code>@amount:[100 +inf]</code></td><td>跳表范围扫描</td><td>范围越窄越快</td></tr><tr><td><strong>TEXT</strong></td><td>TEXT</td><td><code>@name:alice</code></td><td>分词匹配</td><td>分词数影响性能</td></tr><tr><td><strong>GEO</strong></td><td>GEO</td><td><code>@loc:[13.4 52.5 10 km]</code></td><td>空间索引</td><td>半径影响结果数</td></tr><tr><td><strong>VECTOR</strong></td><td>VECTOR</td><td><code>=&gt;[KNN 10 @v $q]</code></td><td>向量搜索</td><td>常与 FILTER 联用</td></tr><tr><td><strong>IDLIST</strong></td><td>内部</td><td>小结果集</td><td>枚举 ID</td><td>自动优化</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>逻辑 / 结构型 Iterator</p></li></ul><table><thead><tr><th>Type</th><th>含义</th><th>触发示例</th><th>性能影响</th><th>是否常见</th></tr></thead><tbody><tr><td><strong>NOT</strong></td><td>取反</td><td><code>-@status:&#123;PAID&#125;</code></td><td>依赖全集</td><td>较少</td></tr><tr><td><strong>OPTIONAL</strong></td><td>可选子句</td><td><code>( @a:&#123;x&#125; )?</code></td><td>增加候选集</td><td>少见</td></tr><tr><td><strong>EMPTY</strong></td><td>无匹配</td><td>条件不可能满足</td><td>极快</td><td>偶发</td></tr><tr><td><strong>PHRASE</strong></td><td>短语匹配</td><td><code>&quot;hello world&quot;</code></td><td>比 TEXT 慢</td><td>低频</td></tr><tr><td><strong>PREFIX</strong></td><td>前缀匹配</td><td><code>abc*</code></td><td>可能退化</td><td>需谨慎</td></tr></tbody></table><h3 id="三、别名管理（生产必备）">三、别名管理（生产必备）</h3><table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td><code>FT.ALIASADD</code></td><td>添加别名</td></tr><tr><td><code>FT.ALIASDEL</code></td><td>删除别名</td></tr><tr><td><code>FT.ALIASUPDATE</code></td><td>更新别名指向</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>一个索引可以有多个别名，一旦创建别名，所有 FT.SEARCH / FT.AGGREGATE / <a href="http://FT.INFO">FT.INFO</a> 等命令都可以直接使用 alias</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 添加别名: FT.ALIASADD alias index</span></span><br><span class="line">FT.ALIASADD <span class="built_in">alias</span>:user idx:user</span><br><span class="line"><span class="comment"># 更新别名指向: FT.ALIASUPDATE alias index</span></span><br><span class="line">FT.ALIASUPDATE <span class="built_in">alias</span>:user idx:user:v2</span><br><span class="line"><span class="comment"># 删除别名: FT.ALIASDEL alias</span></span><br><span class="line">FT.ALIASDEL <span class="built_in">alias</span>:user</span><br><span class="line"><span class="comment"># 判断索引名称是否为别名</span></span><br><span class="line">FT.INFO <span class="built_in">alias</span>:user</span><br><span class="line"> 1) index_name</span><br><span class="line"> 2) idx:user  <span class="comment"># 真实索引名称</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">rSearch.addAlias(<span class="string">&quot;alias:user&quot;</span>, <span class="string">&quot;idx:user&quot;</span>);</span><br><span class="line">rSearch.updateAlias(<span class="string">&quot;alias:user&quot;</span>, <span class="string">&quot;idx:user:v2&quot;</span>);</span><br><span class="line">rSearch.delAlias(<span class="string">&quot;alias:user&quot;</span>);</span><br></pre></td></tr></table></figure><h4 id="索引别名的核心价值">索引别名的核心价值</h4><ul class="lvl-0"><li class="lvl-2"><ol><li class="lvl-5">索引无损重建（最典型场景）</li></ol><ul class="lvl-2"><li class="lvl-4">RediSearch 不支持在线修改 schema，字段变更通常需要重建索引，索引重建时业务将不可用。</li><li class="lvl-4">有了别名，我们可以创建一个新的索引，创建好后将别名指向新的索引，旧索引不再使用，业务无影响。</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">1. 旧索引</span><br><span class="line"> FT.ALIASADD user_idx user_idx_v1</span><br><span class="line"></span><br><span class="line">2. 新建索引</span><br><span class="line">  FT.CREATE user_idx_v2 ...</span><br><span class="line"></span><br><span class="line">3. 数据回填完成后，原子切换</span><br><span class="line">  FT.ALIASUPDATE user_idx user_idx_v2</span><br><span class="line"></span><br><span class="line">4. 删除旧索引（可选）</span><br><span class="line">  FT.DROPINDEX user_idx_v1</span><br></pre></td></tr></table></figure></li><li class="lvl-2"><ol start="2"><li class="lvl-5">灰度 / 多版本并存</li></ol><ul class="lvl-2"><li class="lvl-4">不同环境（dev / staging / prod）</li><li class="lvl-4">不同 schema 版本</li></ul></li><li class="lvl-2"><ol start="3"><li class="lvl-5">运维与发布规范化（强烈推荐）</li></ol><ul class="lvl-2"><li class="lvl-4">在生产环境中：永远不要让业务代码直接使用物理索引名</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">索引真实名: 业务名_版本_时间戳</span><br><span class="line">索引别名 : 业务名</span><br></pre></td></tr></table></figure></li></ul><h3 id="四、游标（大结果集）">四、游标（大结果集）</h3><table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td><code>FT.CURSOR READ</code></td><td>分批读取</td></tr><tr><td><code>FT.CURSOR DEL</code></td><td>删除游标</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>游标用于聚合查询（AGGREGATE）的大结果集分页/分批拉取，典型场景包括：</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">GROUPBY / REDUCE 后结果集很大</span><br><span class="line">需要流式处理聚合结果，避免一次性返回占用大量内存</span><br><span class="line">客户端按批消费结果（类似数据库的 server-side cursor）</span><br><span class="line">注意：游标主要用于 FT.AGGREGATE，普通 FT.SEARCH 不支持游标分页。</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>通过 <code>FT.AGGREGATE ... WITHCURSOR</code> 创建游标，通过 <code>FT.CURSOR READ</code> 获取结果。</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">FT.AGGREGATE idx:user <span class="string">&quot;*&quot;</span></span><br><span class="line">  GROUPBY 1 @status</span><br><span class="line">  REDUCE SUM 1 @amount AS total_amount</span><br><span class="line">  WITHCURSOR COUNT 2 MAXIDLE 60000</span><br><span class="line"><span class="comment">## 参数说明</span></span><br><span class="line"><span class="comment"># WITHCURSOR : 启用游标</span></span><br><span class="line"><span class="comment"># COUNT 2 : 每次从游标中返回的行数（批大小）</span></span><br><span class="line"><span class="comment"># MAXIDLE 60000 : 游标最大空闲时间（毫秒），超时后自动销毁</span></span><br><span class="line"><span class="comment">## 返回结果</span></span><br><span class="line">1) 1) (<span class="built_in">integer</span>) 3   <span class="comment"># GROUP BY 后，一共会产生 3 行聚合结果，这里返回的是实际查询的总行数，不受COUNT 参数限制</span></span><br><span class="line">   2) 1) <span class="string">&quot;status&quot;</span></span><br><span class="line">      2) <span class="string">&quot;CANCELLED&quot;</span></span><br><span class="line">      3) <span class="string">&quot;total_amount&quot;</span></span><br><span class="line">      4) <span class="string">&quot;79.8&quot;</span></span><br><span class="line">   3) 1) <span class="string">&quot;status&quot;</span></span><br><span class="line">      2) <span class="string">&quot;PAID&quot;</span></span><br><span class="line">      3) <span class="string">&quot;total_amount&quot;</span></span><br><span class="line">      4) <span class="string">&quot;4096.89&quot;</span></span><br><span class="line">2) (<span class="built_in">integer</span>) 116452546  <span class="comment"># 游标ID</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取游标</span></span><br><span class="line">FT.CURSOR READ idx:user 116452546 COUNT 2</span><br><span class="line"><span class="comment"># 参数说明</span></span><br><span class="line"><span class="comment"># 116452546 : 游标ID</span></span><br><span class="line"><span class="comment"># COUNT 2 : 每次从游标中返回的行数(可覆盖初始游标中的 COUNT）</span></span><br><span class="line"><span class="comment">## 返回结果</span></span><br><span class="line">1) 1) (<span class="built_in">integer</span>) 0   <span class="comment"># 当前游标中“剩余可读的行数”为 0</span></span><br><span class="line">   2) 1) <span class="string">&quot;status&quot;</span></span><br><span class="line">      2) <span class="string">&quot;CREATED&quot;</span></span><br><span class="line">      3) <span class="string">&quot;total_amount&quot;</span></span><br><span class="line">      4) <span class="string">&quot;238.8&quot;</span></span><br><span class="line">2) (<span class="built_in">integer</span>) 0 <span class="comment"># 0 表示没有更多结果</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 销毁游标，如果设置了 MAXIDLE 参数，则该游标在 MAXIDLE 时间内自动销毁，所以无需手工销毁</span></span><br><span class="line">FT.CURSOR DEL idx:user 116452546</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">AggregationResult</span> <span class="variable">result</span> <span class="operator">=</span> rSearch.aggregate(</span><br><span class="line">        <span class="string">&quot;idx:user&quot;</span>,</span><br><span class="line">        <span class="string">&quot;*&quot;</span>,</span><br><span class="line">        AggregationOptions.defaults()</span><br><span class="line">                .groupBy(GroupBy.fieldNames(<span class="string">&quot;@status&quot;</span>)</span><br><span class="line">                        .reducers(</span><br><span class="line">                                Reducer.sum(<span class="string">&quot;@amount&quot;</span>).as(<span class="string">&quot;total_amount&quot;</span>)</span><br><span class="line">                        )</span><br><span class="line">                )</span><br><span class="line">                .withCursor(<span class="number">2</span>, <span class="number">60000</span>)</span><br><span class="line">);</span><br><span class="line"><span class="type">long</span> total;</span><br><span class="line"><span class="type">long</span> cursorId;</span><br><span class="line">List&lt;Map&lt;String, Object&gt;&gt; attributes;</span><br><span class="line"></span><br><span class="line">total = result.getTotal();</span><br><span class="line">cursorId = result.getCursorId();</span><br><span class="line">System.out.println(<span class="string">&quot;total = &quot;</span> + total);</span><br><span class="line">System.out.println(<span class="string">&quot;cursorId = &quot;</span> + cursorId);</span><br><span class="line">attributes = result.getAttributes();</span><br><span class="line"><span class="keyword">for</span> (Map&lt;String, Object&gt; attribute : attributes) &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;attribute = &quot;</span> + attribute);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (cursorId != <span class="number">0</span>) &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;===============================================&quot;</span>);</span><br><span class="line">    <span class="comment">// 游标读取，Redisson的readCursor方法有bug，无法继续读取游标数据</span></span><br><span class="line">    <span class="comment">// result = rSearch.readCursor(&quot;idx:user&quot;, cursorId, 2);</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 这里游标读取可以自己编写一个基于Lua 的实现</span></span><br><span class="line">    result = readCursor(<span class="string">&quot;idx:user&quot;</span>, cursorId, <span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    total = result.getTotal();</span><br><span class="line">    cursorId = result.getCursorId();</span><br><span class="line">    System.out.println(<span class="string">&quot;total = &quot;</span> + total);</span><br><span class="line">    System.out.println(<span class="string">&quot;cursorId = &quot;</span> + cursorId);</span><br><span class="line">    attributes = result.getAttributes();</span><br><span class="line">    <span class="keyword">for</span> (Map&lt;String, Object&gt; attribute : attributes) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;attribute = &quot;</span> + attribute);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// cursorId=0 或 到期 时会自动删除游标，此处无需删除</span></span><br><span class="line"><span class="comment">// rSearch.delCursor(&quot;idx:user&quot;, cursorId);</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> AggregationResult <span class="title function_">readCursor</span><span class="params">(String indexName, <span class="type">long</span> cursorId, <span class="type">int</span> count)</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">script</span> <span class="operator">=</span> <span class="string">&quot;return redis.call(&#x27;FT.CURSOR&#x27;, &#x27;READ&#x27;, KEYS[1], ARGV[1], &#x27;COUNT&#x27;, ARGV[2])&quot;</span>;</span><br><span class="line"></span><br><span class="line">    List&lt;Object&gt; results = stringRedisTemplate.execute(</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">DefaultRedisScript</span>&lt;&gt;(script, List.class),</span><br><span class="line">            Collections.singletonList(indexName),</span><br><span class="line">            String.valueOf(cursorId),</span><br><span class="line">            String.valueOf(count)</span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    cursorId = (<span class="type">long</span>) results.get(<span class="number">1</span>);</span><br><span class="line">    List&lt;Object&gt; lo = (List) results.get(<span class="number">0</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AggregationResult</span>(</span><br><span class="line">            (<span class="type">long</span>) lo.get(<span class="number">0</span>),</span><br><span class="line">            toListOfMap(lo),</span><br><span class="line">            cursorId</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> List&lt;Map&lt;String, Object&gt;&gt; <span class="title function_">toListOfMap</span><span class="params">(List&lt;Object&gt; lo)</span> &#123;</span><br><span class="line">    List&lt;Map&lt;String, Object&gt;&gt; result = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (lo == <span class="literal">null</span> || lo.size() &lt;= <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从索引 1 开始，跳过聚合总数</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; lo.size(); i++) &#123;</span><br><span class="line">        <span class="type">Object</span> <span class="variable">rowObj</span> <span class="operator">=</span> lo.get(i);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!(rowObj <span class="keyword">instanceof</span> List&lt;?&gt; row)) &#123;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        Map&lt;String, Object&gt; map = <span class="keyword">new</span> <span class="title class_">LinkedHashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; row.size() - <span class="number">1</span>; j += <span class="number">2</span>) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> String.valueOf(row.get(j));</span><br><span class="line">            <span class="type">Object</span> <span class="variable">value</span> <span class="operator">=</span> row.get(j + <span class="number">1</span>);</span><br><span class="line">            map.put(key, value);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        result.add(map);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>完整生命周期总结</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">FT.AGGREGATE ... WITHCURSOR      <span class="comment"># 创建游标（并返回第一批 + cursorId）</span></span><br><span class="line">        ↓</span><br><span class="line">FT.CURSOR READ                  <span class="comment"># 读取第 N 批</span></span><br><span class="line">        ↓</span><br><span class="line">FT.CURSOR READ                  <span class="comment"># 继续读取</span></span><br><span class="line">        ↓</span><br><span class="line">FT.CURSOR DEL（可选）           <span class="comment"># 提前释放资源</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>游标与 OFFSET/LIMIT 的区别</p></li></ul><table><thead><tr><th>对比项</th><th>游标</th><th>OFFSET / LIMIT</th></tr></thead><tbody><tr><td>性能</td><td>高（流式）</td><td>大 OFFSET 性能差</td></tr><tr><td>适合大结果集</td><td>是</td><td>否</td></tr><tr><td>服务器状态</td><td>有</td><td>无</td></tr><tr><td>适用命令</td><td>FT.AGGREGATE</td><td>FT.SEARCH</td></tr></tbody></table><h3 id="五、词典-同义词（搜索增强）">五、词典 &amp; 同义词（搜索增强）</h3><ul class="lvl-0"><li class="lvl-2"><p>词典（DICT）和同义词（SYNONYM）只对 TEXT / TAG 搜索生效</p></li><li class="lvl-2"><p>与 <code>FT.AGGREGATE</code> 无关，但与 <code>FT.SEARCH</code> 强相关。</p></li></ul><table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td><code>FT.DICTADD</code></td><td>添加词</td></tr><tr><td><code>FT.DICTDEL</code></td><td>删除词</td></tr><tr><td><code>FT.DICTDUMP</code></td><td>查看词典</td></tr><tr><td><code>FT.SYNUPDATE</code></td><td>更新同义词</td></tr><tr><td><code>FT.SYNDUMP</code></td><td>查看同义词</td></tr></tbody></table><h4 id="词典">词典</h4><ul class="lvl-0"><li class="lvl-2"><p>词典作用：控制“合法搜索词”，比如将词典的内容发送给用户，让用户只能搜索这些词。</p><ul class="lvl-2"><li class="lvl-4">⚠️ 词典本身不参与搜索匹配</li><li class="lvl-4">⚠️ 只是为搜索增强提供数据来源</li></ul></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 示例：添加商品相关词</span></span><br><span class="line">FT.DICTADD product_dict iphone mobile phone android huawei</span><br><span class="line"><span class="comment"># 查看词典内容（FT.DICTDUMP）</span></span><br><span class="line">FT.DICTDUMP product_dict</span><br><span class="line"><span class="comment"># 删除词典中的词（FT.DICTDEL）</span></span><br><span class="line">FT.DICTDEL product_dict iphone</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">rSearch.addDict(<span class="string">&quot;product_dict&quot;</span>, <span class="string">&quot;iphone&quot;</span>, <span class="string">&quot;mobile&quot;</span>,<span class="string">&quot;phone&quot;</span>,<span class="string">&quot;android&quot;</span>,<span class="string">&quot;huawei&quot;</span>);</span><br><span class="line">List&lt;String&gt; productDict = rSearch.dumpDict(<span class="string">&quot;product_dict&quot;</span>);</span><br><span class="line">rSearch.delDict(<span class="string">&quot;product_dict&quot;</span>, <span class="string">&quot;iphone&quot;</span>);</span><br></pre></td></tr></table></figure><h4 id="同义词">同义词</h4><ul class="lvl-0"><li class="lvl-2"><p>同义词的作用: 让不同的搜索词 命中同一批文档</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">例如：</span><br><span class="line">  搜 iphone</span><br><span class="line">  搜 mobile</span><br><span class="line">  搜 phone</span><br><span class="line"></span><br><span class="line">  👉 命中同一批订单</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>添加 / 更新同义词（FT.SYNUPDATE）</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 定义“手机”的同义词集合，注意同义词是绑定到 索引 的</span></span><br><span class="line">FT.SYNUPDATE idx:user</span><br><span class="line">  phone_group</span><br><span class="line">  iphone mobile phone</span><br><span class="line"><span class="comment">## 参数说明</span></span><br><span class="line"><span class="comment"># phone_group: 同义词集合名称（你自己定义）</span></span><br><span class="line"><span class="comment"># iphone mobile phone: 添加同义词，多个词用空格隔开</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看同义词集合</span></span><br><span class="line">FT.SYNDUMP idx:user</span><br><span class="line"><span class="comment">## 输出</span></span><br><span class="line">1) <span class="string">&quot;phone&quot;</span></span><br><span class="line">2) 1) <span class="string">&quot;phone_group&quot;</span></span><br><span class="line">3) <span class="string">&quot;iphone&quot;</span></span><br><span class="line">4) 1) <span class="string">&quot;phone_group&quot;</span></span><br><span class="line">5) <span class="string">&quot;mobile&quot;</span></span><br><span class="line">6) 1) <span class="string">&quot;phone_group&quot;</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">rSearch.updateSynonyms(<span class="string">&quot;idx:user&quot;</span>, <span class="string">&quot;phone_group&quot;</span>, <span class="string">&quot;iphone&quot;</span>, <span class="string">&quot;mobile&quot;</span>, <span class="string">&quot;phone&quot;</span>);</span><br><span class="line">Map&lt;String, List&lt;String&gt;&gt; synonyms = rSearch.dumpSynonyms(<span class="string">&quot;idx:user&quot;</span>);</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>此时我们再进行搜索，就会命中所有同义词</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">FT.SEARCH idx:user <span class="string">&#x27;phone&#x27;</span></span><br><span class="line"><span class="comment"># 搜索结果</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 4</span><br><span class="line">2) <span class="string">&quot;user:10001&quot;</span></span><br><span class="line">3) 1) <span class="string">&quot;$&quot;</span></span><br><span class="line">   2) <span class="string">&quot;&#123;\&quot;name\&quot;:\&quot;Alice Bob\&quot;,\&quot;age\&quot;:28,\&quot;vip\&quot;:\&quot;yes\&quot;,\&quot;orders\&quot;:[&#123;\&quot;amount\&quot;:199.99,\&quot;status\&quot;:\&quot;PAID\&quot;&#125;,&#123;\&quot;amount\&quot;:59.9,\&quot;status\&quot;:\&quot;CREATED\&quot;&#125;],\&quot;comment\&quot;:\&quot;I have a phone\&quot;,\&quot;items\&quot;:[\&quot;SpringCloud\xe6\x8a\x80\xe6\x9c\xaf\xe6\x8c\x87\xe5\x8d\x97\&quot;,\&quot;Shell\xe8\x84\x9a\xe6\x9c\xac\xe5\x9f\xba\xe7\xa1\x80\&quot;]&#125;&quot;</span></span><br><span class="line">4) <span class="string">&quot;user:10006&quot;</span></span><br><span class="line">5) 1) <span class="string">&quot;$&quot;</span></span><br><span class="line">   2) <span class="string">&quot;&#123;\&quot;name\&quot;:\&quot;Frank\&quot;,\&quot;age\&quot;:27,\&quot;vip\&quot;:\&quot;no\&quot;,\&quot;orders\&quot;:[&#123;\&quot;amount\&quot;:499,\&quot;status\&quot;:\&quot;PAID\&quot;&#125;,&#123;\&quot;amount\&quot;:129,\&quot;status\&quot;:\&quot;CREATED\&quot;&#125;],\&quot;comment\&quot;:\&quot;I have a phone\&quot;,\&quot;items\&quot;:[\&quot;Redis\xe5\xbc\x80\xe5\x8f\x91\xe5\xae\x9e\xe6\x88\x98\&quot;,\&quot;MongoDB\xe4\xbb\x8e\xe5\x85\xa5\xe9\x97\xa8\xe5\x88\xb0\xe5\xae\x9e\xe6\x88\x98\&quot;]&#125;&quot;</span></span><br><span class="line">6) <span class="string">&quot;user:10002&quot;</span></span><br><span class="line">7) 1) <span class="string">&quot;$&quot;</span></span><br><span class="line">   2) <span class="string">&quot;&#123;\&quot;name\&quot;:\&quot;Bob Frank\&quot;,\&quot;age\&quot;:35,\&quot;vip\&quot;:\&quot;no\&quot;,\&quot;orders\&quot;:[&#123;\&quot;amount\&quot;:899.0,\&quot;status\&quot;:\&quot;PAID\&quot;&#125;],\&quot;comment\&quot;:\&quot;I have a iphone\&quot;,\&quot;items\&quot;:[\&quot;Linux\xe5\xbf\x85\xe7\x9f\xa5\xe5\xbf\x85\xe4\xbc\x9a\&quot;,\&quot;Java\xe5\xa4\x9a\xe7\xba\xbf\xe7\xa8\x8b\xe8\xaf\xa6\xe8\xa7\xa3\&quot;]&#125;&quot;</span></span><br><span class="line">8) <span class="string">&quot;user:10003&quot;</span></span><br><span class="line">9) 1) <span class="string">&quot;$&quot;</span></span><br><span class="line">   2) <span class="string">&quot;&#123;\&quot;name\&quot;:\&quot;Carol\&quot;,\&quot;age\&quot;:22,\&quot;vip\&quot;:\&quot;no\&quot;,\&quot;orders\&quot;:[&#123;\&quot;amount\&quot;:19.9,\&quot;status\&quot;:\&quot;CANCELLED\&quot;&#125;],\&quot;comment\&quot;:\&quot;I have a mobile\&quot;,\&quot;items\&quot;:[\&quot;MySQL\xe4\xbb\x8e\xe5\x88\xa0\xe5\xba\x93\xe5\x88\xb0\xe8\xb7\x91\xe8\xb7\xaf\&quot;,\&quot;Oracle\xe5\xbc\x80\xe5\x8f\x91\xe5\xae\x9e\xe8\xb7\xb5\&quot;]&#125;&quot;</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>词典 + 同义词的组合使用</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">1. 用户输入: 手机</span><br><span class="line">2. 前端 / 服务端 → 从 product_dict 给提示</span><br><span class="line">3. 搜索请求 → FT.SEARCH</span><br><span class="line">4. RediSearch → 同义词扩展</span><br><span class="line">5. 返回统一结果</span><br></pre></td></tr></table></figure><h3 id="六、FT-SPELLCHECK（拼写纠错）">六、FT.SPELLCHECK（拼写纠错）</h3><ul class="lvl-0"><li class="lvl-2"><p>FT.SPELLCHECK 用于对用户输入的搜索词进行拼写纠错与相似词推荐</p></li><li class="lvl-2"><p>它并不会真正执行搜索，而是：<br>✅ 分析输入词是否存在于索引词典<br>✅ 如果不存在，基于编辑距离（Levenshtein）给出候选纠错词<br>✅ 支持自定义词典（Dictionary）增强效果</p></li><li class="lvl-2"><p>🎯 核心目标</p></li></ul><table><thead><tr><th>能力</th><th>说明</th></tr></thead><tbody><tr><td>拼写纠错</td><td>iphone → ipohne</td></tr><tr><td>模糊推荐</td><td>用户拼错关键词</td></tr><tr><td>搜索前置纠错</td><td>提升召回率</td></tr><tr><td>自动学习索引词</td><td>自动从索引词库构建词典</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>基本语法</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">FT.SPELLCHECK index query</span><br><span class="line">  [DISTANCE maxDistance]</span><br><span class="line">  [TERMS &#123;INCLUDE | EXCLUDE&#125; dictionary ...]</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>参数说明表</p></li></ul><table><thead><tr><th>参数</th><th>说明</th><th>默认值</th></tr></thead><tbody><tr><td>index</td><td>索引名称</td><td>必填</td></tr><tr><td>query</td><td>用户输入词</td><td>必填</td></tr><tr><td>DISTANCE</td><td>最大编辑距离（1~4）</td><td>1</td></tr><tr><td>TERMS INCLUDE</td><td>仅使用指定词典</td><td>全部</td></tr><tr><td>TERMS EXCLUDE</td><td>排除指定词典</td><td>无</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>示例</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">FT.SPELLCHECK idx:user <span class="string">&#x27;iphnoe&#x27;</span> DISTANCE 2</span><br><span class="line"><span class="comment"># 输出</span></span><br><span class="line">1) 1) TERM         <span class="comment"># 固定标识，表示这是一个被分析的词</span></span><br><span class="line">   2) <span class="string">&quot;iphnoe&quot;</span>     <span class="comment"># 用户输入词</span></span><br><span class="line">   3) 1) 1) <span class="string">&quot;0.4&quot;</span>  <span class="comment"># 相似度评分，越小越相似</span></span><br><span class="line">         2) <span class="string">&quot;iphone&quot;</span> <span class="comment"># 推荐词，建议替换词</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 搜索多个词</span></span><br><span class="line">FT.SPELLCHECK idx:user <span class="string">&#x27;iphnoe linu&#x27;</span> DISTANCE 2</span><br><span class="line">1) 1) TERM</span><br><span class="line">   2) <span class="string">&quot;iphnoe&quot;</span></span><br><span class="line">   3) 1) 1) <span class="string">&quot;0.4&quot;</span></span><br><span class="line">         2) <span class="string">&quot;iphone&quot;</span></span><br><span class="line">2) 1) TERM</span><br><span class="line">   2) <span class="string">&quot;linu&quot;</span></span><br><span class="line">   3) 1) 1) <span class="string">&quot;0.4&quot;</span></span><br><span class="line">         2) <span class="string">&quot;linux&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 增加编辑距离会显示更多的候选词</span></span><br><span class="line">FT.SPELLCHECK idx:user <span class="string">&#x27;phnoe&#x27;</span> DISTANCE 3</span><br><span class="line">1) 1) TERM</span><br><span class="line">   2) <span class="string">&quot;phnoe&quot;</span></span><br><span class="line">   3) 1) 1) <span class="string">&quot;4&quot;</span></span><br><span class="line">         2) <span class="string">&quot;have&quot;</span></span><br><span class="line">      2) 1) <span class="string">&quot;0.8&quot;</span></span><br><span class="line">         2) <span class="string">&quot;phone&quot;</span></span><br><span class="line">      3) 1) <span class="string">&quot;0.4&quot;</span></span><br><span class="line">         2) <span class="string">&quot;iphone&quot;</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Map&lt;String, Map&lt;String, Double&gt;&gt; spellcheck = rSearch.spellcheck(</span><br><span class="line">        <span class="string">&quot;idx:user&quot;</span>,</span><br><span class="line">        <span class="string">&quot;iphnoe linu&quot;</span>,</span><br><span class="line">        SpellcheckOptions.defaults().distance(<span class="number">2</span>));</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>score = 编辑距离 / max(len(用户输入词), len(候选词))，但并不是总是这样</p></li></ul><table><thead><tr><th>输入</th><th>候选</th><th>编辑距离</th><th>maxLen</th><th>score</th></tr></thead><tbody><tr><td>phnoe</td><td>iphone</td><td>2</td><td>6</td><td>2/6 = 0.333 ≈ 0.4</td></tr><tr><td>phnoe</td><td>phone</td><td>1</td><td>5</td><td>0.2 → ~0.8（内部权重可能反转或调整）</td></tr><tr><td>phnoe</td><td>have</td><td></td><td></td><td>“4” → 表示 phnoe 与 have 的编辑距离是 4</td></tr></tbody></table><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">phnoe → have</span><br><span class="line">p → h (1)</span><br><span class="line">h → a (2)</span><br><span class="line">n → v (3)</span><br><span class="line">o → e (4)</span><br><span class="line">e → (删除) (5)   （不同算法可能计为 4 或 5）</span><br><span class="line">该词明显是低质量候选，只是 DISTANCE=3/4 时被勉强收录。</span><br><span class="line"></span><br><span class="line">业务侧做合理过滤</span><br><span class="line">如果 score &gt;= 2     → 直接丢弃（明显噪声）</span><br><span class="line">如果 score &lt;= 1.0   → 可信候选</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>建议策略</p></li></ul><table><thead><tr><th>场景</th><th>建议</th></tr></thead><tbody><tr><td>搜索框实时提示</td><td>1</td></tr><tr><td>搜索提交后纠错</td><td>2</td></tr><tr><td>模糊容错系统</td><td>2~3</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>词典增强（Dictionary Integration）</p></li></ul><blockquote><p>通过 FT.DICTADD / FT.DICTDEL 人工维护一个或多个词典，把“业务词汇”主动注入到 SpellCheck 的候选词空间中。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">默认情况下：</span><br><span class="line">SpellCheck 只能从 索引倒排词典（Inverted Index Terms） 中生成候选词。</span><br><span class="line"></span><br><span class="line">如果某些词：</span><br><span class="line">  不在索引里</span><br><span class="line">  出现频率极低</span><br><span class="line">  属于专有名词</span><br><span class="line">  新词、品牌词</span><br><span class="line">那么 SpellCheck 很可能无法正确纠错。</span><br><span class="line"></span><br><span class="line">词典增强解决的正是这个问题。</span><br><span class="line"></span><br><span class="line">用户输入 → Tokenizer → 拆词 →</span><br><span class="line">  ↓</span><br><span class="line">候选源合并：</span><br><span class="line">  - 倒排索引词典</span><br><span class="line">  - 自定义词典（FT.DICTADD）</span><br><span class="line">  ↓</span><br><span class="line">编辑距离计算</span><br><span class="line">  ↓</span><br><span class="line">候选排序</span><br><span class="line">  ↓</span><br><span class="line">返回纠错词</span><br><span class="line"></span><br><span class="line">✅ SpellCheck 的候选空间被人为扩大。</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">FT.DICTADD dict:tech iphone ipad macbook airpods</span><br><span class="line"><span class="comment"># 使用指定词典纠错，白名单</span></span><br><span class="line">FT.SPELLCHECK idx:user <span class="string">&quot;macboo&quot;</span> TERMS INCLUDE dict:tech</span><br><span class="line">1) 1) TERM</span><br><span class="line">   2) <span class="string">&quot;macboo&quot;</span></span><br><span class="line">   3) 1) 1) <span class="string">&quot;0.4&quot;</span></span><br><span class="line">         2) <span class="string">&quot;macbook&quot;</span></span><br><span class="line"><span class="comment"># 排除词典，黑名单</span></span><br><span class="line">FT.SPELLCHECK idx:user <span class="string">&quot;iphnoe&quot;</span> TERMS EXCLUDE dict:noise</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Map&lt;String, Map&lt;String, Double&gt;&gt; spellcheck = rSearch.spellcheck(</span><br><span class="line">        <span class="string">&quot;idx:user&quot;</span>,</span><br><span class="line">        <span class="string">&quot;macboo&quot;</span>,</span><br><span class="line">        SpellcheckOptions.defaults()</span><br><span class="line">                .distance(<span class="number">2</span>)</span><br><span class="line">                .includedTerms(<span class="string">&quot;dict:tech&quot;</span>)</span><br><span class="line">);</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>FT.SPELLCHECK 与 Suggest 的差异对比（关键）</p></li></ul><table><thead><tr><th>维度</th><th>FT.SPELLCHECK</th><th>FT.SUGGET</th></tr></thead><tbody><tr><td>目标</td><td>拼写纠错</td><td>前缀联想</td></tr><tr><td>输入</td><td>完整词</td><td>前缀</td></tr><tr><td>返回</td><td>相似词</td><td>补全词</td></tr><tr><td>数据来源</td><td>索引词典</td><td>独立 Trie</td></tr><tr><td>是否排序</td><td>否（距离优先）</td><td>是（score）</td></tr><tr><td>是否模糊</td><td>编辑距离</td><td>前缀 + FUZZY</td></tr><tr><td>使用阶段</td><td>搜索前修正</td><td>输入过程中</td></tr></tbody></table><h3 id="已废弃命令（不建议使用）">已废弃命令（不建议使用）</h3><table><thead><tr><th>命令</th><th>状态</th><th>说明</th></tr></thead><tbody><tr><td><code>FT.CONFIG GET</code></td><td>Reids8 将其标记为 Deprecated</td><td>使用 <code>CONFIG GET search-*</code></td></tr><tr><td><code>FT.CONFIG SET</code></td><td>Reids8 将其标记为 Deprecated</td><td>同上</td></tr><tr><td><code>FT.TAGVALS</code></td><td>已经标记为 Deprecated</td><td>改用 AGGREGATE</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>⚠️ 这里要注意，只有 <code>Redis 8.0.0</code> 以上版本支持 <code>CONFIG GET search-*</code>。</p></li></ul><h4 id="FT-CONFIG-GET-和-CONFIG-GET-search-对应参数整理"><code>FT.CONFIG GET *</code> 和 <code>CONFIG GET search-*</code> 对应参数整理</h4><ul class="lvl-0"><li class="lvl-2"><p>1️⃣ 索引基本 / 文档表配置</p></li></ul><table><thead><tr><th>FT.CONFIG 参数</th><th>FT.CONFIG 值</th><th>CONFIG search-* 参数</th><th>CONFIG 值</th><th>说明</th></tr></thead><tbody><tr><td>MINPREFIX</td><td>2</td><td>search-min-prefix</td><td>2</td><td>自动补全最小前缀长度</td></tr><tr><td>MINSTEMLEN</td><td>4</td><td>search-min-stem-len</td><td>4</td><td>最小词干长度</td></tr><tr><td>MAXDOCTABLESIZE</td><td>1000000</td><td>search-max-doctablesize</td><td>1000000</td><td>最大文档表大小</td></tr><tr><td>MAXSEARCHRESULTS</td><td>1000000</td><td>search-max-search-results</td><td>1000000</td><td>搜索结果限制</td></tr><tr><td>MAXAGGREGATERESULTS</td><td>unlimited</td><td>search-max-aggregate-results</td><td>2147483648</td><td>聚合结果限制</td></tr><tr><td>MAXEXPANSIONS</td><td>200</td><td>search-max-prefix-expansions</td><td>200</td><td>前缀扩展限制</td></tr><tr><td>MAXPREFIXEXPANSIONS</td><td>200</td><td>search-max-prefix-expansions</td><td>200</td><td>前缀扩展限制（兼容）</td></tr><tr><td>RAW_DOCID_ENCODING</td><td>false</td><td>search-raw-docid-encoding</td><td>no</td><td>文档ID是否原始编码</td></tr><tr><td>UPGRADE_INDEX</td><td>Upgrade config for upgrading</td><td>N/A</td><td>N/A</td><td>升级索引配置提示</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>2️⃣ 查询 / 评分相关配置</p></li></ul><table><thead><tr><th>FT.CONFIG 参数</th><th>FT.CONFIG 值</th><th>CONFIG search-* 参数</th><th>CONFIG 值</th><th>说明</th></tr></thead><tbody><tr><td>TIMEOUT</td><td>500</td><td>search-timeout</td><td>500</td><td>查询超时（ms）</td></tr><tr><td>ON_TIMEOUT</td><td>return</td><td>search-on-timeout</td><td>return</td><td>超时策略</td></tr><tr><td>DEFAULT_SCORER</td><td>TFIDF</td><td>search-default-scorer</td><td>BM25STD</td><td>默认评分算法</td></tr><tr><td>BM25STD_TANH_FACTOR</td><td>4</td><td>search-bm25std-tanh-factor</td><td>4</td><td>BM25 调整因子</td></tr><tr><td>MULTI_TEXT_SLOP</td><td>100</td><td>search-multi-text-slop</td><td>100</td><td>多字段查询 slop</td></tr><tr><td>DEFAULT_DIALECT</td><td>1</td><td>search-default-dialect</td><td>1</td><td>查询方言</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>3️⃣ GC / 后台索引配置</p></li></ul><table><thead><tr><th>FT.CONFIG 参数</th><th>FT.CONFIG 值</th><th>CONFIG search-* 参数</th><th>CONFIG 值</th><th>说明</th></tr></thead><tbody><tr><td>NOGC</td><td>false</td><td>search-no-gc</td><td>no</td><td>禁用 GC</td></tr><tr><td>GC_POLICY</td><td>fork</td><td>search-fork-gc-policy (implicit)</td><td>fork</td><td>GC 策略</td></tr><tr><td>FORKGC_SLEEP_BEFORE_EXIT</td><td>0</td><td>search-fork-gc-sleep-before-exit</td><td>0</td><td>fork GC 退出前睡眠</td></tr><tr><td>FORK_GC_RUN_INTERVAL</td><td>30</td><td>search-fork-gc-run-interval</td><td>30</td><td>fork GC 执行间隔（秒）</td></tr><tr><td>FORK_GC_CLEAN_THRESHOLD</td><td>100</td><td>search-fork-gc-clean-threshold</td><td>100</td><td>fork GC 清理阈值</td></tr><tr><td>FORK_GC_RETRY_INTERVAL</td><td>5</td><td>search-fork-gc-retry-interval</td><td>5</td><td>fork GC 重试间隔</td></tr><tr><td>FORK_GC_CLEAN_NUMERIC_EMPTY_NODES</td><td>true</td><td>search-_numeric-compress</td><td>no</td><td>清理空数值节点</td></tr><tr><td>GCSCANSIZE</td><td>100</td><td>search-gc-scan-size</td><td>100</td><td>GC 每次扫描大小</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>4️⃣ 游标 / 线程 / 并发配置</p></li></ul><table><thead><tr><th>FT.CONFIG 参数</th><th>FT.CONFIG 值</th><th>CONFIG search-* 参数</th><th>CONFIG 值</th><th>说明</th></tr></thead><tbody><tr><td>CURSOR_MAX_IDLE</td><td>300000</td><td>search-cursor-max-idle</td><td>300000</td><td>游标最大空闲时间</td></tr><tr><td>INDEX_CURSOR_LIMIT</td><td>128</td><td>search-index-cursor-limit</td><td>128</td><td>单索引游标限制</td></tr><tr><td>UNION_ITERATOR_HEAP</td><td>20</td><td>search-union-iterator-heap</td><td>20</td><td>UNION 查询堆大小</td></tr><tr><td>NO_MEM_POOLS</td><td>false</td><td>search-no-mem-pools</td><td>no</td><td>禁用内存池</td></tr><tr><td>_FREE_RESOURCE_ON_THREAD</td><td>true</td><td>search-_free-resource-on-thread</td><td>yes</td><td>线程释放资源</td></tr><tr><td>search_min_operation_workers</td><td>N/A</td><td>search-min-operation-workers</td><td>4</td><td>最小操作线程数</td></tr><tr><td>search-workers</td><td>N/A</td><td>search-workers</td><td>0</td><td>工作线程数</td></tr><tr><td>search-workers-priority-bias-threshold</td><td>N/A</td><td>search-workers-priority-bias-threshold</td><td>1</td><td>线程优先级偏置</td></tr><tr><td>INDEXER_YIELD_EVERY_OPS</td><td>1000</td><td>search-indexer-yield-every-ops</td><td>1000</td><td>索引器 yield 每操作数</td></tr></tbody></table><ul class="lvl-0"><li class="lvl-2"><p>5️⃣ 实验 / 内部 /其他</p></li></ul><table><thead><tr><th>FT.CONFIG 参数</th><th>FT.CONFIG 值</th><th>CONFIG search-* 参数</th><th>CONFIG 值</th><th>说明</th></tr></thead><tbody><tr><td>EXTLOAD</td><td>nil</td><td>search-ext-load</td><td>“”</td><td>外部加载模块</td></tr><tr><td>FRISOINI</td><td>nil</td><td>search-friso-ini</td><td>“”</td><td>中文分词初始化</td></tr><tr><td>ENABLE_UNSTABLE_FEATURES</td><td>false</td><td>search-enable-unstable-features</td><td>no</td><td>启用实验特性</td></tr><tr><td>_PRINT_PROFILE_CLOCK</td><td>true</td><td>search-_print-profile-clock</td><td>yes</td><td>Profiling 开关</td></tr><tr><td>_NUMERIC_RANGES_PARENTS</td><td>0</td><td>search-_numeric-ranges-parents</td><td>0</td><td>数值范围父节点数</td></tr><tr><td>VSS_MAX_RESIZE</td><td>0</td><td>search-vss-max-resize</td><td>1024</td><td>向量索引最大重分配</td></tr><tr><td>PARTIAL_INDEXED_DOCS</td><td>false</td><td>search-partial-indexed-docs</td><td>no</td><td>部分索引文档标记</td></tr><tr><td>_PRIORITIZE_INTERSECT_UNION_CHILDREN</td><td>false</td><td>search-_prioritize-intersect-union-children</td><td>no</td><td>优先处理 INTERSECT/UNION 子节点</td></tr><tr><td>_BG_INDEX_MEM_PCT_THR</td><td>100</td><td>search-_bg-index-mem-pct-thr</td><td>100</td><td>后台索引内存阈值</td></tr><tr><td>_NUMERIC_COMPRESS</td><td>false</td><td>search-_numeric-compress</td><td>no</td><td>数值压缩</td></tr><tr><td>_BG_INDEX_OOM_PAUSE_TIME</td><td>0</td><td>search-_bg-index-oom-pause-time</td><td>0</td><td>后台索引 OOM 暂停时间</td></tr></tbody></table>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2026/01/05/redis7-module-RediSearch-study/</id>
    <link href="https://blog.hanqunfeng.com/2026/01/05/redis7-module-RediSearch-study/"/>
    <published>2026-01-05T13:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 Redis 扩展模块 – RediSearch 的使用方法</li>
<li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li>
<li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li>
<li class="lvl-2">RediSearch 的安装参见 <a href="/2025/12/26/redis7-module-RediSearch/" title="Redis 扩展模块 -- RediSearch 的安装方法">Redis 扩展模块 -- RediSearch 的安装方法</a></li>
<li class="lvl-2">示例代码：<a href="https://github.com/hanqunfeng/springbootchapter/tree/master/springboot3-demo/redis-demo/redisson-demo">GitHub</a></li>
</ul>]]>
    </summary>
    <title>RediSearch 开发实战</title>
    <updated>2026-01-07T07:30:53.062Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 Redis 扩展模块 – RediSearch 的安装方法</li><li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li><li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li></ul><span id="more"></span><h2 id="RediSearch-简介">RediSearch 简介</h2><ul class="lvl-0"><li class="lvl-2"><p><a href="https://github.com/RediSearch/RediSearch">RediSearch</a> 是 Redis 官方推出的 <code>全文搜索</code>与<code>二级索引</code>模块，用于在 Redis 之上提供类似<code>搜索引擎</code>的能力，解决“只能按 key 查、无法高效按字段查询”的问题。它是 Redis Stack 的核心组件之一，常用于搜索、过滤、排序和聚合场景。仅支持对 Hash 和 RedisJSON 类型进行索引。</p></li><li class="lvl-2"><p>该模块以 <code>Redis Module</code> 方式加载，可无缝集成到现有 Redis 实例中。</p></li><li class="lvl-2"><p>Redis8+，RediSearch 已经内置在 Redis 中，可以在安装redis同时安装全部 Stack 模块。</p></li><li class="lvl-2"><p>RediSearch 通过 <code>索引结构</code> + <code>查询语言</code>，提供：</p><ul class="lvl-2"><li class="lvl-4">全文搜索（Text Search）</li><li class="lvl-4">二级索引（按字段查询）</li><li class="lvl-4">多条件过滤 / 排序</li><li class="lvl-4">聚合分析（GROUP BY、COUNT、SUM 等）</li></ul></li><li class="lvl-2"><p>性能与设计特点</p><ul class="lvl-2"><li class="lvl-4">索引在内存中，查询延迟低（毫秒级）</li><li class="lvl-4">写入时同步更新索引（写入成本略高）</li><li class="lvl-4">适合 读多写少 / 查询复杂 的业务</li><li class="lvl-4">非事务型搜索引擎（不替代 ES，而是互补）</li></ul></li><li class="lvl-2"><p>与原生 Redis 的对比</p></li></ul><table><thead><tr><th>维度</th><th>Redis 原生</th><th>RediSearch</th></tr></thead><tbody><tr><td>查询方式</td><td>key 精确</td><td>多字段查询</td></tr><tr><td>全文搜索</td><td>不支持</td><td>支持</td></tr><tr><td>范围查询</td><td>有限</td><td>强</td></tr><tr><td>聚合统计</td><td>基本没有</td><td>类 SQL</td></tr><tr><td>性能</td><td>极快</td><td>近实时，内存换性能</td></tr></tbody></table><h2 id="RediSearch-vs-Elasticsearch">RediSearch vs Elasticsearch</h2><table><thead><tr><th>对比点</th><th>RediSearch</th><th>Elasticsearch</th></tr></thead><tbody><tr><td>部署复杂度</td><td>低</td><td>高</td></tr><tr><td>延迟</td><td>极低</td><td>较高</td></tr><tr><td>数据规模</td><td>中小规模</td><td>超大规模</td></tr><tr><td>实时性</td><td>强</td><td>近实时</td></tr><tr><td>场景</td><td>业务内搜索</td><td>搜索平台</td></tr></tbody></table><h2 id="安装-RediSearch">安装 RediSearch</h2><ul class="lvl-0"><li class="lvl-2"><p>最简单的方式就是从<a href="https://cloud.redis.io">Redis Cloud</a>的<code>Download Center</code>中进行下载，其提供了所有Redis模块编译后的<code>.so</code>文件，可以优先进行尝试，但是并不保证一定兼容，所以最稳妥的方式是通过源码自己编译。<br><img src="https://upic-oss.oss-cn-beijing.aliyuncs.com/uPic/rguEIA.png" alt=""></p></li><li class="lvl-2"><p>源码编译</p></li></ul><blockquote><p>安装时需要科学上网，主要是安装依赖时需要从海外网下载，如果要部署在国内服务器，可能会连接失败。<br>可以在海外的<code>相同配置</code>的服务器上进行编译，之后将编译好的<code>redisearch.so</code>上传到国内服务器即可。</p></blockquote><ul class="lvl-0"><li class="lvl-2"><p>安装依赖</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> dnf install -y \</span><br><span class="line">  gcc \</span><br><span class="line">  gcc-c++ \</span><br><span class="line">  make \</span><br><span class="line">  cmake \</span><br><span class="line">  autoconf \</span><br><span class="line">  automake \</span><br><span class="line">  libtool \</span><br><span class="line">  pkgconfig \</span><br><span class="line">  openssl-devel \</span><br><span class="line">  libstdc++ \</span><br><span class="line">  libstdc++-devel</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>RediSearch 最新版本 依赖 CMake 3.25+，dnf源中的版本较低，所以这里需要手动安装</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">CMAKE_VERSION=3.25.1</span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> /usr/local/src</span><br><span class="line">wget https://github.com/Kitware/CMake/releases/download/v<span class="variable">$&#123;CMAKE_VERSION&#125;</span>/cmake-<span class="variable">$&#123;CMAKE_VERSION&#125;</span>.tar.gz</span><br><span class="line">tar -xf cmake-<span class="variable">$&#123;CMAKE_VERSION&#125;</span>.tar.gz</span><br><span class="line"><span class="built_in">cd</span> cmake-<span class="variable">$&#123;CMAKE_VERSION&#125;</span></span><br><span class="line"></span><br><span class="line">./bootstrap --prefix=/usr/local</span><br><span class="line">make -j$(<span class="built_in">nproc</span>)</span><br><span class="line"><span class="built_in">sudo</span> make install</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证版本 /usr/local/bin/cmake</span></span><br><span class="line">/usr/local/bin/cmake --version</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>编译RediSearch</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p /usr/local/soft/modules/</span><br><span class="line"><span class="built_in">cd</span> /usr/local/soft/modules</span><br><span class="line"><span class="comment"># clone 代码，这里 --recursive 是为了拉取子模块</span></span><br><span class="line">git <span class="built_in">clone</span> --recursive https://github.com/RediSearch/RediSearch.git</span><br><span class="line"><span class="built_in">cd</span> RediSearch</span><br><span class="line"><span class="comment"># 推荐切换到稳定的release版本</span></span><br><span class="line">git checkout v2.10.25</span><br><span class="line"><span class="comment"># 更新子模块，非必须，如果上面 clone 时没有加上 --recursive ，这个步骤就不能省略</span></span><br><span class="line">git submodule update --init --recursive</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建 build_dir 目录，编译失败要先删除该目录在重新创建</span></span><br><span class="line"><span class="built_in">mkdir</span> build_dir &amp;&amp; <span class="built_in">cd</span> build_dir</span><br><span class="line"><span class="comment"># 生成 Makefile</span></span><br><span class="line">/usr/local/bin/cmake .. \</span><br><span class="line">  -DCMAKE_BUILD_TYPE=Release \</span><br><span class="line">  -DCMAKE_C_COMPILER=gcc \</span><br><span class="line">  -DCMAKE_CXX_COMPILER=g++</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译 RediSearch</span></span><br><span class="line">make -j$(<span class="built_in">nproc</span>)</span><br><span class="line"><span class="comment"># 编译过程会报错，同样在 Rocky9 上也会遇到这个问题</span></span><br><span class="line">[100%] Linking CXX shared library redisearch.so</span><br><span class="line">/usr/bin/ld: cannot find -lstdc++: No such file or directory</span><br><span class="line">collect2: error: ld returned 1 <span class="built_in">exit</span> status</span><br><span class="line">make[2]: *** [CMakeFiles/redisearch.dir/build.make:557: redisearch.so] Error 1</span><br><span class="line">make[1]: *** [CMakeFiles/Makefile2:2958: CMakeFiles/redisearch.dir/all] Error 2</span><br><span class="line">make: *** [Makefile:136: all] Error 2</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>编译错误原因分析</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">RediSearch 2.10.x 在链接 redisearch.so 时 强制 使用 -static-libstdc++，而 libstdc++.a 静态文件不存在，导致编译失败</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>解决方法是安装静态库</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> dnf install -y libstdc++-static</span><br><span class="line"><span class="comment"># 验证静态文件是否存在</span></span><br><span class="line"><span class="built_in">ls</span> /usr/lib/gcc/x86_64-amazon-linux/*/libstdc++.a</span><br><span class="line"><span class="comment"># 输出</span></span><br><span class="line">/usr/lib/gcc/x86_64-amazon-linux/11/libstdc++.a</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>安装后重新编译RediSearch</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /usr/local/soft/modules</span><br><span class="line"><span class="comment"># 删除 build_dir 目录</span></span><br><span class="line"><span class="built_in">rm</span> -rf build_dir</span><br><span class="line"><span class="comment"># 创建 build_dir 目录，编译失败要先删除该目录再重新创建</span></span><br><span class="line"><span class="built_in">mkdir</span> build_dir &amp;&amp; <span class="built_in">cd</span> build_dir</span><br><span class="line"><span class="comment"># 生成 Makefile</span></span><br><span class="line">/usr/local/bin/cmake .. \</span><br><span class="line">  -DCMAKE_BUILD_TYPE=Release \</span><br><span class="line">  -DCMAKE_C_COMPILER=gcc \</span><br><span class="line">  -DCMAKE_CXX_COMPILER=g++</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译 RediSearch，如果没有错误，则说明编译成功，输出: build_dir/redisearch.so</span></span><br><span class="line">make -j$(<span class="built_in">nproc</span>)</span><br></pre></td></tr></table></figure><h2 id="Redis-启用模块">Redis 启用模块</h2><ul class="lvl-0"><li class="lvl-2"><p>将生成的 <code>redisearch.so</code> 拷贝到 redis 的 modules 目录下（非必须），目录不存在则创建</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 注意 .so 文件需要包含可执行权限</span></span><br><span class="line"><span class="built_in">cp</span> build_dir/redisearch.so /usr/local/soft/redis-7.4.7/modules/redisearch.so</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>本文采用 <code>loadmodule</code> 加载模块</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 将 redisearch.so 添加到 redis.conf 中，需要重启 redis</span></span><br><span class="line">loadmodule /usr/local/soft/redis-7.4.7/modules/redisearch.so</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动redis</span></span><br><span class="line">redis-server redis.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># 登录测试</span></span><br><span class="line">redis-cli --user admin --pass 123456</span><br><span class="line"><span class="comment"># 查看模块</span></span><br><span class="line">127.0.0.1:6379&gt; MODULE LIST</span><br><span class="line"><span class="comment"># 输出</span></span><br><span class="line">1) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;ReJSON&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 20816</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/local/soft/redis-7.4.7/modules/rejson.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">2) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;bf&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 20817</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/local/soft/redis-7.4.7/modules/redisbloom.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">3) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;search&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 21025</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/local/soft/redis-7.4.7/modules/redisearch.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br></pre></td></tr></table></figure><h2 id="Rocky9-编译安装-RediSearch">Rocky9 编译安装 RediSearch</h2><blockquote><p>系统版本：<code>Rocky Linux release 9.4 (Blue Onyx)</code></p></blockquote><ul class="lvl-0"><li class="lvl-2"><p>安装依赖</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> dnf install -y \</span><br><span class="line">  gcc \</span><br><span class="line">  gcc-c++ \</span><br><span class="line">  make \</span><br><span class="line">  cmake \</span><br><span class="line">  autoconf \</span><br><span class="line">  automake \</span><br><span class="line">  libtool \</span><br><span class="line">  pkgconfig \</span><br><span class="line">  openssl-devel \</span><br><span class="line">  libstdc++ \</span><br><span class="line">  libstdc++-devel</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>启用 CRB</p></li></ul><blockquote><p>在 Rocky Linux 9 中，<code>libstdc++-static</code> 不在默认仓库里，它位于 CRB（CodeReady Builder）仓库，默认是 关闭的，所以我们需要先启用它</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 如果你系统里没有 config-manager，需要先先装</span></span><br><span class="line"><span class="built_in">sudo</span> dnf install -y dnf-plugins-core</span><br><span class="line"><span class="comment"># 启用 CRB</span></span><br><span class="line"><span class="built_in">sudo</span> dnf config-manager --set-enabled crb</span><br><span class="line"><span class="comment"># 刷新缓存</span></span><br><span class="line"><span class="built_in">sudo</span> dnf makecache</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>安装静态库，解决<code>/usr/bin/ld: cannot find -lstdc++: No such file or directory</code>的问题</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> dnf install -y libstdc++-static</span><br><span class="line"><span class="comment"># 验证静态文件是否存在</span></span><br><span class="line"><span class="built_in">ls</span> /usr/lib/gcc/x86_64-redhat-linux/*/libstdc++.a</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>编译RediSearch</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p /usr/local/soft/modules/</span><br><span class="line"><span class="built_in">cd</span> /usr/local/soft/modules</span><br><span class="line"><span class="comment"># clone 代码，这里 --recursive 是为了拉取子模块</span></span><br><span class="line">git <span class="built_in">clone</span> --recursive https://github.com/RediSearch/RediSearch.git</span><br><span class="line"><span class="built_in">cd</span> RediSearch</span><br><span class="line"><span class="comment"># 推荐切换到稳定的release版本</span></span><br><span class="line">git checkout v2.10.25</span><br><span class="line"><span class="comment"># 更新子模块，非必须，如果上面 clone 时没有加上 --recursive ，这个步骤就不能省略</span></span><br><span class="line">git submodule update --init --recursive</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建 build_dir 目录，编译失败要先删除该目录再重新创建</span></span><br><span class="line"><span class="built_in">mkdir</span> build_dir &amp;&amp; <span class="built_in">cd</span> build_dir</span><br><span class="line"><span class="comment"># 生成 Makefile</span></span><br><span class="line">cmake .. \</span><br><span class="line">  -DCMAKE_BUILD_TYPE=Release \</span><br><span class="line">  -DCMAKE_C_COMPILER=gcc \</span><br><span class="line">  -DCMAKE_CXX_COMPILER=g++</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译 RediSearch，如果没有错误，则说明编译成功，输出: build_dir/redisearch.so</span></span><br><span class="line">make -j$(<span class="built_in">nproc</span>)</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2025/12/26/redis7-module-RediSearch/</id>
    <link href="https://blog.hanqunfeng.com/2025/12/26/redis7-module-RediSearch/"/>
    <published>2025-12-26T13:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 Redis 扩展模块 – RediSearch 的安装方法</li>
<li class="lvl-2">本文基于<code>redis-7.4.7</code>，<code>springboot-3.5.8</code></li>
<li class="lvl-2">操作系统：<code>Amazon Linux 2023(内核 6.1)</code></li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2">Redis 命令文档：<a href="https://redis.io/docs/latest/commands/">https://redis.io/docs/latest/commands/</a></li>
</ul>]]>
    </summary>
    <title>Redis 扩展模块 -- RediSearch 的安装方法</title>
    <updated>2026-01-07T07:22:09.326Z</updated>
  </entry>
  <entry>
    <author>
      <name>飘逸峰</name>
    </author>
    <category term="技术" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/categories/%E6%8A%80%E6%9C%AF/redis/"/>
    <category term="redis" scheme="https://blog.hanqunfeng.com/tags/redis/"/>
    <content>
      <![CDATA[<h2 id="摘要">摘要</h2><ul class="lvl-0"><li class="lvl-2">本文介绍 Linux 安装 Redis8 的方法</li><li class="lvl-2">Linux 版本：<code>Amazon Linux 2023(内核 6.1)</code>。</li><li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li><li class="lvl-2"><a href="https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/rpm/">Redis官网安装手册</a></li></ul><span id="more"></span><h2 id="Yum-安装">Yum 安装</h2><blockquote><p>最省事，国内环境也可以顺利完成安装。<br><code>Amazon Linux 2023(内核 6.1)</code> 和 <code>Rocky Linux release 9.4 (Blue Onyx)</code> 均成功安装。</p></blockquote><ul class="lvl-0"><li class="lvl-2"><p>1.添加仓库</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">tee</span> /etc/yum.repos.d/redis.repo &gt; /dev/null &lt;&lt;<span class="string">EOF</span></span><br><span class="line"><span class="string">[Redis]</span></span><br><span class="line"><span class="string">name=Redis</span></span><br><span class="line"><span class="string">baseurl=http://packages.redis.io/rpm/rockylinux9</span></span><br><span class="line"><span class="string">enabled=1</span></span><br><span class="line"><span class="string">gpgcheck=1</span></span><br><span class="line"><span class="string">EOF</span></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>2.安装 Redis</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">curl -fsSL https://packages.redis.io/gpg &gt; /tmp/redis.key</span><br><span class="line"><span class="built_in">sudo</span> rpm --import /tmp/redis.key</span><br><span class="line"><span class="built_in">sudo</span> yum install redis -y</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>3.查看redis安装信息</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看已安装的redis版本</span></span><br><span class="line">$ rpm -qa | grep redis</span><br><span class="line">redis-8.4.0-1.x86_64</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看redis安装信息</span></span><br><span class="line">$ rpm -qi redis-8.4.0-1.x86_64</span><br><span class="line">Name        : redis</span><br><span class="line">Version     : 8.4.0</span><br><span class="line">Release     : 1</span><br><span class="line">Architecture: x86_64</span><br><span class="line">Install Date: Wed 24 Dec 2025 06:05:22 AM UTC</span><br><span class="line">Group       : Applications/Databases</span><br><span class="line">Size        : 83034075</span><br><span class="line">License     :</span><br><span class="line">Signature   : RSA/SHA512, Mon 01 Dec 2025 12:05:10 PM UTC, Key ID 5f4349d6bf53aa0c</span><br><span class="line">Source RPM  : redis-8.4.0-1.src.rpm</span><br><span class="line">Build Date  : Tue 18 Nov 2025 04:41:58 PM UTC</span><br><span class="line">Build Host  : 331d5099e900</span><br><span class="line">Packager    : Redis Labs &lt;redis@redis.io&gt;</span><br><span class="line">URL         : https://redis.io/</span><br><span class="line">Summary     : Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported: Strings, Lists, Sets, Sorted Sets, Hashes, Streams, HyperLogLogs, Bitmaps.</span><br><span class="line">Description :</span><br><span class="line">Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported: Strings, Lists, Sets, Sorted Sets, Hashes, Streams, HyperLogLogs, Bitmaps.</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看redis安装的文件</span></span><br><span class="line">$ rpm -ql redis-8.4.0-1.x86_64</span><br><span class="line">/etc/redis</span><br><span class="line">/etc/redis/redis.conf</span><br><span class="line">/etc/redis/sentinel</span><br><span class="line">/etc/redis/sentinel/sentinel.conf</span><br><span class="line">/run/redis</span><br><span class="line">/run/redis/redis-server.pid</span><br><span class="line">/usr/bin/redis-benchmark</span><br><span class="line">/usr/bin/redis-check-aof</span><br><span class="line">/usr/bin/redis-check-rdb</span><br><span class="line">/usr/bin/redis-cli</span><br><span class="line">/usr/bin/redis-sentinel</span><br><span class="line">/usr/bin/redis-server</span><br><span class="line">/usr/lib/redis</span><br><span class="line">/usr/lib/redis/modules</span><br><span class="line">/usr/lib/redis/modules/redisbloom.so</span><br><span class="line">/usr/lib/redis/modules/redisearch.so</span><br><span class="line">/usr/lib/redis/modules/redistimeseries.so</span><br><span class="line">/usr/lib/redis/modules/rejson.so</span><br><span class="line">/usr/lib/redis/redisbloom.so</span><br><span class="line">/usr/lib/redis/redisearch.so</span><br><span class="line">/usr/lib/redis/redistimeseries.so</span><br><span class="line">/usr/lib/redis/rejson.so</span><br><span class="line">/usr/lib/systemd/system/redis-sentinel.service</span><br><span class="line">/usr/lib/systemd/system/redis.service</span><br><span class="line">/usr/share/selinux/packages/redis-ce.fc</span><br><span class="line">/usr/share/selinux/packages/redis-ce.te</span><br><span class="line">/var/lib/redis</span><br><span class="line">/var/log/redis</span><br><span class="line">/var/log/redis/redis-sentinel.log</span><br><span class="line">/var/log/redis/redis-server.log</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>4.启动redis</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> redis</span><br><span class="line"><span class="built_in">sudo</span> systemctl start redis</span><br><span class="line"><span class="built_in">sudo</span> systemctl status redis</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>5.登录redis</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">$ redis-cli</span><br><span class="line">127.0.0.1:6379&gt; MODULE LIST</span><br><span class="line">1) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;bf&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 80400</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/lib/redis/modules/redisbloom.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">2) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;timeseries&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 80400</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/lib/redis/modules/redistimeseries.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">3) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;ReJSON&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 80400</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/lib/redis/modules/rejson.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">4) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;vectorset&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 1</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br><span class="line">5) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;search&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 80402</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;/usr/lib/redis/modules/redisearch.so&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br></pre></td></tr></table></figure><h2 id="源码安装">源码安装</h2><ul class="lvl-0"><li class="lvl-2"><p>1.更新环境(非必须)</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> dnf update -y</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>2.安装依赖包</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> dnf install -y --nobest --skip-broken \</span><br><span class="line">    pkg-config \</span><br><span class="line">    xz \</span><br><span class="line">    wget \</span><br><span class="line">    <span class="built_in">which</span> \</span><br><span class="line">    git \</span><br><span class="line">    make \</span><br><span class="line">    openssl \</span><br><span class="line">    openssl-devel \</span><br><span class="line">    python3 \</span><br><span class="line">    python3-pip \</span><br><span class="line">    python3-devel \</span><br><span class="line">    unzip \</span><br><span class="line">    rsync \</span><br><span class="line">    clang \</span><br><span class="line">    llvm \</span><br><span class="line">    curl \</span><br><span class="line">    libtool \</span><br><span class="line">    automake \</span><br><span class="line">    autoconf \</span><br><span class="line">    jq \</span><br><span class="line">    systemd-devel</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>3.下载并提取Redis源代码</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /usr/src</span><br><span class="line"><span class="built_in">sudo</span> wget -O redis-8.4.0.tar.gz https://github.com/redis/redis/archive/refs/tags/8.4.0.tar.gz</span><br><span class="line"><span class="built_in">sudo</span> tar -xzf redis-8.4.0.tar.gz</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>4.编译Redis</p></li></ul><blockquote><p>这里要注意，不会自动安装模块，需要手动安装。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 启用GCC工具集</span></span><br><span class="line"><span class="built_in">cd</span> /usr/src/redis-8.4.0</span><br><span class="line"><span class="comment"># 构建TLS</span></span><br><span class="line"><span class="built_in">export</span> BUILD_TLS=<span class="built_in">yes</span></span><br><span class="line"><span class="comment"># 构建Redis模块，配不配都不会自动安装模块</span></span><br><span class="line"><span class="built_in">export</span> BUILD_WITH_MODULES=<span class="built_in">yes</span></span><br><span class="line"><span class="comment"># 安装Rust工具链</span></span><br><span class="line"><span class="built_in">export</span> INSTALL_RUST_TOOLCHAIN=<span class="built_in">yes</span></span><br><span class="line"><span class="comment"># 关闭警告</span></span><br><span class="line"><span class="built_in">export</span> DISABLE_WERRORS=<span class="built_in">yes</span></span><br><span class="line"><span class="comment"># 开始编译</span></span><br><span class="line"><span class="built_in">sudo</span> make -j <span class="string">&quot;<span class="subst">$(nproc)</span>&quot;</span> all</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证</span></span><br><span class="line">./src/redis-server --version</span><br><span class="line">./src/redis-cli --version</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>5.启动Redis</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> ./src/redis-server redis.conf</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>6.登录Redis</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">$ ./src/redis-cli</span><br><span class="line">127.0.0.1:6379&gt; MODULE LIST</span><br><span class="line">1) 1) <span class="string">&quot;name&quot;</span></span><br><span class="line">   2) <span class="string">&quot;vectorset&quot;</span></span><br><span class="line">   3) <span class="string">&quot;ver&quot;</span></span><br><span class="line">   4) (<span class="built_in">integer</span>) 1</span><br><span class="line">   5) <span class="string">&quot;path&quot;</span></span><br><span class="line">   6) <span class="string">&quot;&quot;</span></span><br><span class="line">   7) <span class="string">&quot;args&quot;</span></span><br><span class="line">   8) (empty array)</span><br></pre></td></tr></table></figure><ul class="lvl-0"><li class="lvl-2"><p>7.安装模块，可以参考如下文章中的介绍</p><ul class="lvl-2"><li class="lvl-5"><a href="/2025/12/24/redis7-module-RedisJSON/" title="Redis 扩展模块 -- RedisJSON 的安装方法">Redis 扩展模块 -- RedisJSON 的安装方法</a></li><li class="lvl-5"><a href="/2025/12/21/redis7-module-RedisBloom/" title="Redis 扩展模块 -- RedisBloom 的安装方法">Redis 扩展模块 -- RedisBloom 的安装方法</a></li><li class="lvl-5"><a href="/2025/12/26/redis7-module-RediSearch/" title="Redis 扩展模块 -- RediSearch 的安装方法">Redis 扩展模块 -- RediSearch 的安装方法</a></li><li class="lvl-5"><a href="/2026/01/07/redis7-module-RedisTimeSeries/" title="Redis 扩展模块 -- RedisTimeSeries 的安装方法">Redis 扩展模块 -- RedisTimeSeries 的安装方法</a></li></ul></li></ul>]]>
    </content>
    <id>https://blog.hanqunfeng.com/2025/12/25/redis8-install-amazon-linux-2023/</id>
    <link href="https://blog.hanqunfeng.com/2025/12/25/redis8-install-amazon-linux-2023/"/>
    <published>2025-12-25T13:30:05.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘要">摘要</h2>
<ul class="lvl-0">
<li class="lvl-2">本文介绍 Linux 安装 Redis8 的方法</li>
<li class="lvl-2">Linux 版本：<code>Amazon Linux 2023(内核 6.1)</code>。</li>
<li class="lvl-2">Redis官网：<a href="https://redis.io/">https://redis.io/</a></li>
<li class="lvl-2"><a href="https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/rpm/">Redis官网安装手册</a></li>
</ul>]]>
    </summary>
    <title>Amazon Linux 2023(内核 6.1) 安装 Redis8</title>
    <updated>2026-01-07T10:32:31.441Z</updated>
  </entry>
</feed>
