ElasticSearch
一、 ElasticSearch介绍
Elasticsearch 是一个分布式、可扩展、实时的搜索和分析引擎,基于 Apache Lucene 构建。它能够快速存储、搜索和分析大量数据,广泛应用于全文搜索、日志分析、业务指标监控等场景。
Lucene 介绍
Lucene是开源、免费、高性能、纯Java编写的全文检索工具包
他是一个全文检索的工具包,是一个全文检索框架,并不是一个全文检索引擎
它非常复杂,并且需要Java集成使用
Lucene 和 ElasticSearch
- ElasticSearch和solr是基于lucene的开源项目
- ElasticSearch通过简单易用的restful api接口,隐藏了lucene的复杂性
- ElasticSearch自带分布式管理,并且可以跨语言使用
Lucene 和 Solr
- ElasticSearch自带分布式管理而Solr需要借助Zookpeeper实现分布式管理
- Solr支持多格式的数据,在传统搜索中表现好于ES,但是它的更新效率比较低
- ElasticSearch只支持json格式的数据,在处理实时索引搜索时明显好于Solr
二、. 核心概念
与关系型数据库的类比
| 关系型数据库 (MySQL) | Elasticsearch |
|---|---|
| Database (数据库) | Index (索引) |
| Table (表) | Type (类型,已弃用) |
| Row (行) | Document (文档) |
| Column (列) | Field (字段) |
| Schema (模式) | Mapping (映射) |
注意:Elasticsearch 7.x 开始移除了 Type 概念,现在一个索引直接包含文档。
1. 文档(Document)
- Elasticsearch 中的基本数据单元,类似于关系数据库中的一行记录。
- 文档以 JSON 格式存储,包含多个字段。
- 示例:
{
"id": 1,
"title": "Elasticsearch Guide",
"content": "Elasticsearch is a distributed search engine.",
"tags": ["search", "distributed"]
}
2.索引(Index)
- 索引是文档的集合,类似于关系数据库中的表。
- 每个索引有一个唯一的名称,用于标识和操作数据。
- 示例:books 索引存储所有书籍相关的文档。
3.类型(Type)(已弃用)
在早期版本中,索引可以包含多个类型(类似于表结构),但在 Elasticsearch 7.x 及更高版本中已被弃用。
4.分片(Shard)
- 索引可以被分成多个分片,每个分片是一个独立的 Lucene 索引。
- 分片允许数据水平拆分,支持分布式存储和并行处理。
- 分片分为主分片(Primary Shard)和副本分片(Replica Shard)。
5.节点(Node)
- 节点是 Elasticsearch 集群中的一个服务器实例,负责存储数据和执行操作。
- 节点可以扮演不同的角色(如主节点、数据节点、协调节点)。
6.集群(Cluster)
- 集群由一个或多个节点组成,共同存储数据并提供搜索服务。
- 集群通过唯一的名称标识。
3.架构与工作原理
1.分布式架构
- Elasticsearch 采用分布式设计,数据存储在多个节点上。
- 数据被分成多个分片,每个分片可以有多个副本,确保高可用性和容错性。
2.数据写入流程
- 客户端发送写入请求到协调节点。
- 协调节点根据文档 ID 计算目标分片,并将请求转发到主分片所在的节点。
- 主分片写入数据后,同步到副本分片。
- 写入成功后,返回响应给客户端。
3.数据搜索流程
- 客户端发送搜索请求到协调节点。
- 协调节点将请求广播到所有相关分片(主分片或副本分片)。
- 每个分片执行搜索操作,返回结果给协调节点。
- 协调节点合并结果,排序后返回给客户端。
4.倒排索引
- Elasticsearch 使用倒排索引(Inverted Index)实现快速全文搜索。
- 倒排索引将文档中的每个词映射到包含该词的文档列表。
- 示例:
- 文档 1:{“content”: “Elasticsearch is fast”}
- 文档 2:{“content”: “Elasticsearch is distributed”}
- 倒排索引:
"Elasticsearch" -> [文档1, 文档2] "fast" -> [文档1] "distributed" -> [文档2]
三、主要特性
1.高性能
- 支持实时搜索和分析,响应时间通常在毫秒级。
- 通过分布式架构和倒排索引,能够快速处理大规模数据。
2.可扩展性
- 支持水平扩展,可以通过增加节点来提升存储和计算能力。
- 自动分片和副本机制,确保数据分布均衡。
3.高可用性
- 通过副本分片实现数据冗余,确保节点故障时数据不丢失。
- 主节点选举机制,确保集群的高可用性。
4.丰富的查询功能
- 支持全文搜索、结构化搜索、模糊搜索、范围搜索等多种查询方式。
- 提供聚合(Aggregation)功能,支持数据统计和分析。
5.插件生态
- 支持丰富的插件,扩展 Elasticsearch 的功能。
- 例如:IK 分词插件(中文分词)、Elasticsearch-Hadoop(与 Hadoop 集成)。
ik分词器 一个标准的中文分词器。可以根据定义的字典对域进行粉刺,并且支持用户配置自己的字典,所以它除了可以按通用的习惯分词外,还可以定制化分词。 可以使用插件的方式将他接入到ES。
ik分词器有两种分词方式:ik_smart最粗粒度的拆分和ik_max_word最细粒度的拆分。
四、使用场景
1.全文搜索与信息检索
- 适用于搜索引擎、电商网站的商品搜索、内容管理系统的文档搜索等场景。
- 示例:一个电商网站有100万件商品,用户搜索“手机 2000元以内 拍照好”,传统MySQL的LIKE查询可能需要数秒甚至超时,而Elasticsearch通过倒排索引和组合查询,能在毫秒级返回结果。
2.日志分析与可观测性
- 与 Logstash 和 Kibana 结合,构建日志管理和分析系统(ELK Stack)。
- 示例:分析服务器日志,监控系统状态,排查故障。
3.向量检索与AI应用
- Elasticsearch 内置向量数据库能力,支持将文本、图像等转换为向量嵌入,进行语义相似度搜索。这是当前 RAG(检索增强生成)和 AI 助手的核心基础设施。
4. 数据分析与商业智能
电子商务、广告业务等需要多维度统计分析。Elasticsearch 支持复杂聚合查询,可在数亿条数据中实时完成按分类统计、趋势分析等操作。
5. 数据库查询加速
传统关系型数据库在复杂查询场景下性能不足,可通过数据同步将数据导入Elasticsearch,利用其高并发低延迟的查询能力加速检索。
五、优缺点
1.优点
- 高性能:支持实时搜索和分析。
- 可扩展性:支持水平扩展,适应大规模数据。
- 高可用性:通过副本机制确保数据安全。
- 易用性:提供 RESTful API,易于集成和使用。
2.缺点
- 资源消耗:对内存和 CPU 要求较高。
- 复杂性:分布式系统的部署和维护较为复杂。
- 数据一致性:在极端情况下可能出现数据不一致问题。
六、PHP 开发实践
使用官方推荐的 elasticsearch-php 客户端:
composer require elasticsearch/elasticsearch
客户端版本需与 Elasticsearch 服务器大版本匹配。例如 Elasticsearch 8.x 使用 elasticsearch-php 8.x。语言客户端向前兼容,但新功能需要新版本客户端支持。
连接:
<?php
require 'vendor/autoload.php';
use Elasticsearch\ClientBuilder;
// 无安全认证模式
$client = ClientBuilder::create()
->setHosts(['http://localhost:9200'])
->build();
// 带用户名密码认证
$client = ClientBuilder::create()
->setHosts(['http://localhost:9200'])
->setBasicAuthentication('username', 'password')
->build();
// HTTPS + 证书验证
$client = ClientBuilder::create()
->setHosts(['https://localhost:9200'])
->setBasicAuthentication('username', 'password')
->setSSLVerification('/path/to/cert.pem') // 或 true/false
->build();
// 检查集群健康状态
$health = $client->cluster()->health();
print_r($health);
// 返回 'status' => 'green' 表示集群正常
连接配置的关键参数:
超时设置:setConnectionParams([‘client’ => [’timeout’ => 10, ‘connect_timeout’ => 5]])
重试次数:setRetries(3)
HTTPS安全验证:生产环境建议开启证书验证,测试环境可临时关闭setSSLVerification(false)
示例:使用 Elasticsearch 构建商品搜索系统
数据准备
- 创建 products 索引,定义字段映射:
PUT /products { "mappings": { "properties": { "name": { "type": "text" }, "price": { "type": "float" }, "category": { "type": "keyword" } } } }$params = [ 'index' => 'products', 'body' => [ 'settings' => [ 'number_of_shards' => 2, // 分片数 'number_of_replicas' => 1 // 副本数 ], 'mappings' => [ 'properties' => [ 'name' => [ 'type' => 'text', // 全文搜索字段,使用分析器 'analyzer' => 'ik_max_word' // 中文分词(需安装IK插件) ], 'price' => [ 'type' => 'double' // 数值字段,用于范围查询和排序 ], 'category' => [ 'type' => 'keyword' // 精确匹配字段,不进行分词 ], 'description' => [ 'type' => 'text' ], 'created_at' => [ 'type' => 'date' ] ] ] ] ]; $response = $client->indices()->create($params);写入数据
- 插入商品数据:
POST /products/_doc/1 { "name": "Smartphone X", "price": 599.99, "category": "Electronics" }搜索数据
- 搜索名称包含 “Smartphone” 的商品:
GET /products/_search { "query": { "match": { "name": "Smartphone" } } }聚合分析
- 按类别统计商品数量:
GET /products/_search { "size": 0, "aggs": { "categories": { "terms": { "field": "category" } } } }更新
完全替换文档
通过指定文档 ID,直接覆盖旧文档。若文档不存在,则会创建新文档。
示例:更新商品价格
PUT /products/_doc/1 { "name": "Smartphone X", "price": 549.99, // 价格从 599.99 更新为 549.99 "category": "Electronics" }操作说明:
- Elasticsearch 会先删除旧文档(ID=1),再写入新文档。
- 即使只修改部分字段,也需要传递完整的文档内容。
部分更新(Partial Update)
使用 _update API,仅更新文档的指定字段,无需传递完整文档。
语法:
POST /<index>/_update/<doc_id> { "doc": { "field1": "new_value1", "field2": "new_value2" } }示例:仅更新商品价格
POST /products/_update/1 { "doc": { "price": 499.99 } }操作说明:
Elasticsearch 内部会执行以下操作:
获取旧文档。
合并新旧文档的字段。
删除旧文档,写入新文档。
优点:减少网络传输数据量,适合仅更新少量字段的场景。
使用脚本更新(Scripted Update)
通过 Painless 脚本动态更新文档字段,支持复杂的逻辑(如条件更新、计算字段值等)。
语法:
POST /<index>/_update/<doc_id> { "script": { "source": "ctx._source.<field> = <value>", "lang": "painless" } }示例:将商品价格打 9 折
POST /products/_update/1 { "script": { "source": "ctx._source.price *= 0.9", "lang": "painless" } }示例:条件更新(仅当价格高于 500 时打折)
POST /products/_update/1 { "script": { "source": """ if (ctx._source.price > 500) { ctx._source.price *= 0.8; } """, "lang": "painless" } }
4.批量更新
使用 _bulk API 批量更新文档,提升效率。
POST /_bulk { "update": { "_index": "products", "_id": "1" } } { "doc": { "price": 499.99 } } { "update": { "_index": "products", "_id": "2" } } { "doc": { "price": 299.99 } }