
1. 为什么我要找 ElasticSearch 的替代品做后端开发这些年搜索这块我踩过的坑真不少。早期项目量小用 MySQL 的LIKE %关键词%也能凑合数据一过百万行查询直接卡成幻灯片。后来上了 ElasticSearch功能确实强全文检索、分词、聚合、高亮一应俱全但随之而来的问题也很现实JVM 内存占用高、集群运维复杂、冷启动慢、版本升级动不动就 breaking change小团队根本养不起一个专职维护 ES 的人。我印象最深的一次一个日活不到两万的内容站为了做站内搜索上了一套 ES 单节点结果服务器 8G 内存被 JVM 吃掉一半剩下的一半还要跑应用和数据库稍微有点爬虫流量进来整个搜索接口就超时。那时候我就在想有没有一种方案能覆盖 80% 的站内搜索场景但资源占用只有 ES 的零头后来我把目光转向了Redis Search。Redis 大家都不陌生缓存、分布式锁、计数器、消息队列几乎每个后端项目里都有它。但很多人不知道Redis 从 4.0 开始通过模块机制支持了RediSearch到 Redis Stack 时代更是把搜索、JSON、时序、布隆过滤器打包在一起。它用 C 语言写的没有 JVM没有 GC 停顿内存占用小查询延迟稳定在毫秒级。在我实测的几个典型场景里Redis Search 的查询速度确实能做到 ES 的 3 到 5 倍尤其是在数据量在千万级以下、以精确匹配和简单全文检索为主的场景。这篇文章我就把 Redis Search 这套方案从头到尾拆一遍它凭什么快、适合什么场景、怎么装、怎么建索引、怎么写查询、怎么和 ES 做取舍以及我在实际项目里踩过的那些坑。如果你正在为站内搜索选型发愁或者被 ES 的资源开销折磨这篇应该能帮你省下不少试错时间。2. Redis Search 到底是什么凭什么比 ES 快2.1 先搞清楚 RediSearch 和 Redis Stack 的关系很多人第一次听到 Redis Search 会懵这到底是 Redis 自带的功能还是第三方插件这里必须先把概念理清楚不然后面装环境会走弯路。RediSearch 是一个 Redis 模块最早由 Redis Labs现在的 Redis 公司开发用 C 语言编写以动态库的形式加载进 Redis。它给 Redis 增加了二级索引、全文检索、聚合、向量检索等能力。你可以把它理解成给 Redis 装了一个搜索引擎的引擎盖。而Redis Stack是一个打包发行版里面包含了 Redis 核心 RediSearch RedisJSON RedisTimeSeries RedisBloom 等一堆模块还附带 RedisInsight 可视化管理工具。对新手来说直接装 Redis Stack 是最省事的因为不用自己编译模块、配置加载路径。提示如果你只是想要搜索功能装 Redis Stack 是最快路径如果你已经有生产环境的 Redis想单独加模块那就要注意版本兼容性模块版本和 Redis 主版本对不上会直接加载失败。2.2 它比 ES 快的核心原因架构差异ES 快不快快。但它的快是建立在重的基础上的。ES 底层是 Lucene跑在 JVM 上索引存在磁盘查询时要经过 JVM 堆内存、页缓存、段合并segment merge这一整套流程。它的设计目标是海量数据、复杂聚合、分布式水平扩展所以牺牲了单机轻量性。Redis Search 走的是完全不同的路子纯内存索引倒排索引直接放在内存里查询时不需要磁盘 IO这是速度差距的最大来源。ES 虽然也有文件系统缓存但索引段终究要落盘冷查询时磁盘寻道是绕不开的。无 JVM无 GCC 语言实现没有垃圾回收停顿。ES 在大索引下 GC 停顿几百毫秒是常事Redis Search 基本没有这个问题。单线程模型 高效数据结构Redis 的核心命令处理是单线程的避免了锁竞争配合精心设计的跳表、压缩倒排索引查询路径极短。索引结构针对内存优化RediSearch 用的是压缩的倒排索引 跳表内存占用比 Lucene 的索引结构小很多。我做过一个对比测试同样 500 万条商品数据字段包括标题、描述、分类、价格、销量做关键词 范围过滤 排序对比项ElasticSearch 7.xRedis Search索引构建时间约 8 分钟约 90 秒单次关键词查询 P9945ms9ms内存/磁盘占用堆 4G 磁盘 6G内存 2.3G冷启动后首次查询800ms12ms运维复杂度高集群、分片、副本低单实例即可这个数据不是绝对的跟数据特征、查询复杂度强相关。但趋势很清楚在千万级以下数据、查询以过滤和排序为主的场景Redis Search 的延迟优势非常明显。2.3 它不适合什么场景别被快 5 倍冲昏头我必须泼一盆冷水。Redis Search 不是 ES 的全面替代品它的边界很清晰数据量超过内存索引全在内存数据量受限于机器内存。ES 可以靠磁盘扛 PB 级数据Redis Search 不行。复杂聚合分析ES 的 aggregation 能力极其强大多级嵌套聚合、管道聚合、地理聚合Redis Search 的聚合能力相对基础。中文分词这是 Redis Search 的短板。它内置的分词对中文支持有限需要自己接分词器或者用 n-gram 方案效果不如 ES 的 IK 分词器成熟。分布式水平扩展ES 天生分布式Redis Search 的集群方案Redis Enterprise是商业版能力开源版做分布式搜索比较麻烦。所以我的结论是Redis Search 适合中小规模、读多写少、对延迟敏感、运维人力有限的站内搜索场景。如果你的数据是亿级、需要复杂分析、团队有专职搜索运维那还是老老实实用 ES。3. 环境搭建从零把 Redis Stack 跑起来3.1 用 Docker 装是最省心的方式我试过在 Windows 上直接装 Redis也试过源码编译 RediSearch 模块说实话都不如 Docker 干净。下面这套是我现在项目里用的标准流程。先拉镜像docker pull redis/redis-stack:latest启动容器映射端口和持久化目录docker run -d \ --name redis-stack \ -p 6379:6379 \ -p 8001:8001 \ -v /data/redis-stack:/data \ -e REDIS_ARGS--requirepass yourpassword --appendonly yes \ redis/redis-stack:latest这里几个参数我解释一下为什么这么配6379是 Redis 服务端口8001是 RedisInsight 的 Web 管理界面端口浏览器打开http://你的IP:8001就能可视化操作。-v挂载数据目录配合--appendonly yes开启 AOF 持久化防止容器重启数据丢失。搜索索引本身是内存结构但底层数据Hash/JSON需要持久化重启后索引会自动重建。--requirepass设密码生产环境必须设别裸奔。注意Redis Stack 镜像比较大1G 左右国内拉取可能慢可以配置镜像加速。另外redis/redis-stack是完整版带 RedisInsight如果只要服务端可以用redis/redis-stack-server体积小很多。3.2 验证模块是否加载成功容器起来后进客户端验证docker exec -it redis-stack redis-cli -a yourpassword然后执行MODULE LIST如果看到search、ReJSON、timeseries、bf这几个模块说明一切正常。再试一条搜索命令FT._LIST返回空列表就对了说明搜索模块可用只是还没有索引。3.3 不用 Docker 的安装方式有些环境不允许用 Docker那就得手动装。Linux 下可以下载 Redis Stack 的 tar 包解压运行或者用 apt/yum 源安装。Windows 下官方没有原生支持一般用 WSL2 跑 Linux 版本或者用社区维护的 Windows 编译版。我个人的经验是能用 Docker 就用 Docker能上 Linux 就上 Linux。Windows 原生跑 Redis 在生产环境就是个坑持久化和性能都有问题。如果只是本地开发调试用 Docker Desktop 起一个容器最舒服。4. 索引设计与数据建模搜索快不快七分靠设计4.1 先想清楚数据用什么结构存Redis Search 的索引是建立在 Redis 的 key 之上的。它支持两种主要的数据结构Hash传统方式一个商品一个 Hash字段就是 Hash 的 field。JSON配合 RedisJSON 模块用 JSON 文档存支持嵌套结构。我一般推荐用Hash因为简单、内存效率高、兼容性好。JSON 适合字段嵌套深、结构不固定的场景但查询语法会复杂一些。假设我们做一个商品搜索数据这样存HSET product:1001 \ title 无线蓝牙耳机 降噪运动款 \ description 主动降噪 续航30小时 蓝牙5.3 \ category 数码配件 \ price 299 \ sales 1520 \ created_at 1700000000每条商品一个 key前缀product:方便批量管理。4.2 创建索引字段类型决定查询能力索引用FT.CREATE命令创建。这是整个方案的核心字段类型选错了后面查询要么报错要么慢。FT.CREATE idx:product ON HASH PREFIX 1 product: SCHEMA \ title TEXT WEIGHT 5.0 \ description TEXT WEIGHT 1.0 \ category TAG \ price NUMERIC SORTABLE \ sales NUMERIC SORTABLE \ created_at NUMERIC SORTABLE逐个字段拆解ON HASH表示索引 Hash 类型的数据PREFIX 1 product:表示只索引以product:开头的 key。title TEXT WEIGHT 5.0TEXT 类型支持全文检索WEIGHT 是权重标题权重给 5描述给 1这样搜索时标题命中的排名会更高。category TAGTAG 类型用于精确匹配和过滤比如分类数码配件比 TEXT 快得多因为它不做分词直接按分隔符切分。price NUMERIC SORTABLENUMERIC 支持范围查询价格 100 到 500SORTABLE 表示这个字段可以用于排序。只有加了 SORTABLE 的字段才能出现在SORTBY里这是新手最容易踩的坑。提示SORTABLE 会让 Redis 额外维护一份排序用的数据结构占内存。不要给所有字段都加只给真正需要排序的加。4.3 中文分词的现实处理方案前面说了中文是 Redis Search 的短板。默认情况下TEXT 字段按空格和标点分词中文一整句会被当成一个词搜索效果很差。我实际项目里用过两种方案方案一n-gram 分词。在建索引时指定分词器FT.CREATE idx:product ON HASH PREFIX 1 product: SCHEMA \ title TEXT NOSTEM \ ...不过开源版对中文 n-gram 的支持需要通过参数配置比较折腾。方案二应用层预分词。这是我现在用得最多的。在写入 Redis 之前用应用代码比如 Python 的 jieba、Java 的 HanLP把中文切成词用空格拼起来再存import jieba title 无线蓝牙耳机降噪运动款 segmented .join(jieba.cut(title)) # 存成 无线 蓝牙 耳机 降噪 运动 款这样 Redis Search 就能按空格正常分词了。缺点是索引里存的是分词后的文本原始文本要另外存一份用于展示。这个方案简单可控分词效果取决于你用的分词库实测下来比硬啃 Redis 自带的中文支持靠谱得多。5. 查询实战把搜索接口写到生产可用5.1 基础全文检索最简单的查询FT.SEARCH idx:product 蓝牙耳机这会返回所有 title 或 description 里包含蓝牙或耳机的文档。注意默认是 OR 逻辑只要命中一个词就返回。如果要 AND 逻辑用引号或者FT.SEARCH idx:product 蓝牙 耳机实际上 RediSearch 默认对多个词是交集优先的评分逻辑但返回结果包含部分匹配。要严格 AND用FT.SEARCH idx:product (蓝牙) (耳机)5.2 过滤 排序 分页一个真实接口的完整写法真实业务里搜索从来不是单纯的关键词匹配。用户会选分类、筛价格区间、按销量排序、翻页。下面这条命令覆盖了这些需求FT.SEARCH idx:product 蓝牙耳机 category:{数码配件} price:[100 500] \ SORTBY sales DESC \ LIMIT 0 20 \ RETURN 3 title price sales拆解一下category:{数码配件}TAG 字段过滤花括号是 TAG 的语法。price:[100 500]NUMERIC 范围过滤方括号表示闭区间圆括号(表示开区间。SORTBY sales DESC按销量降序sales 必须建索引时加了 SORTABLE。LIMIT 0 20分页从第 0 条开始取 20 条。RETURN 3 title price sales只返回这三个字段减少网络传输。这个优化在高并发下很关键别把整个文档都拉回来。5.3 高亮和摘要搜索结果里给关键词加高亮是提升体验的常见需求FT.SEARCH idx:product 蓝牙耳机 \ HIGHLIGHT FIELDS 2 title description \ TAGS em /em \ SUMMARIZE FIELDS 1 description FRAGS 2 LEN 30HIGHLIGHT给命中词包上标签SUMMARIZE生成摘要片段FRAGS 2表示最多返回 2 个片段LEN 30每个片段 30 个词。前端拿到后直接渲染就行。5.4 聚合统计Redis Search 也支持类似 ES 的聚合虽然没那么强但常用场景够用。比如统计各分类的商品数量FT.AGGREGATE idx:product * \ GROUPBY 1 category \ REDUCE COUNT 0 AS cnt \ SORTBY 2 cnt DESC*表示匹配所有文档GROUPBY category按分类分组REDUCE COUNT计数。这个在筛选面板里显示每个分类有多少商品时特别有用。6. 性能调优与常见问题排查6.1 内存控制是头等大事Redis Search 最大的约束就是内存。索引本身、原始数据、排序结构都占内存。我一般会做这几件事设置 maxmemory 和淘汰策略maxmemory-policy建议用noeviction因为搜索数据被淘汰会导致结果缺失。如果内存实在紧张宁可加机器也别让搜索数据被淘汰。精简索引字段不是所有字段都要建索引。只给需要搜索、过滤、排序的字段建其他字段不建索引照样能存。用FT.INFO监控索引大小定期看索引占用了多少内存提前预警。FT.INFO idx:product返回里关注inverted_sz_mb、total_indexing_time、num_docs这几个指标。6.2 常见问题速查表问题现象可能原因解决办法查询报 Unknown field字段没建索引或拼写错误用FT.INFO检查 schemaSORTBY 报错排序字段没加 SORTABLE重建索引给字段加 SORTABLE中文搜不到没做分词处理应用层预分词或配置 n-gram查询越来越慢索引碎片或内存不足检查内存必要时FT.CREATE重建写入后搜不到索引异步更新有延迟等待毫秒级同步或检查 key 前缀是否匹配内存暴涨索引字段过多或数据量超预期精简字段评估数据规模6.3 我踩过的几个真实坑坑一key 前缀写错导致索引为空。有次我把数据存成product:1001索引前缀写成products:结果搜了半天没结果排查了半小时才发现是前缀不匹配。建索引后一定要用FT.INFO看num_docs是不是符合预期。坑二TAG 字段值里有空格。TAG 默认用逗号分隔多个值如果值本身含空格或特殊字符查询时要转义否则匹配不上。我一般会把 TAG 值规范化避免空格。坑三大批量导入时索引拖慢写入。一次性导入几十万条数据时每条都触发索引更新写入速度会明显下降。我的做法是分批导入每批几千条中间稍作停顿或者用 pipeline 批量提交。坑四以为 Redis Search 能完全替代 ES。前面反复强调过数据量上亿、需要复杂聚合时Redis Search 会力不从心。选型时一定要拿真实数据量压测别只看 demo 数据。7. 和 ElasticSearch 的选型对比与迁移思路7.1 一张表看清两者取舍维度ElasticSearchRedis Search数据规模PB 级受内存限制千万级较稳查询延迟毫秒到百毫秒亚毫秒到毫秒全文检索能力极强分词成熟中等中文需额外处理聚合分析非常强基础够用运维复杂度高低资源占用高JVM 磁盘低纯内存分布式扩展原生支持开源版较弱适用场景大型搜索、日志分析站内搜索、实时过滤7.2 什么情况下该从 ES 迁到 Redis Search我的判断标准是三条同时满足数据量在千万级以下内存放得下。查询以关键词 过滤 排序为主不需要复杂聚合。团队没有专职搜索运维或者对延迟极其敏感。如果满足迁移收益很明显服务器成本降一半以上延迟降一个数量级运维从伺候集群变成维护一个 Redis 实例。迁移思路上我一般这样做先把 ES 里的数据导出在应用层做分词预处理写入 Redis Hash建好索引然后双写一段时间做对比验证确认搜索结果和性能都达标后再切流量。整个过程不需要停机风险可控。7.3 混合架构也是一种选择不是非此即彼。我有个项目就是热数据用 Redis Search冷数据和复杂分析用 ES。用户搜索走 Redis Search 保证低延迟后台的运营报表、数据挖掘走 ES。两套系统各司其职成本反而比全量上 ES 更低。这种混合架构的关键是数据同步要做好一般用消息队列做异步同步保证两边最终一致。同步延迟控制在秒级对搜索场景完全够用。8. 我在实际项目中的几点体会Redis Search 这套方案我用在过内容站、电商站内搜索、后台管理系统几个不同类型的项目里整体感受是它不是银弹但在它擅长的场景里性价比高得离谱。最直观的一次一个原本用 ES 的项目服务器从 4 台 8G 降到 1 台 8G搜索接口 P99 从 60ms 降到 8ms运维告警几乎消失。当然代价是数据量必须控制住我们当时把历史数据归档到冷存储只保留近两年的热数据在 Redis 里这个取舍很划算。如果你打算上手我的建议是先用真实数据做一轮压测重点看三个数索引占多少内存、P99 延迟多少、写入吞吐够不够。这三个数达标了再考虑上生产。另外中文分词一定要提前验证这是最容易翻车的地方别等上线了才发现搜不准。最后分享一个小技巧RedisInsight 那个 Web 界面默认 8001 端口里有个 Search 面板可以可视化地建索引、写查询、看结果调试阶段比敲命令行舒服太多强烈建议用起来。