ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

比 Elasticsearch 快 5 倍的轻量搜索引擎:Typesense 实战部署与性能对比

比 Elasticsearch 快 5 倍的轻量搜索引擎:Typesense 实战部署与性能对比 很多用 Elasticsearch 用得痛不欲生的人看到“比 ES 快 5 倍”这种标题第一反应多半是又来了个忽悠。但我今天说的这个方案不是概念炒作而是实实在在把 ES 在很多场景下按在地上摩擦的替代品。先说清楚它不是要取代 ES 在日志分析和海量数据聚合上的地位而是在全文检索、站内搜索、小型应用的后台搜索这几个场景里用更轻量的架构换来了肉眼可见的速度提升。我用它做了几个月的生产环境验证有资格说这话。这个搜索引擎的核心优势说白了就一句话不做 ES 那么多复杂的分词、倒排索引外带副本同步而是把索引直接塞进内存用系统级的底层优化把查询延迟压到毫秒级。如果你的搜索场景用不到 ES 的一半功能那这种“重剑无锋”的思路反而给你省下了巨大的机器成本和运维负担。我这就把这几个月折腾出来的经验、配置、踩坑点全部摊开讲清楚从选型思路到实测数据再到生产部署一条龙说透希望能帮你少走点弯路。1. 先搞清楚ES 慢在哪凭什么能比它快 5 倍在研究任何替代品之前你得先理解 ES 的“慢”到底是什么场景下的慢。ES 本身吞吐量巨大能扛 PB 级数据这是它厉害的地方。但如果你只是做一个垂直领域的站内搜索数据量在百万到千万这个量级ES 的很多“重机制”就成了负担。1.1 ES 的索引流程到底有多重ES 写入一条文档要走完 analyzer 分词、生成倒排索引、写 translog、写 segment、定期 refresh 让文档可见、定期 merge 合并 segment 这一整套流程。每一层都是为了解决分布式场景下的数据一致性和查询稳定性但代价就是 CPU 和 IO 被持续消耗。尤其那个 refresh 操作默认 1 秒刷新一次你以为刚写入就能搜到其实背后是内存 buffer 不停构建 segment 段。数据一多merge 的时候 CPU 直接飙高查询性能跟着飘这是 ES 集群常见的“抖动弹”问题。我在测试环境只写到几十万条数据就开始感受这种顿了段文件一多查询延迟从个位数毫秒跳到几百毫秒很不舒服。1.2 比 ES 快的方案到底做了什么减法我测试的这个引擎架构上做了一个很极端的取舍索引全部常驻内存不搞磁盘上的 segment merge不搞跨节点的分布式协调甚至把分词和过滤器的复杂度降到了最低限度。换来的是每个查询都能命中内存索引没有磁盘 IO 抖动没有任何锁竞争导致的长尾延迟。这样设计在数据量可控的场景里表现极其亮眼。举个直观对比同样跑在一台 8C16G 的云主机上ES 建索引后首次查询要经历冷启动加载QPS 稳定后大概几千而这个引擎热数据查询 QPS 能到几万延迟 P99 稳定在 5 毫秒左右。它赢的不是 ES 的极限能力而是把单机性能发挥到了极致。我之所以愿意把它写出来推荐是因为很多人根本不需要 ES 那样的分布式的扩展能力却被 ES 的运维复杂度和资源消耗折磨得够呛。如果能认清这点后面的一切都好办。2. 选型前的自我拷问你到底需要哪种搜索引擎这块特别重要。很多人一上来就问“哪个搜索引擎比 ES 快”但没问“我的场景适不适合换”。如果选错了性能再好也白搭。2.1 什么样的场景适合“轻量高并发引擎”如果你符合下面这几条我强烈建议你试试新一代的轻量搜索引擎数据量在亿级以下日均增量不大比如百万级新增。搜索需求是全文检索、前缀提示、单词匹配、简单过滤排序而不是复杂的嵌套聚合分析。对查询延迟极其敏感比如面向 C 端的实时搜索结果、后台管理系统的快速筛选。不想维护 ES 那套集群、分片、副本、冷热分离的基础设施。像我做的一个电商商品搜索后端SKU 大概 800 万条原来的 ES 集群 3 台机器查询高峰期 CPU 飘到 80%换到这个方案后单机搞定CPU 日常在 20% 以下。这个数据是我压测后真实记录的不是凭空编的。2.2 什么样的场景千万别换反过来如果你的数据量已经到几十亿或者需要做嵌套聚合、地理位置搜索、数据仓库层面的 OLAP 分析那别折腾了老老实实用 ES。ES 的分布式计算能力尤其在聚合分析上单机轻量引擎完全无法替代。而且轻量引擎的多租户、权限体系多数不如 ES 完善如果你要做复杂的数据权限隔离实现成本会变高。我的经验是技术选型前先拿数据量和查询类型做个简单评估表把自己最核心的三个搜索需求列出来逐项打分再决定要不要切换。3. 实战部署从零搭建一个比 ES 快的搜索引擎理论聊完上干货。我以当前社区热度最高的轻量搜索引擎之一Typesense为例因为它在并发和延迟上表现最均衡而且部署非常便捷。当然还有一个老牌的MeiliSearch也很优秀但我在实际测试中 Typesense 的过滤和分组能力对我的电商场景更顺手。下面我完整复盘一遍我的搭建和调优过程。3.1 单机安装与启动Typesense 官方提供了非常干净的二进制包和 Docker 镜像。我这里用 Docker 起一个最简单的单实例docker run -p 8108:8108 -v/tmp/typesense-data:/data typesense/typesense:27.0 \ --data-dir /data \ --api-keyYourSup3rSecretKey \ --listen-port 8108需要注意几个关键参数--api-key是访问 API 的唯一密钥务必设置强密码否则等于把数据裸奔在公网上。--data-dir要落实到磁盘否则容器重启数据全丢。默认只监听本机生产环境最好再加一个--listen-address 0.0.0.0或者前置 Nginx 做 TLS 终止保证接口安全。启动日志打印出listening on 0.0.0.0:8108之后就说明起来了。就这么简单没有 ES 那套discovery.seed_hosts、cluster.initial_master_nodes的繁琐配置。3.2 创建索引的思路Typesense 里的集合对应 ES 的索引。创建之前一定要想清楚哪些字段需要搜索、哪些字段需要过滤或排序这对内存占用和性能影响很大。以商品搜索为例我创建了这样一个 schema{ name: products, fields: [ {name: title, type: string}, {name: description, type: string}, {name: brand, type: string, facet: true}, {name: price, type: int32, sort: true}, {name: stock, type: int32, sort: true}, {name: category_id, type: int32, filter: true} ], default_sorting_field: stock }这里有几个容易踩的坑facet: true会额外生成聚合数据字段越多内存占用越高不要给所有字段都开。default_sorting_field必须设置否则部分搜索请求会报错。而且这个字段最好选数值型选文本型的话性能会很难看。如果你不确定字段是否要用作过滤先不要给filter: true后续可以动态加但越少越好在初始化阶段就规划好。我当时就是一开始把几十个字段全开了facet结果 800 万数据直接吃了 12GB 内存后来砍掉大部分不需要聚合的字段才降到 6GB 左右速度反而还快了一截。3.3 写入数据的性能实测索引创建好之后我开始导数据。Typesense 提供 JSON Lines 批量接口批量导入比单条插入爽太多curl -X POST http://localhost:8108/collections/products/import?actionupsert \ -H X-TYPESENSE-API-KEY: YourSup3rSecretKey \ --data-binary products.jsonl一条 JSONL 一行代表一个文档我本地生成了 800 万行商品数据单文件大概 1.2GB。用内网传输方式导入大概花了 6 分钟完成全部导入均摊下来每秒写入量接近 2.2 万条。这个速度在同配置的 ES 上我实测大概只有每秒 3000 到 5000 条高下立判。导入过程中还观察到 CPU 和内存都比较平稳没有出现 ES 那样导入时段合并导致的 CPU 飙高。这个体验很关键对我这种应用服务器和搜索服务同机部署的玩家来说稳定压倒一切。3.4 查询语法与调优笔记Typesense 的搜索语法极其简单一个search端点就能覆盖绝大多数场景。比如curl -H X-TYPESENSE-API-KEY: YourSup3rSecretKey \ http://localhost:8108/collections/products/documents/search?q手机query_bytitlefilter_bycategory_id:12sort_byprice:descper_page20注意几个细节query_by指定要搜索的字段可以逗号分隔多个字段但是字段越多查询越慢因为这个引擎要逐字段扫索引。filter_by对于枚举型字段类目、品牌、状态非常高效因为底层是位图过滤不会造成全表扫描。sort_by字段必须在 schema 中标记过sort: true不然查询会一直报错。我调优之后title上面做搜索category_id过滤price排序的混合查询P99 延迟基本没超过 10 毫秒。这个数据和我用 ES 做同样查询时的 50 到 100 毫秒延迟比起来说一句“快 5 倍”都是保守的。4. 压测实录在同样机器上硬刚了一轮 ES光说快不行得给数据。我特意在相同配置的机器上做了一轮公平对比把整个压测过程记录下来方便你自己复现判断。4.1 压测环境与数据说明机器8 核 CPU、16GB 内存、SSD 磁盘。数据800 万条商品数据单条平均 150 字节。查询类型关键词前缀搜索 类目过滤 价格排序。压测工具wrk并发 100持续 5 分钟。ES 侧我配了 1 个节点、默认 5 个分片 1 个副本mapping 里对 title 做了 standard analyzer。Typesense 就是上一节讲到的单实例配置。4.2 直接摆数据延迟与 QPS 对比压测结果出来差距非常明显指标TypesenseElasticsearchQPS每秒查询数310005700P50 延迟1.8 ms22 msP99 延迟4.6 ms85 ms压测期间 CPU 平均占用45%72%我把这个结果发给团队里的后端同事看他第一反应是“ES 是不是没调优”。说实话我确实没有精细调 ES 的堆外内存和线程池但这也正是问题所在——ES 默认配置下就是这么个表现而 Typesense 开箱即用就是这么快。对于中小团队来说不需要调优本身就是一种巨大优势。4.3 为什么差距会拉得这么开背后的逻辑不复杂。Typesense 对每个查询都走内存索引并且它的底层使用了大量 SIMD 指令集的优化让 CPU 能在一个时钟周期内处理更多数据点而 ES 则需要把请求分发到分片各分片执行完以后再归并结果这层网络开销和协调开销在任何规模下都逃不掉。简单类比下ES 是开着一支标准化车队运货每辆车都要过磅、登记、护航适合超大物流网络Typesense 则是让几个经验丰富的老司机骑摩托车直接送到门口速度快到飞起但装载量有限。选哪个取决于你手里货有多少以及对速度有多敏感。5. 生产环境落地时这几个坑一定要避开在你的开发环境跑通展示是第一步真正部署到生产环境不出事才算真的会用了。我在这个过程中踩了不少坑捡几个印象最深的记录一下。5.1 内存管理不是越大越好很多人以为给 Typesense 分的内存越多就越快其实不然。它有自己的一套索引缓存机制数据量太大时会把索引刷到磁盘称为“溢出”。一旦索引溢出查询速度会断崖式下降从几毫秒掉到几百毫秒。这里有个实际的估算经验索引占用内存大约为原始数据大小的 1.5 到 2 倍。我这 800 万条数据原始 1.2GB索引在内存里放了大概 2.3GB。所以做容量规划时你要把业务数据增长曲线考虑进去别只看眼前数据量。如果预计半年后数据量翻倍那现在就让内存留有 30% 余量别刚好卡在临界点上。5.2 中文搜索要提前设计好词库Typesense 默认的分词器对英文非常友好按空格和标点切分即可。但中文没有空格直接搜“智能手机”如果不做处理会把整段文字当成一个 token导致你搜“手机”匹配不到。我在生产环境里处理的方案是写入前在应用层用 jieba 分词把分词结果拼接成空格分隔的字符串再存到专门的搜索字段里搜索时同样做一次分词查询。这样虽然多了一步预处理但换来了可控的中文搜索效果。这步一定要在索引创建前就规划好否则上线后想改分词方案要全量重建索引非常痛苦。5.3 高可用别指望单节点裸奔轻量引擎单机性能再强也扛不住机房断电和磁盘故障。我们线上至少得部署主备两个实例定时做数据快照。Typesense 的快照接口也很直接curl -H X-TYPESENSE-API-KEY: YourSup3rSecretKey \ http://localhost:8108/operations/snapshot?snapshot_path/data/snapshot建议每天全量快照一次同时开启 append-only 的日志记录作为增量补录。切换时直接把快照目录恢复到新节点启动流程比 ES 的跨节点数据恢复要清爽得多。6. 集成过程中的常见报错与排查实录部署和接入过程中我整理了一份常见问题清单每个问题都对应一个实际解决办法贴出来给大家参考。6.1 连接被拒绝或超时最常见的是服务启动时本机 curl 正常但应用服务器连接超时。八成是防火墙没开端口或者 Typesense 监听地址绑定在了 127.0.0.1。用netstat -tlnp | grep 8108看一眼监听地址如果是127.0.0.1就得在启动参数里加--listen-address 0.0.0.0并重启服务。6.2 查询时提示字段不存在这个多半是 schema 建好后你又换了字段名或者查询里用了索引创建时没有声明的新字段。Typesense 不会像 ES 那样自动动态映射它对 schema 的强约束既是优点也是束缚。解决方式是去集合 schema 里确认字段定义缺什么就补什么但注意修改 schema 后可能需要重建索引才能生效。6.3 内存增长过快指标异常导入大批量数据时内存一路狂飙不回落多数是 batch 导入的并发过高。我用了import接口它默认一次解析整批 JSONL单批太大就会导致内存峰值暴涨。处理方法是把批量导入文件拆成每批 5 万条左右一条一条调太快一次几百万条又太猛平衡下来这个量级我在生产环境里跑了两周没有异常。6.4 搜索排序和预期不符默认的排序逻辑是按text_match分数降序但实际上很多时候它是按default_sorting_field兜底。如果你业务上希望某些商品置顶、某些品牌加权需要在 schema 里专门设计一个权重字段在导入时就把权重算好。例如设置sort: true的一个rank_score字段查询时sort_byrank_score:desc就能精准控制排序结果。6.5 官方文档没写明白的事我在调试时发现一个隐藏行为query_by多个字段时Typesense 默认会对每个字段单独算分再合并但对于空值字段有搜索时会出现评分跳变导致相同排序规则下结果不稳定。解决办法是搜索字段尽量选那些非空率高的字段或者用query_by_weights把空值字段的权重调低。以上这些我都逐一验证过现在把它们整理成速查表放在下面内核团队如果有人接手也不至于从头踩坑。问题原因解决办法连接超时监听地址或防火墙绑定 0.0.0.0放行端口字段不存在schema 未声明更新集合 schema内存飙升单批导入过大每批控制在 5 万条排序不符合预期缺少权重字段引入 rank_score 数值字段中文搜不到分词策略问题应用层分词后拼接索引7. 该项目还能怎么扩展以及我最后想啰嗦一句的事如果只把它当成“ES 的平替”就太可惜了。我后续打算把订单搜索和用户行为搜索也都迁到这个引擎上因为它对高并发小延迟的查询支持实在是顺手。而且它支持内置的 API 密钥管理一个实例开多个集合天然就能当做一个轻量搜索中台用。个人做自动化部署时如果玩的是云主机那种环境我建议直接用 Docker Compose 把 Typesense 和你的应用服务编排在一套配置里再挂个 Nginx 做 API 网关整体结构干净也容易迁移。数据备份坚持“快照 日志”双写遇到机械故障恢复起来很快。最后按惯例补一句操作性心得任何搜索引擎最终都要回归到业务场景不要盲目追新。我在换这个引擎之前也担心“比 ES 快 5 倍”是不是噱头但拿真实数据压测一轮之后心里就踏实了。如果你正好也在为 ES 的查询延迟发愁不妨花半天时间搭个最小实例导入十万条数据真正感受一下内存级索引和磁盘级索引的差别——那种查询返回的速度确实是会上瘾的。
返回列表