作者:禅与计算机程序设计艺术
1.简介
8.Elasticsearch原理与实战是我给《数据库系统概念》第五卷作者赵敏先生的一个专题教程,我会结合自己的学习心得和实际工作经验,用通俗易懂的语言将Elasticsearch的核心概念和实践方法讲清楚,并提供基于Elasticsearch的业务案例,希望能够帮助广大的技术爱好者、开发人员及企业解决实际应用中遇到的各种Elasticsearch的问题。本课程内容包括:Elasticsearch的背景知识、主要特点、安装部署、数据模型、查询语法、集群管理、监控告警、性能调优等方面,另外还会涉及到一些开源组件的原理和配置方法。
Elasticsearch简介
Elasticsearch是一个基于Lucene库的开源搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。相比于其他的搜索引擎,Elasticsearch有如下几个显著特征:
1、全文检索
Elasticsearch基于Apache Lucene library实现,它的优点是快速、高效,并且对中文分词提供了很好的支持;
2、RESTful Web接口
Elasticsearch通过Restful API让用户可以从HTTP请求向服务端发送指令,而不用关心底层实现;
3、分布式多用户能力
Elasticsearch是个开源的分布式搜索引擎,它允许多个节点协同处理搜索请求,可以扩展到上百台服务器,处理PB级的数据量;
4、索引分析器
Elasticsearch支持丰富的索引分析器,例如自定义分词器、边缘 n-gram 分词器等,使得用户可以轻松完成复杂的分析任务;
5、自动生成索引
Elasticsearch支持自动生成索引,可以根据文档内容生成索引映射规则,不需要提前定义;
6、插件机制
Elasticsearch提供了插件机制,第三方开发者可以利用Elasticsearch提供的API快速开发功能模块;
7、可视化界面
Elasticsearch提供了强大的可视化界面,可以直观地查看索引中的文档数量、大小、存储位置等信息;
8、弹性分布式设计
Elasticsearch的集群架构是高度模块化和可扩展的,各个模块之间采用无状态通信,因此节点之间可以进行动态添加或删除;
9、水平扩展性
Elasticsearch支持水平扩展,可以通过增加机器资源来提升系统处理能力,非常适用于海量数据的处理。
总之,Elasticsearch是一个非常强大的搜索引擎,它通过简单的Restful API和丰富的查询语言,帮助用户轻松完成各种复杂的搜索需求,非常值得学习和应用。
Elasticsearch的核心概念
集群(Cluster)
Elasticsearch是分布式的搜索引擎,它由一个或多个服务器节点组成,这些服务器节点构成了一个集群。集群拥有自己的名字,这个名字在创建集群时指定,默认为“elasticsearch”。
节点(Node)
集群中的每个服务器都是一个节点。每个节点都有分配一个唯一标识符。集群中的每个节点都参与到集群的管理和数据分片过程中,并参与到集群内的所有CRUD(Create/Read/Update/Delete)操作。
分片(Shard)
每个索引可以被分为多个分片,每一个分片是一个Lucene的索引。当你插入、更新或者删除数据的时候,这些数据会被路由到对应的分片,然后被分片的副本做数据同步。默认情况下,一个索引包含5个主分片和1个复制分片。
当一个查询需要被执行的时候,ElasticSearch 会把查询语句发送到所有的相关分片,然后再把结果合并成单个结果集返回给客户端。这样做的好处是可以在一定程度上提高查询效率,因为只需要搜索少量分片就可以得到足够准确的结果,而且不用考虑负载均衡等复杂问题。
倒排索引(Inverted Index)
在Elasticsearch 中,倒排索引就是文档字段与其相应的文档 id 的对应关系。倒排索引可以帮助我们快速地检索某个关键字或短语出现在哪些文档中,以及那些文档的评分如何。
Elasticsearch 使用倒排索引建立索引。通过索引中的每个文档,ElasticSearch 可以自动分析出其中的关键词,并为每一个关键词生成一个倒排索引,用来快速查找这些关键词是否存在于文档中。倒排索引是一种树形结构,每个节点代表一个单词,路径表示该单词所在的文档。
举个例子:对于文档 "A quick brown fox jumps over the lazy dog" ,如果使用标准的词法分析方式,则该文档将分割成以下的单词:"A", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"。倒排索引中的每个单词都指向一个包含该单词的文档列表,如图所示:
Key Doc IDs
| / \
A [doc1] []
/ \
o l
/ \
czy ł
上图展示了关键字 "czy" 的倒排索引,它指向包含该关键字的文档列表。左侧的箭头表示指向,右侧的箭头表示指向不存在。
根据图中所示的倒排索引,我们可以快速地判断某个查询词是否存在于某篇文档中。假设我们要找出所有包含关键字 "fox" 或 "dog" 的文档,那么可以查询倒排索引中关于 "fox" 和 "dog" 的节点。如果有一个节点指向了包含关键字 "fox" 或 "dog" 的文档,则该节点的子节点都指向了包含该文档的子文档。如果没有,则节点不存在。反过来,也可以通过遍历所有节点,找到包含这些关键字的文档。这种快速查找的方式称为倒排索引,具有极快的查询速度。
集群状态(Cluster State)
Elasticsearch 集群中的各个节点之间通过集群状态协议来互相通信。集群状态协议中包含集群中所有节点的最新状态信息,包括已启动的节点、未启动的节点、分片分布情况、节点负载、节点健康状况、索引配置、内存使用情况等。
数据存储(Data Storage)
Elasticsearch 将数据保存在磁盘上。每一个分片的主副本和复制品都存放在不同的物理机器上,从而保证高可用性和容错能力。另外,由于每个分片都是一个 Lucene 索引,所以它也具备 Lucene 存储的特性。
默认情况下,Elasticsearch 会在硬盘上维护三个仓库:
- 一个仓库保存持久化数据,包括索引数据、集群元数据、节点配置等;
- 另一个仓库保存快照数据,包括 Lucene 搜索引擎的每一个分片的索引快照;
- 最后一个仓库保存日志文件,包括系统运行时的日志记录。
用户可以选择将这三个仓库放在不同的设备上,以获得最佳的读写性能。
RESTful API
Elasticsearch 提供了基于 HTTP 的 Restful API 来访问和控制集群。它封装了各种操作,如索引创建、数据搜索和删除等,并通过集群状态协议来保持集群的一致性。
通过 Restful API 可以轻松地实现应用之间的集成,比如编写程序或脚本调用 Elasticsearch 服务。同时,通过 Restful API,你可以方便地使用其他的工具进行数据分析、数据可视化、监控告警等。
客户端库
Elasticsearch 提供了各种客户端库,支持多种编程语言,包括 Java、C#、Python、Ruby、PHP 等。通过这些客户端库,你可以方便地与 Elasticsearch 交互,实现各种功能,如索引 CRUD 操作、查询分析、集群管理、数据导入导出、数据统计分析等。
模板(Template)
Elasticsearch 支持模板,你可以根据索引模式创建索引,而不是手动指定每个索引的配置参数。模板可以减少索引的冗余和错误,并帮助自动执行日常任务。
X-Pack
Elasticsearch 提供了名为 X-Pack 的插件,它提供许多额外的特性,如安全认证、角色管理、API 控制、监控、报表等。X-Pack 可以让你快速、轻松地实现各种管理任务,如集群监控、审计、安全管理、用户权限控制等。
Elasticsearch 安装部署
Elasticsearch 有多种安装部署的方法。这里只介绍两种常用的方法:
- Docker 安装
如果你的环境中已经安装了 Docker,可以使用 Docker Hub 中的官方镜像来安装 Elasticsearch。
docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:latest
上面的命令会拉取 Elasticsearch 最新版镜像,并运行一个单节点集群。其中 -p
参数将宿主机端口 9200 和 9300 映射到容器内部端口 9200 和 9300。-e
参数设置集群类型为单节点。
- 本地安装
下载 Elasticsearch 安装包,解压后运行 bin 文件夹下的 elasticsearch
命令即可启动 Elasticsearch 实例。
Elasticsearch数据模型
Elasticsearch 是基于文档的搜索引擎,它的数据模型是以文档为单位存储数据的。每个文档由字段和值的集合组成,文档的字段可以包括字符串、数字、布尔值、日期时间、嵌套对象和数组。类似 MongoDB,Elasticsearch 对数据的处理方式也是面向文档的。
下面是一个简单示例,假设我们有一条客户订单数据:
{ "_index": "customers", "_id": "1", "_source": { "name": "John Smith", "age": 30, "address": { "street": "Main Street", "city": "Anytown", "state": "CA", "zipcode": "12345" }, "orders": [{ "orderNumber": "ABCD123", "totalAmount": 100.00 }, { "orderNumber": "EFGH456", "totalAmount": 150.00 }] } }
此条订单数据中包含四个字段:“name”、“age”、“address”和“orders”,其中“orders”是一个嵌套的数组。订单数据中的每个元素都是一个对象,包括两个字段——“orderNumber”和“totalAmount”。
Elasticsearch 用文档型数据模型存储数据,每个文档包含一个独特的 _id
,用来标识文档。文档也可以包含多个字段,每个字段又有自己的数据类型,例如:字符串、整数、浮点数、日期、布尔值、嵌套对象、数组。这些数据类型能够为Elasticsearch 提供极高的灵活性和查询能力。
查询语法
Elasticsearch 提供丰富的查询语言,包括基于关键字、基于短语、基于正则表达式、基于过滤、基于聚合、基于排序等。下面是 Elasticsearch 的一些查询示例:
- 查找 name 为 “John Smith” 的所有客户:
GET customers/_search
{
"query": {
"match": {"name": "John Smith"}
}
}
- 查找 age 大于等于 30 的所有客户:
GET customers/_search
{
"query": {
"range": {"age": {"gte": 30}}
}
}
- 查找地址中城市为 “Anytown” 的所有客户:
GET customers/_search
{
"query": {
"nested": {
"path": "address",
"query": {
"match": {"address.city": "Anytown"}
}
}
}
}
- 检索 orderTotal 小于 200 的所有订单:
GET orders/_search
{
"query": {
"script_score": {
"query": {"match_all": {}},
"script": {
"inline": "if (doc['orderTotal'].value < 200) return _score; else return 0;"
}
}
}
}
- 返回最近的一条订单:
GET orders/_search?sort=_id:desc&size=1
Elasticsearch集群管理
Elasticsearch 支持动态添加和删除节点,因此可以随时调整集群规模。如果你需要自动缩放集群以响应负载变化,可以设置集群自动扩缩容策略。 在 Elasticsearch 中,集群由若干个节点组成,每个节点都扮演着不同的角色。其中主节点(Primary node)是管理整个集群、数据分片和处理搜索/查询请求的节点,同时也是其他节点联系的入口。主节点在任何时候只能有一个,它负责集群的管理、数据分片以及负载均衡。其他节点都是数据副本的储存节点,主要职责是在数据节点之间复制数据。副本数量越多,数据容灾性就越高。 下面是一个 Elasticsearch 集群架构示意图:
集群中的每个节点都有各自的唯一标识符 ID。一个集群通常由三类节点组成:主节点、数据节点、客户端节点。下面详细介绍一下各类节点的作用:
主节点(Master Node)
主节点负责管理整个集群,包括集群状态、数据分片和搜索/查询请求的处理。在 Elasticsearch 中,一个集群只能有一个主节点,它会接收客户端的请求并对请求进行协调,还可以分配数据分片给数据节点。主节点可以做很多事情,但一般来说,只有当集群出现故障时才需要重新选举出新的主节点。
数据节点(Data Node)
数据节点是集群中最重要的节点类型。它主要负责存储数据,并且对数据执行 CRUD 操作。在 Elasticsearch 中,数据节点既充当主节点,又充当数据节点。它既可以接收客户端的 CRUD 请求,又可以进行数据复制。数据节点还可以负责数据分析、数据可视化等,这取决于用户的具体需求。
客户端节点(Client Node)
客户端节点是连接到集群的节点。它们可以发送 HTTP 请求或者使用 Elasticsearch 的官方客户端库来连接集群并进行操作。客户端节点既可以执行集群管理操作,又可以执行数据查询操作。客户端节点的角色并不是严格要求的,但是建议将 Elasticsearch 只作为查询引擎使用,而非存储引擎。
Elasticsearch监控告警
Elasticsearch 提供了多种监控和告警工具,用户可以根据实际需求进行配置。下面列举几种常用监控手段:
节点级别监控
Elasticsearch 提供了节点级别的指标监控,例如 CPU 使用率、内存占用率、堆空间利用率等。这些指标可以帮助用户识别集群中存在的瓶颈节点,及时发现异常节点并进行处理。
集群级别监控
Elasticsearch 提供了集群级别的指标监述,例如集群整体的处理速度、索引数量、分片数量、线程池状态等。这些指标可以帮助用户了解集群整体的运行状态,并掌握集群当前的容量限制和资源使用情况。
索引级别监控
Elasticsearch 提供了索引级别的指标监控,例如索引的存储大小、最大序列号、文档数量、刷新频率、查询延迟等。这些指标可以帮助用户优化索引配置,并预测集群的索引压力。
通知方式
Elasticsearch 支持多种通知方式,例如邮件、短信、电话、Webhook 等。用户可以根据需要配置通知渠道,包括故障检测、集群指标变动、查询日志等。通知方式可以帮助用户及时收到集群警告和故障提示。
Elasticsearch性能调优
Elasticsearch 是一个高性能的搜索引擎,但是在生产环境中仍然可能遇到一些性能瓶颈。下面列举一些常见的性能调优策略:
JVM设置
JVM 设置是影响 Elasticsearch 性能的关键因素。下面是一些设置建议:
- 设置 JVM Heap Size
为了更精确地控制 JVM 的内存使用情况,可以设置较小的初始堆内存(最小堆内存)和最大堆内存。如果内存过低,JVM 将不会开始垃圾回收,这可能会导致内存不足甚至内存泄露问题。
- 设置 Garbage Collection Strategy
Elasticsearch 默认使用的是 Concurrent Mark Sweep(CMS)回收器。尽管 CMS 回收器有良好的暂停时间和吞吐量,但是对于持续性的高写入场景,它可能会遇到长时间的 GC 等待。为了改善暂停时间,可以尝试设置 Garbage Collector 为 Serial Old + Parallel Old 组合。Serial Old 表示串行收集器,Parallel Old 表示并行收集器。
- Disable Swap Memory
Elasticsearch 可以利用 Linux 的 swap 机制来缓冲数据,但是 swap 机制可能导致 JVM 频繁发生 FullGC,从而降低 Elasticsearch 的查询响应速度。为了避免这种情况,可以禁用 swap 机制。
集群拆分
虽然 Elasticsearch 支持横向扩展,但在实际生产环境中,还是应该控制集群的规模,防止资源过度消耗。可以考虑对集群进行拆分,即将整个集群分为多个子集群,每一个子集群承担一部分工作负载。这样可以更有效地利用硬件资源,避免单点故障。
索引优化
在 Elasticsearch 中,索引的性能直接决定了搜索和查询的响应速度。优化索引配置可以提高搜索和查询的效率。下面是一些优化建议:
- 正确设置字段类型
为了提高字段的性能,Elasticsearch 需要知道每个字段的类型。正确设置字段类型可以极大地提高查询速度。例如,不要将字符串字段设置为 text 类型,而应设置为 keyword 类型。
- 压缩文档
压缩文档可以减少磁盘的 IO 消耗,提高索引的写入和查询速度。通过配置文件中的 index.codec 配置项可以启用压缩。
- 使用批量加载
在 Elasticsearch 中,索引是一个不可更改的对象。为了提高写入和查询的性能,建议使用批量加载,即一次性加载多条文档,然后批量提交到 Elasticsearch。
查询优化
在 Elasticsearch 中,查询也是影响性能的关键环节。下面是一些查询优化建议:
- 避免复杂的查询
Elasticsearch 不支持所有类型的复杂查询,尤其是大型的跨字段查询。尝试优化查询,如使用布尔查询代替短语查询,或使用过滤器。
- 使用查询缓存
查询缓存可以加速热门查询的响应速度。通过配置文件中的 index.cache.field.enable 配置项可以开启查询缓存。
- 使用请求超时
请求超时可以避免无限等待的查询。通过配置文件中的 http.max_response_time 配置项可以设置超时时间。
- 使用索引别名
索引别名可以减少查询时对索引名称的敏感度。可以通过配置文件中的 index.names 配置项设置别名。
Elasticsearch实战案例
本章节介绍一些基于 Elasticsearch 的实际案例。
搜索商品信息
假设你需要搜集一些商品信息,包括产品名称、价格、描述、图片等。你需要创建一个名为 products 的索引,并向其中添加一些样本数据。 创建索引:
PUT /products
{
"mappings": {
"properties": {
"title": {"type": "text"},
"price": {"type": "float"},
"description": {"type": "text"},
"image": {"type": "keyword"}
}
}
}
添加样本数据:
POST /products/_bulk
{ "index" : { "_id" : "1" } }
{ "index" : { "_id" : "2" } }
...
现在,你需要实现一个搜索框,用户输入搜索条件,Elasticsearch 会自动返回符合条件的商品信息。下面是搜索框 UI 设计:
当用户输入搜索条件时,可以触发下面 JavaScript 函数:
function searchProducts() {
let title = document.getElementById("title").value;
let priceFrom = document.getElementById("price-from").value;
let priceTo = document.getElementById("price-to").value;
// Build query string based on user input
const queryString = [];
if (title!== "") {
queryString.push(`title:"${title}"`);
}
if (priceFrom!== "" && priceTo!== "") {
queryString.push(`price:[${priceFrom} TO ${priceTo}]`);
} else if (priceFrom!== "") {
queryString.push(`price:{* TO ${priceFrom}]`);
} else if (priceTo!== "") {
queryString.push(`price:[${priceTo} TO *}`);
}
// Send request to Elasticsearch server for product information
const url = `/products/_search?q=${queryString.join(" AND ")}`;
fetch(url)
.then((response) => response.json())
.then((data) => {
// Update display with results
})
.catch((error) => console.log(error));
}
此函数获取用户输入的参数,构建查询字符串,然后发送请求到 Elasticsearch 服务器。如果有搜索结果,就可以显示出来。
搜索广告信息
假设你需要搜索和展示广告信息。你需要创建 advertisements 索引,并向其中添加一些广告数据。 创建索引:
PUT /advertisements
{
"mappings": {
"properties": {
"advertiserName": {"type": "text"},
"campaignName": {"type": "text"},
"creativeTitle": {"type": "text"},
"creativeDescription": {"type": "text"},
"creativeImageLink": {"type": "keyword"}
}
}
}
添加样本数据:
POST /advertisements/_bulk
{ "index" : { "_id" : "1" } }
{ "index" : { "_id" : "2" } }
...
现在,你需要实现一个搜索框,用户输入搜索条件,Elasticsearch 会自动返回符合条件的广告信息。下面是搜索框 UI 设计:
当用户输入搜索条件时,可以触发下面 JavaScript 函数:
function searchAdvertisements() {
let advertiserName = document.getElementById("advertiser-name").value;
let campaignName = document.getElementById("campaign-name").value;
// Build query string based on user input
const queryString = [];
if (advertiserName!== "") {
queryString.push(`advertiserName:"${advertiserName}"`);
}
if (campaignName!== "") {
queryString.push(`campaignName:"${campaignName}"`);
}
// Send request to Elasticsearch server for advertising information
const url = `/advertisements/_search?q=${queryString.join(" AND ")}`;
fetch(url)
.then((response) => response.json())
.then((data) => {
// Update display with results
})
.catch((error) => console.log(error));
}
此函数获取用户输入的参数,构建查询字符串,然后发送请求到 Elasticsearch 服务器。如果有搜索结果,就可以显示出来。文章来源:https://uudwc.com/A/V6gpn
未来发展方向
本文介绍了 Elasticsearch 的基础知识,重点阐述了搜索引擎的相关概念和原理。下一章节将会介绍 Elasticsearch 的安装和部署,以及 Elasticsearch 集群管理和性能调优。文章来源地址https://uudwc.com/A/V6gpn