资讯动态

Elasticsearch从倒排索引到分片副本:原理、架构与选型

发布时间:2026/10/26 20:32:21 来源:尧图企业网站定制
简介《Elasticsearch 演讲PPT》面向希望系统了解 Elasticsearch 的技术人员、架构师及团队内部分享者围绕全文检索、实时搜索与数据分析场景梳理从基础概念到应用实践的知识框架适合初学者建立术语体系也便于有经验的技术人员整理分享材料。演示内容从 Lucene 基础、分布式架构与 RESTful 接口讲起依次覆盖集群、节点、索引、文档、分片和副本等核心概念并延伸到字段数据类型、Mapping 映射、索引模板与别名等数据管理机制。讲解中还结合倒排索引原理、中文分词器选择、TF-IDF 打分以及 ES 与 MySQL 在检索响应时间上的对比帮助读者理解其性能优势与适用边界。资源包共 1 个 pptx 文件约 3.21MB结构紧凑适合直接用于技术分享、培训课件或自学提纲已有 223 人学习可作为快速建立 ES 知识地图、准备演讲与团队交流的参考材料对准备内部分享或培训课件的读者较友好。1. 到底什么是 Elasticsearch从「搜三个字页面转到人事不省」说起几乎每个做过查询功能的人都干过同一件事接到「加个搜索框」的需求第一反应是打开数据库写一句LIKE %关键词%按下回车然后看着页面转圈。几万条数据时它还挺听话几十万条开始喘上百万条就足够让人起身去泡杯茶。Elasticsearch圈内一般直接叫 ES就是来解决这个尴尬的。它是一个基于 Lucene 构建的开源分布式全文搜索引擎用 Java 写成对外用 RESTful 接口说话能在多台机器上协同工作做到近实时的存储、搜索与分析。通俗地说它不是「一个更大的数据库」而是一套把「找词」这件事做到极致的检索与分析平台——你喂给它一堆 JSON 文档它回你毫秒级的答案。搞懂它你才真正搞懂了今天大部分搜索框背后那台发动机。2. 倒排索引凭什么这么快把「逐本翻书」换成「先查目录」一次查询从敲下回车到结果返回中间隔着一段很短的旅程。这段旅程的长短取决于一件很朴素的事系统是「挨个翻」还是「查目录」。这一章就把这条链路拆开看看完你会明白为什么同样是搜一个词有的系统要三分钟有的只要几十毫秒。2.1 正排与倒排同一批数据两种查找姿势假设手上有两篇文档文档 1某信息技术公司文档 2某环境咨询公司如果数据是按「文档 → 文档里有哪些词」的方式存的这叫正排。要判断某篇文档里有没有「信息」这个词只能把每篇文档的词表读一遍读完再比对。数据量越大要读的越多耗时几乎是线性往上涨的。用数据库的模糊匹配去搜一个词走的差不多就是这个路子前面的%让索引失效只能一条条扫。倒排索引把关系颠倒过来存的是「词 → 哪些文档里有它」。这时候再搜「信息」系统不用碰任何一篇原文直接去词典里找到「信息」这个词取出它后面挂着的一串文档编号答案就出来了。类比一下一本厚厚的工具书如果书末没有索引你想找某个名词只能一页页翻如果书末有名词索引写着「某某 P123、P456」翻两页就到位。倒排索引就是给整个文档集合配了一本这样的书末索引。它通常由两部分构成单词词典记录所有出现过的词倒排列表记录每个词出现在哪些文档里、出现几次、在什么位置。搜索「信息」这个动作在内部就是「查词典拿列表 → 多个词的列表求交集 → 按编号取回文档」。这里有个关键细节倒排列表一般按文档编号有序存放两个有序列表求交集代价跟命中多少条相关而跟整个集合有多少条几乎无关。所以在十亿级的数据里搜一个冷门词依然是几十毫秒的事。注意这是一条简化过的链路。真实的实现里还有词典的压缩结构、跳表、列表压缩等一堆优化但方向就是这个方向。2.2 分词倒排索引的第一道工序也是中文搜索最容易翻车的地方倒排索引的前提是「先有词」。而中文没有天然的空格分隔一句话怎么切成词直接决定了它能被怎么搜到。还是刚才那两篇文档换两种切法结果完全不同分词策略文档 1 切出来的词条文档 2 切出来的词条搜「信息」的命中细粒度切分某 / 信息 / 技术 / 公司某 / 环境 / 咨询 / 公司命中文档 1整串当成一个词某信息技术公司某环境咨询公司一条都不命中第二种切法并不是瞎编很多人配置分词的第一个跟头就栽在这字段用错了类型比如该用全文类型的地方用了精确类型或者分词器压根没装结果建索引时整句话被当成一个词条存进去查询时再按词去搜自然对不上。这就是那种「数据明明在就是搜不出来」的玄学问题。常见的中文分词器有 IK、ansj、jieba、hanLP 等各有取舍。IK 生态最广提供粗粒度和细粒度两种切法适合大多数通用场景ansj 在人名识别和新词识别上更下功夫语料里专有名词、人名多的时候更有优势其余几个也各有拥趸。选型没有标准答案取决于你的数据长什么样——一份满是企业全称的名录和一份满是用户昵称的评论最优的切法本来就不同。还有一个高频踩坑点建索引时用的分词器和查询时用的分词器必须对得上。有的系统给字段配了细粒度分词建索引查询时却用了粗粒度或者反过来结果就是「分词切出来的词条」和「查询拆出来的词条」永远碰不上面。字段级别的analyzer和search_analyzer就是干这个的配错一个前面所有的优化都白搭。2.3 TF-IDF命中了之后凭什么它排第一搜「信息」可能一下命中八千篇文档。谁排前面这就轮到打分算法上场了。TF-IDF 是信息检索里最经典的一套思路拆开看就两个量TF词频某个词在这篇文档里出现了多少次。出现得越多说明这篇文档越可能就是在讲这个词。IDF逆文档频率某个词在整个文档集合里有多罕见。越罕见的词区分度越高权重越大。两者相乘就是这个词对这篇文档的重要程度。举个直观的例子「信息」这个词在某篇文档里出现了 5 次在另一篇里一次没出现前者的得分自然更高。但「公司」这个词如果在几乎每篇文档里都出现它的 IDF 就会低得可怜——因为它区分不出任何东西。这也解释了为什么搜索系统几乎从不把「的」「是」「公司」这类高频词当回事它们的信息量接近于零。除了 TF-IDF成熟的搜索系统还会叠加别的信号来决定顺序比如基于链接关系的分析方法用来判断一篇内容被多少其他地方引用、权威程度如何。如今的 ES 默认使用的是一种由 TF-IDF 改良而来的相似度算法BM25它对词频的增益做了饱和处理——一个词出现 10 次和出现 100 次得分差距不会夸张到 10 倍更符合人的直觉。但不管怎么改良底层逻辑没变先靠倒排索引找得快再靠打分算法排得准这是两件不同的事很多人把它们混成一件事。2.4 一次模糊匹配的性能对照为什么数据量越大差距越夸张原始演讲资料里给过一组对照测的是模糊匹配的响应时间只列趋势不必纠结具体数字背后的机器配置数据规模传统数据库模糊查询ES 查询10 万条0.2 秒0.008 秒100 万条2.2 秒0.013 秒1000 万条193.1 秒0.028 秒1 亿条未能完成测试0.066 秒10 亿条——0.1 秒以内看这张表要抓的不是「快了多少倍」而是两条曲线的形状。传统数据库那条是往右上角猛蹿的因为模糊匹配让索引失效数据翻十倍扫描量就翻十倍ES 那条几乎是贴着地面的一条平线因为查询代价主要取决于「命中多少个文档」而不是「总共有多少个文档」。数据越多这个差距越离谱——百万级还能忍千万级就是几分钟和几十毫秒的区别。但也别只盯着读的那一列。ES 为了这份读性能付出了写入更重、存储更多、运维更复杂的代价。它把一份数据切开放到多台机器上还要额外存副本这些都不是免费的。看到「快 7000 倍」就冲动地把主库换成 ES是另一个更贵的坑后面会专门讲。3. 集群、节点、索引、分片、副本一套「图书馆」式的组织架构搞懂了「快在哪」接下来要搞懂「怎么摆」。ES 的术语听起来唬人其实全都可以映射到一个图书馆的日常运作上一个馆装不下就开分馆一套书印成几册再各复印一份读者多了就分流。理清这套骨架你才知道建索引时那几个数字到底在调什么。3.1 集群与节点一个馆装不下就开分馆单台服务器跑一个 ES 进程这叫一个节点本身就能对外提供服务。但数据一多、或者要求「一台机器挂了服务不能停」就得让多台机器连起来协同工作这些相互合作的服务器集合叫集群。集群里的节点分工不一样。有负责管理元数据的比如索引建没建、分片放在哪台机器上有专门存数据、扛查询的。类比一下总馆长不负责搬书他只决定哪一批书放哪个分馆、哪个书架真正找书的是各馆的馆员。集群靠一个共同的集群名字来认亲节点启动时发现彼此、加入同一个集群这个过程是自动的。这里有个容易被忽略的点不是节点越多就越快。节点之间要通信查询要分发到多个分片再汇总协调的活儿本身有成本。三五台机器能扛住的量硬堆到三十台收益可能还不如把分片数调对。3.2 索引与文档它像表但只是「像」索引是一堆结构相似的文档的集合很多资料直接说「相当于数据库里的表」文档是能被索引的最小信息单元一条 JSON 数据相当于表里的一行。这个类比能帮你快速上手但它的边界比想象中窄。第一个差别ES 没有强制的表结构约束。字段类型可以预先定义也可以让它自动猜猜出来的结果常常不是你想要的。第二个差别文档是不可变的所谓「更新一条文档」底层是把旧的那条标记删除、再写一条新的进去。所以频繁修改同一条数据的场景用 ES 会很别扭。第三个差别索引之间基本没有 join 的概念你没法像写 SQL 那样随手把两张表连起来查需要提前把数据「拍平」成一份宽文档。提示索引名只能用英文小写不能有大写字母这条限制劝退过不少第一次建索引的人。3.3 分片与副本切开来算得快复制一份才不怕丢一个索引如果只存在一台机器的一个目录里数据量一旦超过单机磁盘就彻底没辙了。分片解决的就是这个问题索引被水平切成若干份每一份是一个独立的、完整的底层索引结构可以放在不同机器上。查询时多个分片并行干活最后把结果汇总。切开来既是扩容也是提速。副本是分片的拷贝一个分片可以有零个或多个副本。它的作用有两个一是容错某台机器挂了副本顶上数据不丢、服务不断二是分担读请求同一份数据有多个拷贝读的人多时可以分流到不同拷贝上去。类比一下一套三册的百科全书读者多的时候你把它拆成三册分别放在三个阅览室找起来更快分片再给每一册各复印一份放到别的馆万一某个馆漏水书还在而且两个人同时要查同一册也不用排队副本。这里藏着 ES 建索引时最贵的一个决定主分片数在索引创建时就定死了之后改不了。想改只能新建一个索引再把数据搬过去。副本数倒是随时可以调。所以第一次建索引时按「未来两三年的数据量」来估主分片数是必须做的一次功课随手写个默认值将来就是一场彻夜的数据迁移。3.4 五个概念的层级关系概念图书馆里的对应物一句话说明建好后能否改集群整个图书馆系统多台服务器协同工作的集合名称固定节点可增删节点单个分馆集群里的一台服务器可随时加入或退出索引某一类藏书的目录相似文档的集合可删可建字段类型难改分片一套书拆成的几册索引的水平切分可分布到不同节点主分片数创建后不可改副本每册书的复印件分片的拷贝用于容错和分担读随时可调文档一本书一条 JSON 数据可增删改改即重建4. 数据类型与 Mapping为什么字段类型要在建索引前想清楚前面讲的都是「怎么找得快」这一章讲「怎么存才对」。字段类型选错是那种当时毫无感觉、三个月后才爆发的错误——数据进了几千万条才发现某个字段被识别成了文本改类型要重建整个索引。这种后悔药最好提前吃。4.1 基础类型与复杂类型ES 里到底有哪些「格子」ES 的字段类型大致可以分成四类基础类型是主力。字符串分两种这点特别重要精确类型用于不分词的完整匹配编号、状态、标签全文类型用于需要分词的搜索标题、正文、备注。数值类型有一整套从单字节的到双精度浮点的都有还有一个专门的缩放浮点类型用整数加缩放因子的方式存小数适合金额这种需要精确计算的场景。此外还有日期、布尔、二进制以及区间类型比如表示「某个数值落在某个范围内」。复杂类型用来表达嵌套结构。数组类型其实不需要单独声明任何字段默认都能存多个值对象类型用于嵌套的 JSON 结构嵌套类型则是为了解决对象数组在查询时的「跨对象误匹配」问题——这个坑很隐蔽普通对象数组在底层会被拍平成一组平行字段查「A 是 1 且 B 是 2」时可能命中一条 A 和 B 来自不同对象的记录。特定类型是为专门场景准备的地理坐标、IP 地址、自动补全、父子关系等。选型时最常出错的是字符串的两种类型。记住一条判断标准这个字段需要被「搜」还是被「精确匹配」。商品标题要搜用全文订单号要精确匹配用精确类型。把订单号设成全文类型搜「1001」时会连「10010」「21001」一起捞出来业务方第二天就会来找你。4.2 Mapping 映射预先定义还是让系统自己猜Mapping 就是字段类型和相关属性的定义。建索引时你可以显式写上也可以什么都不写让系统看到第一条数据时自动推断。自动推断听起来省事但它是典型的前期舒服、后期要命。带前导零的编号比如00123很可能被当成文本日期如果格式不常见也可能识别不对字符串默认常常被同时映射成「可分词 精确」两个子字段索引体积直接翻倍。更麻烦的是一旦某个字段被推断成文本你想按数值范围去筛就没法用了。显式 Mapping 的写法很直白字段名、类型、要不要索引、用什么分词器。前面提到的分词器配置也在这儿落地。要记住的是大部分字段类型一旦确定就不能直接修改唯一的正规路子是新建一个索引、把老数据重新灌一遍。所以在数据量还小的时候把 Mapping 定好比什么都划算。4.3 索引模板与索引别名给未来的自己留一颗后悔药索引模板是给「还没建出来的索引」准备的。你写一条规则比如「凡是名字以 order- 开头的索引都自动套用这套分片数、这套字段映射、这套分词器」之后新建索引时不用再重复交代一遍。按时间分索引的场景每月一个、每天一个几乎是靠模板撑起来的手写必错。索引别名则是一层指向。别名可以指向一个索引也可以指向多个索引还可以带过滤条件。它最大的价值是让业务代码不认死某个具体的索引名。举例来说你要重建索引正常的操作是建新索引 → 灌数据 → 把别名从老索引切到新索引 → 删老索引。整个过程业务代码一行没改用户毫无感知。没有别名这一步就得停机改配置。4.4 一段能看懂的配置示例下面这段是给一个订单类索引准备的模板用命令行方式提交注释写在每行前面# 注册一个索引模板凡是名字以 order- 开头的索引都自动套用下面的设置和映射 curl -X PUT http://localhost:9200/_index_template/order_tpl \ -H Content-Type: application/json -d { index_patterns: [order-*], template: { settings: { number_of_shards: 3, number_of_replicas: 1 }, mappings: { properties: { order_no: { type: keyword }, remark: { type: text, analyzer: ik_max_word }, amount: { type: scaled_float, scaling_factor: 100 }, created_at: { type: date } } } } }逐项说清楚index_patterns决定这条模板管哪些索引用的是通配符匹配number_of_shards是主分片数创建后不能改要按未来的数据量估number_of_replicas是每个分片的副本数随时可调order_no用了精确类型因为它要的是「一字不差地匹配」remark用了全文类型并指定中文分词器因为它要的是「搜得到」amount用了缩放浮点把小数乘 100 存成整数避免浮点误差created_at声明成日期这样范围查询和按时间排序才走得动。接着给索引挂一个别名让业务只认这一个名字# 把别名 order_search 指向当前的 order-2024 索引 # 将来重建索引时只需要把这条 add 换成指向新索引业务代码无需改动 curl -X POST http://localhost:9200/_aliases \ -H Content-Type: application/json -d { actions: [ { add: { index: order-2024, alias: order_search } } ] }两个要点一是别名要指向「当前正在服务的那一个」切换时先把新索引的数据备齐再一次性把别名挪过去中间不留空窗二是如果用了带过滤条件的别名同一份数据可以给不同业务看到不同的子集这在多租户场景里非常好用。中文分词器需要先装好对应插件否则上面那段会因为找不到分词器直接报错——这也是部署时最常见的第一个错误。5. 六个最容易搞混的概念索引不是表分片也不是副本学 ES 的时候最大的障碍往往不是难而是「几个词长得太像」。下面这几组是我见过被问得最多、也最容易在方案评审会上吵起来的。5.1 索引 vs 数据库表像但不能照着表的思路用易混点几乎所有入门材料都说「索引相当于表」于是有人把原来那套表结构原封不动搬过来一列一列照抄。为什么混这个类比在「存一类结构相似的数据」这件事上确实成立理解到这个层面已经够用了所以大家就停在这儿了。怎么区分表有强 schema、有事务、有外键、能 join索引没有这些。索引里的一条文档是自包含的 JSON你查一条订单想把下单人的昵称也带出来正确做法是在写入时就把昵称冗余进去而不是查询时再去连另一张「表」。另外索引的文档不可变更新等于重建这一点决定了它不适合高频修改的场景。把它当「表」来建模早晚要在关联查询上撞墙。5.2 分片 vs 副本一个是「切开」一个是「抄一份」易混点都跟「一份数据变成好几份」有关看监控面板时一片数字分不清哪个涨了会怎样。为什么混两者经常同时出现一个索引有 3 个主分片、每个 1 个副本界面上就是 6 个分片看起来像同一件事的两个说法。怎么区分分片是把数据切开每片装的是不同的数据合起来才是全部副本是把分片复制每份装的是相同的数据。分片数影响的是「一份数据能被切成几块并行处理」副本数影响的是「同一块数据有几个备份、能扛几次机器故障」。所以主分片数建完就改不了副本数随时可调。机器加多了想提升吞吐调的是副本数据涨了装不下那才轮到分片而且只能重建。5.3 Mapping vs 索引模板 vs 索引别名三个都带「索引」各管一段易混点三个词都跟索引有关配置的时候常常被写在同一个文件里看起来很像是同一层东西。为什么混它们确实经常一起出现模板里就包含了 Mapping模板里也能顺带定义别名。怎么区分Mapping 管的是「这个索引里的字段长什么样」索引模板管的是「将来新建的索引自动长成什么样」索引别名管的是「对外暴露的那个名字指向谁」。一个是字段级定义一个是创建时的自动化规则一个是查询时的间接层。出问题时看现象就能定位字段类型不对是 Mapping 的事新索引没套上配置是模板没匹配上切了索引之后查询报错多半是别名没挪。5.4 精确类型 vs 全文类型同一句话两种存法易混点都是字符串都存文本看起来只是「类型名字不同」。为什么混很多字段两种都能存建索引时不报错所以没人深究直到某天搜索行为变得莫名其妙。怎么区分精确类型不分词整串原样存适合编号、状态、标签、枚举值查询时必须一模一样全文类型会分词切成一个个词条适合标题、正文、备注查询时按词去匹配。判断标准就一句这个字段是给人「搜」的还是给程序「对」的。给程序对的用了全文类型会捞出一堆不该有的结果给人搜的用了精确类型用户少打一个字就搜不到。5.5 搜索引擎 vs 数据库不是替代是分工易混点既然 ES 查询快这么多那为什么还要数据库直接把数据都塞进 ES 不就行了。为什么混前面那张性能对照表太有冲击力容易让人产生「新的一定全面更好」的错觉。怎么区分数据库保证的是「这条数据一定在、一定对、改完立刻生效」也就是事务和一致性ES 保证的是「找得快、排得准、扛得住大并发读」。前者是账本后者是目录。目录丢了可以重建账本丢了就是事故。所以常见的架构是数据库当唯一可信源ES 当检索层写入时同步过去查询时从 ES 拿结果需要强一致的操作用主键回数据库确认。把 ES 当主库用是在拿目录当账本。5.6 分词器 vs 相似度算法一个决定「能不能搜到」一个决定「谁排前面」易混点都在搜索链路上出了问题都表现为「搜出来的结果不对」。为什么混用户反馈「搜不到」和「搜出来顺序怪」在现象上离得很近。怎么区分分词器在写入和查询两端工作决定的是词条切分它出问题表现为完全搜不到或者搜出一堆不相关的相似度算法只在打分阶段起作用它出问题表现为该排前面的排后面了。排查顺序也该是这个顺序先确认分词对得上再去看打分。分词都没对上就调打分参数纯属白费力气。6. 什么时候该上什么时候别硬上一次取舍的复盘说个虚构的场景。某团队做企业名录检索数据量在千万级用户在搜索框里输入一个公司名的片段要求秒级返回。最初用的是数据库模糊匹配百万级还能忍到千万级直接几分钟起步页面只能挂个转圈动画。换成 ES 之后同样的查询稳定在几十毫秒。他们的数据有个特点每天批量导入一次白天只读不写读远远多于写。这个特点恰好是 ES 最舒服的姿势。反过来如果数据是一天改几十万次的订单状态每次改动都要立刻对其他系统可见还要保证「扣了库存就一定不能重复扣」那就别把 ES 当主存储。它没有跨文档事务更新是「删了重写」近实时也意味着写入到可见之间有一小段延迟。这种场景的正确姿势是主库照旧ES 只做检索层。判断的边界可以归纳成几个问题你的场景建议原因全文检索、模糊匹配、多条件组合筛选用它倒排索引的主场数据越大优势越明显日志、指标类的海量写入与聚合分析用它天然分布式按时间分索引后查询和清理都方便需要事务、强一致、频繁更新的核心账务别当主库无跨文档事务更新成本高复杂的多表关联查询慎用没有 join只能靠写入时冗余宽表数据量很小、查询简单未必值得引入一套分布式系统运维成本远大于收益我自己有过一次教训一个内部小工具总共几万条数据查询条件就一个主键我图省事接了 ES结果为了它多维护一套集群、多写一套同步逻辑、多排查一类「数据不一致」的问题收益几乎为零。从那以后每次有人提「上 ES」我都会强制先问三个问题数据量真的到瓶颈了吗读写比例是什么样这份数据丢了能不能重建三个问题里有一个答不上来就先别动手。这份 Elasticsearch 演讲资料把这些东西用图串成了一条线——从倒排索引的单词词典和倒排列表到分词策略的选择到 TF-IDF 怎么打分再到集群、分片、副本、Mapping、模板、别名怎么摆最后落在应用场景和常见提问上。自己零散搜十篇帖子拼出来的认知和顺着一条线走一遍密度是不一样的。拿去给团队做一次内部分享或者自己过一遍打底都合适。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑