ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Redis Search 与 Elasticsearch 选型对比:实测快 5 倍的场景与边界

Redis Search 与 Elasticsearch 选型对比:实测快 5 倍的场景与边界 1. 从一次搜索延迟排查说起为什么我开始重新审视搜索引擎选型去年帮一个做内容社区的朋友处理线上问题他们的检索服务在晚高峰频繁超时。技术栈是典型的 Elasticsearch 单集群扛全文检索数据量其实不算夸张——两千多万条帖子加评论但每次带过滤条件的模糊查询 P99 能飙到 1.8 秒以上。运维同学第一反应是加节点从 3 个数据节点扩到 6 个内存翻倍结果延迟只降了不到 20%。这个投入产出比让人很难接受。后来我们把查询日志拉出来逐条分析发现问题根本不在集群规模而在于查询模式大量请求是关键词匹配 多字段过滤 按时间排序 分页而且过滤字段的基数很高。Elasticsearch 在这种场景下需要把倒排索引命中的文档逐个回表取字段、算分、排序再走一遍 fetch 阶段开销主要花在协调节点和数据节点之间的往返以及打分计算上。换句话说不是它不够快而是它的架构决定了这类查询天然偏重。这次排查让我开始认真对比几个主流方案其中 Redis Search 给我的印象最深。在同样的数据集和查询模式下它的响应时间稳定在 200 到 300 毫秒差不多是原来 Elasticsearch 的五分之一。标题里说比 ES 快 5 倍这个数字不是拍脑袋来的而是在特定查询形态下实测出来的量级差异。当然这不代表 Redis Search 全面碾压 Elasticsearch两者定位不同适用的场景也不同。这篇文章我想把这次选型对比的完整思路、实测方法、踩过的坑和最终结论都摊开讲清楚给正在做检索选型或者被 ES 延迟困扰的同行一个参考。需要先说明的是本文讨论的搜索引擎指的是面向应用的结构化/半结构化数据检索服务不是那种爬全网做索引的通用搜索引擎。Redis Search 和 Elasticsearch 都属于前者只是设计哲学差异很大。下面我会从原理、实测、配置、优化几个层面展开。2. Redis Search 与 Elasticsearch 的架构差异到底在哪2.1 一个内存优先一个磁盘优先理解性能差异得先理解两者对数据的态度。Elasticsearch 建立在 Lucene 之上核心是倒排索引 段合并segment merge的磁盘存储模型。文档写入后先进内存 buffer再 refresh 成 segment 落到文件系统缓存最终 fsync 到磁盘。查询时如果数据不在 page cache 里就要走磁盘 IO。它的设计目标是海量数据的持久化检索能扛 TB 级甚至 PB 级数据代价是查询路径长、延迟下限高。Redis Search 则是纯内存索引。它作为 Redis 的一个模块运行索引结构直接放在内存里查询时不需要磁盘 IO也没有 segment 合并带来的写放大。数据持久化靠 Redis 自身的 RDB/AOF 机制兜底但检索路径本身是内存操作。这就解释了为什么它的延迟能压到毫秒级——没有磁盘这一层物理上限就高出一截。打个比方Elasticsearch 像是一个藏书百万册的图书馆你要找书得先查目录卡、再去书架取书流程规范但慢Redis Search 像是一个把所有书都摊在桌面上的书房伸手就能拿到但桌面大小有限书太多就放不下。2.2 查询执行路径的差异具体到一次查询的执行差异更明显。Elasticsearch 的查询分两个阶段query phase和fetch phase。query phase 在每个分片上执行算出命中文档的 doc id 和排序值返回给协调节点协调节点归并排序后再发起 fetch phase 去各分片取实际字段。跨分片通信、打分BM25 或自定义、归并都是开销。即使你用了 filter 不走打分fetch 阶段的网络往返依然存在。Redis Search 的查询在单个 Redis 实例内完成集群模式下按 key 分片没有独立的协调节点做二次归并。它用的是自己的索引结构支持全文、数值、标签、地理等多种字段类型查询时直接在内存索引上做交集、并集运算然后返回结果。路径短、无跨节点归并、无磁盘 IO这是它快的根本原因。2.3 两者的能力边界对比不能只看速度得看能不能满足需求。我整理了一张对比表维度Redis SearchElasticsearch数据规模受内存限制通常千万级可扩展到 TB/PB 级查询延迟毫秒到百毫秒级百毫秒到秒级全文检索能力支持分词器生态较弱极强分词、同义词、高亮完善聚合分析支持基础聚合非常强大适合 OLAP 场景持久化依赖 Redis RDB/AOF原生持久化副本机制成熟运维复杂度低复用 Redis 运维体系高需要专门的集群运维成本内存成本高存储成本低内存需求相对小这张表的核心结论是Redis Search 适合数据量可控、查询延迟敏感、以过滤和排序为主的场景Elasticsearch 适合数据量大、检索逻辑复杂、需要深度聚合的场景。选型不是比谁快而是比谁匹配。3. 实测对比我是怎么测出 5 倍差距的3.1 测试环境与数据集构造为了让对比有说服力我搭了一套尽量公平的环境。两台机器配置相同8 核 16GSSD 盘千兆内网。一台跑 Elasticsearch 7.17 单节点一台跑 Redis 7.2 加 Redis Stack内置 RediSearch 2.8。数据集用脚本生成 2000 万条模拟帖子每条包含标题、正文、作者 ID、分类标签、发布时间、点赞数等字段。Elasticsearch 的索引 mapping 里标题和正文用 text 类型配 ik 分词标签和作者用 keyword时间用 date点赞数用 integer。Redis Search 这边用 FT.CREATE 建索引标题正文用 TEXT 类型标签作者用 TAG时间用 NUMERIC点赞数也用 NUMERIC。两边字段语义对齐保证查询可比。提示数据集生成时要注意字段值的分布。如果所有标签都集中在少数几个值上过滤的选择性会很差测出来的延迟不能反映真实场景。我特意让标签分布接近长尾模拟真实社区的分类分布。3.2 查询场景设计与压测方法我设计了四类查询覆盖典型使用场景纯关键词匹配在标题和正文里搜一个词返回前 20 条。关键词 标签过滤 时间排序搜词限定某个分类按发布时间倒序。多标签过滤 数值范围 分页限定多个标签点赞数大于某值翻到第 10 页。聚合统计按分类统计命中数量。压测工具用 wrk 和自写的 Python 脚本每类查询预热 1000 次后正式跑 60 秒记录 P50、P95、P99 和 QPS。并发从 10 逐步加到 200观察延迟曲线。3.3 实测数据与结果解读结果如下表单位毫秒查询类型ES P50ES P99Redis Search P50Redis Search P99纯关键词45320835关键词过滤排序180180022210多标签范围分页260240035280聚合统计12090018150可以看到查询越复杂、过滤条件越多Redis Search 的优势越明显。纯关键词查询差距约 5 到 9 倍带过滤排序的复杂查询差距能到 8 倍以上。标题里说的快 5 倍是个保守说法实际在复杂查询下差距更大。但这里有个关键前提Redis Search 的数据全部在内存里而 Elasticsearch 的数据量已经超过了 page cache 能缓存的范围。如果 ES 的数据集小到能全量放进内存差距会缩小。所以这个对比的准确表述是在数据量超过内存缓存能力、且查询以过滤排序为主的场景下Redis Search 比 Elasticsearch 快约 5 倍。3.4 内存占用与成本核算快是有代价的。2000 万条数据Redis Search 索引加原始数据占了约 11G 内存。同样数据在 Elasticsearch 里堆内存给了 8G磁盘占用约 6G但查询时大量依赖 page cache实际机器内存占用也在 10G 以上。单看内存Redis Search 略高但考虑到它省掉了独立的集群协调节点和副本开销在小规模场景下总成本反而可能更低。如果数据量继续涨到 1 亿条Redis Search 需要的内存会到 50G 以上这时候单机就吃不消了得上集群分片。而 Elasticsearch 在这个量级依然从容。所以成本拐点大概在几千万条数据、内存 32G 到 64G 这个区间超过之后 ES 的性价比开始反超。4. 把 Redis Search 跑起来从建索引到查询的完整操作4.1 环境准备与模块加载Redis Search 现在通过 Redis Stack 分发最省事的方式是用官方 Docker 镜像docker run -d --name redis-stack \ -p 6379:6379 \ -p 8001:8001 \ redis/redis-stack:latest8001 端口是 RedisInsight 可视化界面调试索引很方便。如果你用源码编译需要单独加载 RediSearch 模块在 redis.conf 里加loadmodule /path/to/redisearch.so。我建议直接用 Redis Stack省去模块版本兼容的麻烦。验证模块是否加载成功redis-cli MODULE LIST输出里应该能看到search模块及其版本号。如果没看到说明模块没加载上后面所有 FT.* 命令都会报错。4.2 用 FT.CREATE 设计索引结构建索引是核心步骤字段类型选错了后面查询会很别扭。以帖子场景为例FT.CREATE post_idx ON HASH PREFIX 1 post: \ SCHEMA \ title TEXT WEIGHT 2.0 \ content TEXT \ author TAG \ category TAG \ publish_ts NUMERIC SORTABLE \ likes NUMERIC SORTABLE几个关键点解释一下。ON HASH表示索引的是 Redis Hash 类型的数据PREFIX 1 post:表示只索引 key 以post:开头的 Hash。WEIGHT 2.0让标题在全文检索里权重更高符合标题匹配更重要的直觉。SORTABLE标记的字段才能用于排序没标 SORTABLE 的字段排序会报错这是新手最容易踩的坑。TAG 类型适合精确匹配的枚举值比如分类、作者它比 TEXT 更省内存也更快。4.3 数据写入与索引同步数据写入有两种方式。一种直接写 HashRediSearch 会自动索引HSET post:1001 title Redis Search 实战 content ... \ author u123 category tech publish_ts 1700000000 likes 42另一种是批量导入用FT.CREATE配合 pipeline 批量 HSET效率高很多。我实测 2000 万条数据用 pipeline 分批写入大概 15 分钟能完成单条写入则要几个小时。注意写入时如果 Hash 的字段名和索引 schema 对不上该字段不会被索引但也不会报错。排查为什么搜不到时第一件事就是用FT.INFO post_idx看索引里的文档数和字段配置。4.4 查询语法与分页实践基础查询FT.SEARCH post_idx title:Redis category:{tech} \ SORTBY publish_ts DESC LIMIT 0 20字段:值是字段限定语法{tech}是 TAG 匹配多个 TAG 用|表示或。数值范围用likes:[100 inf]。分页靠LIMIT offset num但要注意深分页性能会下降因为 offset 越大需要跳过的结果越多。深分页的优化思路是用游标式分页记录上一页最后一条的排序值下一页用范围查询接着取FT.SEARCH post_idx category:{tech} publish_ts:[(1700000000 inf] \ SORTBY publish_ts ASC LIMIT 0 20这样每次都是从索引的某个位置开始取不随页码增大而变慢。这个技巧在 ES 里叫 search_after思路是一样的。5. 那些文档里不会写的踩坑记录5.1 中文分词的现实困境Redis Search 内置的分词器对中文支持有限默认按空格和标点切分中文长句会被当成一个整体导致搜搜索引擎匹配不到推荐一个搜索引擎。解决办法有两个一是写入前用外部分词库如 jieba预处理把词用空格拼好再存二是用 Redis Search 2.6 之后支持的自定义分词器接口。我选的是第一种在应用层做分词。代价是索引里存的是分词后的文本原文需要另存一份用于展示。这个取舍要提前想清楚不要指望 Redis Search 开箱即用地处理好中文这是它和 Elasticsearch 差距最大的地方。5.2 内存碎片与索引膨胀跑了一段时间后发现内存涨得比预期快。用MEMORY DOCTOR一看碎片率到了 1.6。原因是频繁更新文档时旧索引项不会立即回收会留下碎片。解决办法是配置activedefrag yes开启主动碎片整理或者定期用FT.CREATE重建索引。另外TAG 字段如果基数极高比如把用户 ID 做成 TAG索引体积会膨胀得厉害。TAG 适合基数在几千到几万级别的字段超过这个量级要考虑是否真的需要索引它。5.3 持久化与数据安全的平衡Redis Search 的数据在内存里一旦进程崩溃且没有持久化索引就没了。生产环境必须开 AOFappendfsync everysec是延迟和安全的折中点。但要注意AOF 重放时 RediSearch 会重建索引数据量大时启动会很慢。我实测 2000 万条数据 AOF 重放要 8 分钟左右这段时间服务不可用。如果对可用性要求高得做主从复制从节点预热好再切流量。或者接受索引可重建的思路把原始数据存在 MySQL 等持久化存储里Redis 只作为检索加速层崩了就从源头重建。5.4 集群模式下的分片陷阱单机内存扛不住时会上 Redis Cluster。但 RediSearch 在集群模式下每个分片只能搜自己那部分数据跨分片的聚合和排序需要客户端自己做。这意味着你没法像单机那样一条命令拿到全局 Top 20得从每个分片各取 20 条再归并。这个逻辑得在应用层实现复杂度不低。我的建议是能用单机大内存解决就别上集群。现在单机 256G 内存的机器很常见能撑到上亿条数据比集群运维省心得多。6. 什么场景该选它什么场景该绕道6.1 优先考虑 Redis Search 的几种情况如果你的场景符合下面几条Redis Search 值得认真评估数据量在千万级以内内存放得下查询以关键词 过滤 排序 分页为主聚合需求简单已经在用 Redis想复用现有运维体系不想再维护一套 ES 集群对延迟敏感P99 要求控制在几百毫秒内团队规模小没有专职的搜索运维。我那个内容社区的朋友最后就是切到了 Redis Search把 ES 集群下线机器成本降了一半延迟还更稳了。他们的数据量正好在 Redis Search 的舒适区。6.2 老老实实用 Elasticsearch 的场景反过来这些情况别硬上 Redis Search数据量上亿甚至更多内存成本扛不住需要复杂的分词、同义词、拼写纠错、高亮有大量聚合分析、报表类查询需要跨字段的相关性打分调优数据必须强持久化不能接受重建索引的窗口。Elasticsearch 在这些方面积累深厚不是 Redis Search 短期能替代的。选型的本质是匹配不是追新。6.3 混合架构其实可以都要还有一种思路是两者结合。用 Elasticsearch 做全量数据的深度检索和分析用 Redis Search 做热数据的快速检索。比如社区场景里最近 7 天的帖子放 Redis Search 保证实时性历史数据放 ES 做归档查询。查询时先查 Redis没有再回源 ES。这种架构的复杂度在于数据同步和一致性维护适合有一定工程能力的团队。如果团队小我建议先单用一个把场景跑透再考虑混合。7. 性能调优的几个实操参数7.1 索引层面的关键配置建索引时几个参数对性能影响很大。MAXTEXTFIELDS允许索引更多 TEXT 字段但会增加内存NOOFFSETS关闭词偏移量存储省内存但失去高亮能力NOHL关闭高亮NOFIELDS关闭字段级过滤。如果不需要高亮加上NOOFFSETS NOHL能省 20% 到 30% 内存。FT.CREATE post_idx ON HASH PREFIX 1 post: \ NOOFFSETS NOHL \ SCHEMA title TEXT content TEXT category TAG publish_ts NUMERIC SORTABLE7.2 查询层面的优化技巧尽量用 TAG 代替 TEXT 做过滤TAG 是哈希匹配比全文匹配快得多避免前缀通配符查询title:red*这类查询无法用索引会退化成扫描限制返回字段用RETURN 2 title publish_ts只取需要的字段减少网络传输合理设置 LIMIT不要一次取几千条深分页用游标。7.3 监控指标该看什么生产环境要盯几个指标FT.INFO里的indexing状态是否在重建、num_docs文档数是否异常、hash_indexing_failures索引失败次数。Redis 层面看used_memory、mem_fragmentation_ratio、evicted_keys。如果出现 evicted_keys 大于 0说明内存不够开始淘汰数据了索引会不完整这是最危险的信号。我一般会配一个告警碎片率超过 1.5 或者淘汰键数大于 0 就通知。这两个指标能提前发现大部分内存相关问题。8. 我个人的选型体会折腾完这一轮我最大的感受是没有银弹只有匹配。Redis Search 快是因为它把数据放在内存、砍掉了磁盘和跨节点归并这些环节代价是内存成本和规模上限。Elasticsearch 慢一点换来的是海量数据能力和丰富的检索功能。标题说比 ES 快 5 倍这个结论成立的前提是场景对得上——数据量可控、查询以过滤排序为主、能接受内存成本。如果你正被 ES 的延迟困扰我的建议是先别急着换把查询日志拉出来分析一下。很多时候延迟高是因为查询写法有问题比如该用 filter 的地方用了 query、该用 keyword 的地方用了 text、深分页没优化。这些改完可能就够用了。如果确实是架构层面的瓶颈再考虑 Redis Search并且一定要用真实数据做压测别信任何脱离场景的性能数字包括我这篇里的。最后分享一个小技巧迁移时不要一次性切先用双写的方式让两边数据同步查询走灰度对比一段时间的结果一致性和延迟表现确认没问题再切流量。这样即使 Redis Search 那边有问题也能快速回滚到 ES风险可控。
返回列表