搜索引擎

Lucene 和 ES 的前世今生

Lucene 是最先进、功能最强大的搜索库。如果直接基于 Lucene 开发,非常复杂,即便写一些简单的功能,也要写大量的 Java 代码,需要深入理解原理。

ElasticSearch 基于 Lucene,隐藏了 lucene 的复杂性,提供了简单易用的 RESTful api / Java api 接口(另外还有其他语言的 api 接口)。

  • 分布式的文档存储引擎
  • 分布式的搜索引擎和分析引擎
  • 分布式,支持 PB 级数据

如果把搜索引擎比作一辆赛车,那么 Lucene 就是这辆车的引擎(核心动力,复杂、裸露),而 Elasticsearch 就是整车(包含了引擎,并且装配了方向盘、仪表盘、车轮,让你直接能开)。

ES 的核心概念

Near Realtime

近实时,有两层意思:

  • 从写入数据到数据可以被搜索到有一个小延迟(大概是 1s)
  • 基于 ES 执行搜索和分析可以达到秒级

为什么是 1 秒?因为数据写入 ES 后,先进入内存 Buffer,此时还搜不到。ES 默认每隔 1 秒执行一次 Refresh 操作,把 Buffer 里的数据生成一个新的 Segment(段),这时数据才能被索引到。这 1 秒就是 refresh_interval。

Cluster 集群

集群包含多个节点,每个节点属于哪个集群都是通过一个配置来决定的,对于中小型应用来说,刚开始一个集群就一个节点很正常。

Node 节点

Node 是集群中的一个节点,节点也有一个名称,默认是随机分配的。默认节点会去加入一个名称为 elasticsearch 的集群。如果直接启动一堆节点,那么它们会自动组成一个 elasticsearch 集群,当然一个节点也可以组成 elasticsearch 集群。

Document & field

文档是 ES 中最小的数据单元,一个 document 可以是一条客户数据、一条商品分类数据、一条订单数据,通常用 json 数据结构来表示。每个Index下可以存储多条document。一个 document 里面有多个 field,每个 field 就是一个数据字段。

1
2
3
4
5
6
7
{
"product_id": "1",
"product_name": "iPhone X",
"product_desc": "苹果手机",
"category_id": "2",
"category_name": "电子产品"
}

Index

索引包含了一堆有相似结构的文档数据,比如商品索引。一个索引包含很多 document,一个索引就代表了一类相似或者相同的 document。

Type

(在现在的 Elasticsearch(7.x 和 8.x 版本)开发中,Type 已经被废弃并移除。)

类型,每个索引里可以有一个或者多个 type,type 是 index 的一个逻辑分类,比如商品 index 下有多个 type:日化商品 type、电器商品 type、生鲜商品 type。每个 type 下的 document 的 field 可能不太一样。

类比

ES Cluster (集群) ≈≈MySQL Database(数据库实例)

ES Index ≈≈MySQL Table(表)

ES Document ≈≈MySQL Row(行)

Shard (分片)

单台机器无法存储海量数据。ES 类似于把一张巨大的“表”水平切分,切成几块,每一块就是一个 Shard

  • 作用横向扩展。比如你有 3TB 数据,切成 3 个 Shard,每个 Shard 1TB,就可以分散存在 3 台服务器上。
  • 本质每个 Shard 就是一个独立的、功能完整的 Lucene Index

注意:

  • ES 说的 Index(索引): 是一个逻辑概念。比如“商品索引”,它是一个巨大的集合,包含了 1 亿条数据。
  • Lucene 说的 Index(索引): 是一个物理概念。它是硬盘上实实在在的一堆文件,是 Lucene 这个库能够管理的最小单元。

“Shard 是 Lucene Index” 的意思就是:
Elasticsearch 也是个“二道贩子”,它自己并不直接处理落盘的数据,而是把数据切分成一块一块的(Shard),然后每一块都交给一个 Lucene 实例去管理。

通俗类比:图书馆与书架

假设你要建一个**“国家图书馆”(ES Index)**,里面有 1 亿本书。

问题:
如果只用一个房间(一台机器)存这 1 亿本书,房间放不下,而且找书的时候,几万人挤进一个房间查,效率太慢。

解决方案(Sharding):
你决定把这 1 亿本书切分到 5 个分馆(Shard) 去存放。

  • 分馆 1:放编号 0-2000万 的书
  • 分馆 2:放编号 2000万-4000万 的书

重点来了(Shard = Lucene Index):
每个“分馆”,其实就是一个完整、独立的小型图书馆

  • 每个分馆都有自己的管理员(Lucene)
  • 每个分馆都有自己的目录系统(倒排索引文件)
  • 每个分馆都可以独立进行书籍的入库和查询。

当你去搜索时:

  1. 你在这个“国家图书馆”(ES Index)的门口喊一声:“我要找《哈利波特》!”
  2. 总台(ES Node)会把这个命令通过对讲机广播给这 5 个分馆的管理员(Lucene)。
  3. 这 5 个分馆的管理员(Lucene)同时在自己的分馆里找。
  4. 找完后,大家把结果汇总给总台,总台再给你。

技术层面的解释(面试怎么说)

如果在面试中,面试官问到 Shard 的底层原理,你可以这样回答:

  1. Lucene 的局限性: Lucene 是一个 Java 库,它本身是不支持分布式的。它就像一个单机的软件,只能管理本地磁盘上的一组文件。这组文件在 Lucene 的术语里就叫“Index”。
  2. ES 的包装: Elasticsearch 为了实现分布式(让多台机器协同工作),在逻辑上弄了一个“ES Index”的概念,然后把数据切分成多个 Shard
  3. 一一对应关系: 每一个 ES 的 Shard,在物理底层,就是一个完整的 Lucene Index。 这意味着每一个 Shard 都有自己独立的倒排索引文件,都可以独立执行搜索。

为什么要这么设计?(好处)

  • 并行计算:因为每个 Shard 都是一个独立的搜索引擎(Lucene Index),所以当你查 ES 时,这 5 个 Shard 是同时在工作的。速度变成了原来的 5 倍。
  • 水平扩展:因为 Lucene 是独立的,我可以把 Shard 1 放在服务器 A,Shard 2 放在服务器 B……这样就可以无限扩容了。

总结

当你看到 “Shard 是 Lucene Index” 时,你就理解为:
Shard 是一个物理上独立存在的、最小化的、完整的搜索引擎实例。

Replica (副本)

为了防止某个服务器挂了导致 Shard 丢失,ES 允许给 Shard 做备份,这就是 Replica

  • 作用
    1. 高可用:主分片挂了,副本分片马上升级为主分片,保证数据不丢。
    2. 高性能:查询时,可以在主分片和副本分片上同时查,分担查询压力(提升 QPS)。
  • 配置
    • Primary Shard:建立索引时设置,一旦设置一般不能修改(除非重建索引)。
    • Replica Shard:随时可以修改数量。
    • 默认值:ES 7.x 以后,默认是 1 主 1 副(共 2 个分片)。

Q1: 什么是倒排索引(Inverted Index)?

  • 通俗解释:
    • 正排索引(MySQL): 根据 ID 找内容。(ID 1 -> “iPhone X”)
    • 倒排索引(ES): 根据关键词找 ID。(“iPhone” -> ID 1, ID 5, ID 8…)
    • 作用: 它是 ES 搜索快的最核心原因。

Q2: Shard 和 Replica 的区别?

  • Shard(主分片): 负责。数据是切分开存的,为了水平扩展(存更多数据)。
  • Replica(副本分片): 负责(和备份)。数据是完整的拷贝,为了高可用(防宕机)和高并发(读得更快)。

Q3: ES 写入数据和搜索数据的流程是怎样的?

  • 写: 只能写在 Primary Shard,然后同步给 Replica。
  • 读: Primary 和 Replica 都可以读,ES 会自动做负载均衡。

更详细可以查看这个网站中其他系列文章~