ARTICLE DETAIL

资讯详情

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

比Elasticsearch快5倍?中小规模业务搜索的引擎选型与迁移实践

比Elasticsearch快5倍?中小规模业务搜索的引擎选型与迁移实践 直接说结论至少在中小规模业务搜索这个场景里比 Elasticsearch 快 5 倍的搜索引擎确实存在而且不止一个。我之前在一台 4C8G 的云主机上做过对比同样一批 100 万条商品的索引数据用 Typesense 和 Meilisearch 跑前缀搜索和筛选排序P99 延迟能做到 ES 的三分之一到五分之一吞吐量翻几倍也很正常。但这篇文章不是要无脑吹ES 不行了。我是一个用了很多年 Elasticsearch 的老用户恰恰是因为太了解 ES 的优缺点才知道什么场景该换什么场景换了就是给自己挖坑。下面我把为什么 ES 会慢谁更快快在哪怎么迁这几个问题一次讲透。1. 为什么 Elasticsearch 在某些场景下真的不快先别急着喷我在黑 ES。Elasticsearch 在日志分析、全文检索、复杂聚合这些领域依然是王者这点没什么好争的。但它慢的地方也客观存在而且这些慢恰恰集中在我们最常用的业务搜索场景里。1.1 查询链路本身就比其他引擎长一次 ES 搜索大致要经历这么几步请求先打到协调节点协调节点把请求广播到该索引的全部分片每个分片在本地执行查询和聚合再把结果返回给协调节点协调节点做全局合并、排序、翻页最后才返回给客户端。这套分布式架构在数据量几百 GB、索引几十个分片时毫无问题。但很多团队的数据量根本没到那个规模反而一直在承担这套架构带来的固定开销。你查一条数据ES 内部要做分片路由、多分片并发、结果归并这些步骤每一步都有网络序列化和内存拷贝的成本。换成单机内存型搜索引擎索引就一份查询直接在当前节点上执行没有跨分片的协调开销自然快得多。1.2 倒排索引是双刃剑ES 的核心是 Lucene 的倒排索引对我搜一个词它出现在哪些文档这个问题速度确实极快。但业务搜索里大量查询不是单纯的关键词匹配而是带着各种条件价格在 50 到 200 之间的商品最近 7 天发布且标签包含促销的文章某个分类下按销量排序的前 20 条这些查询一旦叠加了过滤、排序、聚合ES 就不能只靠倒排索引了。Lucene 需要回表读取正排字段doc_values或者遍历位集bitset再结合堆排序完成 Top N 的筛选。字段越多、条件越复杂这条链路上的开销就越大。1.3 聚合和排序的开销常常被低估ES 做 terms 聚合时对于高基数字段比如用户 ID、商品 ID内存占用和计算量都会暴涨。更难受的是ES 的分片架构导致聚合结果要先在各分片本地做一遍再汇总到协调节点做合并。只要有一个分片处理得慢整个查询就得等它。我自己踩过一个典型的坑一个电商项目的搜索页需要同时按分类过滤、按价格排序、再返回品牌聚合。索引数据量其实只有 200 万条但每次请求平均耗时 800 毫秒以上。后来把同样的数据和查询逻辑放到单机内存型搜索引擎上耗时直接降到 100 毫秒左右。那个项目并不需要 ES 的分布式能力纯粹是被架构的固定开销拖慢了。1.4 数据量和内存之间的错位ES 为了控制内存占用索引文件大多在磁盘上靠操作系统页缓存加速。当索引文件超过物理内存的一定比例磁盘 IO 就会成为瓶颈。很多人觉得ES 慢的体感其实就是磁盘 IO 拖垮的。而新兴的搜索引擎普遍采用尽量把索引放进内存的设计加上更紧凑的索引编码方式同样 100 万条数据它们的索引体积往往只有 ES 的几分之一可以整索引常驻内存。内存操作和磁盘操作的速度差距不用我说你也明白。2. 市面上比 ES 快的方案到底有哪些市面上的搜索引擎很多但真正常被拿来和 ES 对比、也确实在某些场景下更快的主力就几个Typesense、Meilisearch、Manticore Search还有 ZincSearch。2.1 主流方案横向对比引擎开发语言部署方式核心优势适合场景不适合场景ElasticsearchJava多节点分布式功能全面、生态完善日志分析、复杂聚合、大规模集群中小业务搜索、快速迭代项目TypesenseC单机或小集群极速、内存优先、内置向量搜索业务搜索、模糊搜索、地理搜索千亿级数据、复杂文本分析MeilisearchRust单机为主开箱即用、中文友好、搜索体验好站点搜索、应用内搜索、MVP超大集群、复杂聚合Manticore SearchC单机或集群MySQL 协议兼容、性能和 ES 接近有时更快替代 ES 的老牌查询场景生态和文档相对小众ZincSearchGo单机轻量、S3 存储、API 兼容 ES日志搜索替代功能还不够成熟这几个项目里我最推荐重点关注的是 Typesense 和 Meilisearch。它们两个是目前比 ES 快这件事上口碑最稳、社区最活跃、落地案例最多的。2.2 为什么它们能做到更快先说说 Typesense。它的核心设计是内存优先索引尽量装进 RAM底层是 C 实现对 CPU 缓存友好。它的集群模式用的是 Raft 一致性协议主节点单份索引副本节点只用来容灾和扩展读能力。对比 ES 的多主分片 副本分片机制Typesense 在查询时就少了一层多个分片分别查询再合并的开销。Meilisearch 是 Rust 写的把默认体验做到了极致无需配置启动建索引就能搜索。它对中文用户尤其友好内置的 charabia 分词器支持中文切词可以直接解决 ES 那套 IK 分词器配置的麻烦。Meilisearch 的搜索速度来自大量内存缓存、前缀搜索优化和精心设计的倒排索引结构。有一个容易被忽略的点这两个引擎在实现时都针对即时搜索场景做了特殊优化也就是用户每敲一个字符就发一次请求。ES 对这类高频、短查询支持也不是不行但在同样的压力下延迟和 CPU 占用都会明显偏高。原因就在于前面说的协调开销和分片合并。2.3 快是快但不是所有场景都适合换这里必须泼盆冷水。它们的快是有前提的数据集规模在千万级以内通常表现最好查询以关键词、筛选、排序、分面聚合为主不需要非常复杂的文本分析管道对数据的实时一致性要求没那么苛刻如果你的场景是百亿级日志、需要在大规模集群上做 ad-hoc 分析或者依赖 ES 全家桶Logstash、Kibana、Beats、各种插件那现在还不是换的时候。记住一句话搜索不是越快越好而是要在对的地方用对的工具。Typesense 和 Meilisearch 做的不是 ES 的全面替代品而是把业务搜索这个最痛的场景做得比 ES 好。3. 实操我用 Typesense 替换 ES 搜索服务的全过程理论说再多不如直接跑一遍。下面以 Typesense 为例分享我从部署到上线、再到性能对比的全过程。这套方案不挑语言REST API 谁都能调。3.1 用 Docker 一分钟拉起 TypesenseTypesense 官方提供了 Docker 镜像本地体验最简单的方式就是直接跑容器docker run -d \ --name typesense-server \ -p 8108:8108 \ -v /data/typesense:/data \ -e TYPESENSE_DATA_DIR/data \ -e TYPESENSE_API_KEYmy_search_api_key \ typesense/typesense:27.0注意TYPESENSE_API_KEY 如果不设置Typesense 会生成一个临时 key重启就失效。生产环境务必显式配置并且建议用环境变量管理不要写死在代码里。启动后访问http://localhost:8108会返回一行提示说明服务还在运行这就是 Typesense 对 API 路径的一种守卫逻辑。回到命令行用健康检查接口验证一下curl http://localhost:8108/health正常返回{ok:true}就说明服务起来了。整个过程比装 ES 简单太多不用调 JVM 堆内存不用手动建索引模板不用等分片也变成 green。3.2 创建集合并导入数据Typesense 里的集合就类似 MySQL 的表、ES 的索引。我们创建一个商品集合包含标题、描述、价格、分类、库存几个字段curl -X POST http://localhost:8108/collections \ -H X-TYPESENSE-API-KEY: my_search_api_key \ -H Content-Type: application/json \ -d { name: products, fields: [ {name: title, type: string}, {name: description, type: string}, {name: price, type: float}, {name: category, type: string}, {name: in_stock, type: bool} ], default_sorting_field: price }这里有个容易踩坑的点default_sorting_field不支持布尔类型或字符串类型必须是数字类型。所以如果你需要按某个字段排序一定记得在集合里给它分配合适的数字类型字段。导入数据用批量接口比较划算一次可以传一批 JSON 文档curl -X POST http://localhost:8108/collections/products/documents/import \ -H X-TYPESENSE-API-KEY: my_search_api_key \ -H Content-Type: text/plain \ --data-binary products.jsonl每一行是一个 JSON 文档Typesense 返回的响应里会带每一行的导入状态。如果某一行失败后面的行不会受影响而且响应中会明确指出失败的行号排查问题很方便。3.3 常用搜索场景怎么调 APITypesense 的搜索接口同样走 REST核心参数非常直白curl -X GET \ http://localhost:8108/collections/products/documents/search?q手机query_bytitle,descriptionfilter_byin_stock:truesort_byprice:asc几个核心参数q搜索关键词query_by要在哪些字段里搜索多个字段用逗号分开filter_by过滤条件支持:、、、、等也支持多个条件用连接sort_by排序price:asc表示按价格升序facet_by分面统计字段比如facet_bycategory,price可以统计分类和价格区间的分布拿它和 ES 的 DSL 一比直观感受就是轻量太多。ES 同样的查询要是用 Java Client 拼 QueryBuilder代码量能绕两圈。Typesense 这边几行代码、一个 URL 就能跑通。3.4 性能实测一份可复现的对比数据我在自己的一台 4C8G 云主机上做了一个基准测试不是特别严谨但很能说明问题。机器配置4 vCPU、8GB 内存、SSD 系统盘。数据集是 100 万条商品公开数据ES 用 7.10 版本默认配置单节点Typesense 用 27.0 版本默认配置单节点。测试工具是 wrk压测查询是关键词搜索 分类过滤 按价格排序这个组合场景。指标ElasticsearchTypesense索引大小约 1.8 GB约 450 MB单查询 P99 延迟245 ms48 ms极限 QPS线程数 16约 920约 4100这只是我在特定数据集、特定硬件下的测试结果换一批数据和机器数字会变但趋势基本一致Typesense 在内存足够容纳索引的情况下查询延迟和吞吐量都明显优于同配置的 ES。它响应体里自带search_time_ms每次请求到底花了多少毫秒直接能看到。我在测试过程中还注意观察了 ES 的慢查询日志。压测期间 ES 的慢查询日志频繁记录超过 200ms 的请求而 Typesense 的search_time_ms大部分在 10ms 上下。直观的对比比任何 benchmark 文档都更有说服力。3.5 Meilisearch 的备选体验顺便说一句Meilisearch 的部署更简单Docker 启动后直接有 Web 搜索界面可以体验搜索效果docker run -d \ --name meilisearch \ -p 7700:7700 \ -e MEILI_MASTER_KEYmaster_key \ -v /data/meili:/meili_data \ getmeili/meilisearch:latest它对中文搜索的开箱体验比 Typesense 好很多因为它内置的中文分词器能正确处理中文长文本。如果你主要做站点内文章搜索、文档搜索Meilisearch 会让你少折腾很多分词配置的功夫。Typesense 对新版本也加强了对中文支持但我在实际项目里依然更倾向于用 Meilisearch 处理中文全文搜索场景。4. 替换过程中最容易踩的坑把 ES 换成 Typesense 或 Meilisearch 不是改个 API 地址那么简单。我在这条路上栽过几次把这些坑单独列出来给你提个醒。4.1 中文分词谁来做这是中文项目迁移时最大的坑。ES 有 IK 分词器、HanLP 等成熟插件而 Typesense 默认自带分词器对中文支持比较弱尤其是不借助空格分词的中文句子场景。我测试过直接用 Typesense 搜索智能手机这个词如果你把整句话新款智能手机作为一条 title 索引进去它默认可能不会把智能手机作为一个完整词匹配到。解决思路有几条应用层先分词再写入比如用 jieba 或 HanLP 做文本预处理把分词后的词用空格拼接成一个独立的搜索字段。Typesense 在较新版本中支持通过tokenizers配置自定义分词规则可以针对中文场景设置 N-gram 模式。Meilisearch 就没有这个烦恼它对中文的支持是默认开箱的这也是很多中文站点选择它的理由。4.2 内存规划不要死脑筋Typesense 官方文档建议把整个索引放在内存中所以内存大小至少要能覆盖数据总量。这里有个关键点索引大小不等于原始数据大小编码后通常更小但也要按原始数据的 30%~60% 做预估。举个例子1000 万条商品数据如果原始数据有 5GB索引打包后可能在 1.5~3GB 之间8GB 内存的机器跑起来问题不大。如果数据大到内存放不下不建议硬上 Typesense。它虽然也在优化磁盘存储但那不是它的主场。这种情况老老实实用 ES或者升级机器内存。4.3 别依赖 ES 那些全家桶能力ES 真正强大的地方是它的周边生态Kibana 做可视化、Logstash 做数据管道、各种插件做分析。Typesense 和 Meilisearch 更专注于搜索本身不是 ELK 全家桶。迁移时一定要先盘点现有业务里哪些功能依赖 ES 的特性嵌套文档nested type用的是不是很多父子关系文档结构有没有用到依赖不依赖term高基数聚合向量检索和混合检索的需求是否存在Typesense 支持固定 schema 的扁平 JSON 文档不支持复杂的嵌套文档。如果你原来的业务大量依赖 nested type建议别急着迁或者先做 schema 扁平化改造。4.4 高可用和数据一致性Typesense 的多节点集群是基于 Raft 协议的节点数量一般建议 1、3、5 这样的奇数。它的写入是主节点负责副本节点通过 Raft 日志同步数据。但注意它不是 ES 那种多主分片并行写写入压力大的时候主节点可能成为瓶颈。Meilisearch 更直接官方对集群模式支持相对有限生产环境很多用户直接跑单机 备份方案。如果你对高可用要求很严格需要仔细读官方文档确认当前版本的集群能力。我的建议是中小规模业务搜索场景单台 Typesense 加上定期的数据备份已经能覆盖绝大多数需求。把实时性要求极高的数据缓存层也做在更前面比如 Redis搜索引擎只做搜索主路径不承担写入风暴。5. 迁移实战从 ES 平滑切换到 Typesense 的步骤如果你确定要换下面是我总结的一套平滑迁移路径。这套方案的核心思路是双写 校验 切流能最大程度降低切换风险。5.1 第一步数据全量导出导入在 ES 里对目标索引执行 scroll 查询把数据导出成 JSONL 文件curl -X POST http://localhost:9200/products/_search?scroll5msize5000 \ -H Content-Type: application/json \ -d {query: {match_all: {}}}拿到第一批文档和_scroll_id后用 scroll 接口一路翻页导完。然后按前面说的 import 接口把 JSONL 文件导入 Typesense。这一步建议写个小脚本不要手动处理数据量大的时候很容易中途断。5.2 第二步双写机制在应用层做一个写入分流同时写 ES 和 Typesense。上线初期ES 依然承担读流量Typesense 只累积数据。这个阶段的目标是让两边数据保持同步为后续切流做准备。双写期间要注意幂等性和失败补偿。如果 Typesense 写入失败不能影响正常的 ES 写入要记录失败日志通过定时任务重放。5.3 第三步读流量灰度切流先切一部分读流量到 Typesense比如 10%。这一步要看两类指标接口 P99 延迟是否稳定低于切流前搜索结果质量通过对比 ES 和 Typesense 的搜索结果检查返回的召回集是否有明显偏差如果查出来的结果差异很大先别急着全切多半是查询参数映射没对齐。我就遇到过filter_by的布尔值格式不一样导致结果集不一致的问题排查了好久才定位到。5.4 第四步全量切流 下线 ES灰度观察 2~3 天确认稳定后把全部读流量切到 Typesense。ES 保留一段时间用于数据回刷和问题排查最终下线。强烈建议在整个过程中保留一份 ES 索引的备胎数据至少保留到新系统稳定运行两周之后。搜索系统出问题的时候你需要的不是更多讨论而是一套可以随时回滚的方案。6. 常见问题速查表问题场景原因解决方案搜索中文长句召回不准默认分词对中文支持弱应用层预分词或配置 N-gram 分词对中文场景优先考虑 Meilisearch集合创建报错 default_sorting_field 不支持该字段必须为数字类型将用于默认排序的字段设为 int/float导入数据响应里出现某行失败JSONL 单行格式有问题检查该行 JSON 是否合法字段类型是否匹配集合 schema查询结果和 ES 不一致查询参数翻译有误或过滤条件类型不匹配逐个对照 ES DSL 和 Typesense 查询参数先小流量验证搜索结果排序不稳定缺少 tie_breaker 或排序字段有重复值配置次要排序字段内存占用过高偶尔 OOM索引数据超过内存规划优化字段类型、精简索引字段或升级内存超大规模场景建议回到 ES7. 一些选型建议和我的个人体会写了这么多最后聊聊我的真实感受。ES 不是不好它是太全了全到很多场景里你根本用不到那么多能力却要为它付出性能、运维和复杂度的代价。如果你现在正在做一个新项目数据量不大、搜索是核心体验、团队没人愿意天天调 JVM 和分片参数我更推荐从第一天就用 Typesense 或 Meilisearch。如果你已经在用 ES 且没有明显痛点那也没必要追新稳定比什么都重要。我自己目前更偏爱的组合是Meilisearch 处理中文文本搜索Typesense 处理高性能过滤和业务查询。两者互补日常使用几乎没有让我挠头的时候。最后再分享一个小技巧无论选哪个都给搜索引擎单独配置告警监控重点盯 P99 延迟、内存占用、慢查询数这三个指标。搜索引擎慢起来用户比你先感知到。
返回列表