遭遇了 “黑天鹅” 事件
近期真是遭遇了 “黑天鹅” 事件,公司因卷入非法问题,面临一系列棘手处理,无奈之下只得遣散全体员工,我也因此瞬间失业。原本按部就班的工作节奏戛然而止,每天醒来,看着毫无安排的一天,心里满是茫然与失落。与其在这种不确定中焦虑徘徊,倒不如主动出击,给自己找点事儿做。我打算投身到文档撰写中,既能有效填补空闲时间,让生活重回正轨,也能借此梳理过往积累的知识与经验,为未来求职筑牢根基,让这段意外失业的日子,不至于虚度 。
回顾
上一篇我们一同探索了 Sphinx 这个强大的全文搜索引擎,从它的功能特性到安装配置,相信大家对如何利用 Sphinx 搭建高效搜索系统有了清晰认知。不过技术的世界从不止步,在搜索领域,还有一位 “大咖”——Elasticsearch(简称 ES),它凭借分布式、高扩展、实时搜索等卓越优势,在企业级应用中大放异彩。这一篇,就让我们把目光聚焦 ES,深入剖析它的安装配置流程,探寻如何在 PHP 开发环境里,巧妙借助 ES 的力量,实现更智能、更高速的数据检索,解锁不一样的搜索体验。
什么是Elasticsearch?它有什么优缺点?和 sphinx 相比,差异在哪里?
- Elasticsearch 是基于 Lucene 的分布式开源搜索引擎,用于全文搜索、数据分析等。
- 优点是分布式、实时性强、功能丰富。
- 缺点是硬件要求高、索引维护成本高。
- 与 Sphinx 相比,ES 分布式和扩展性更好,功能更全面;
- Sphinx 在特定场景性能较好,配置相对简单,更适合中小规模数据。
安装和配置
- Elasticsearch官网下载地址
- 服务器:CentOS7,Java版本21.0.2(Elasticsearch基于Java开发)
- 没有Java环境的自行去下载合适的版本,并把java加到环境变量中。
sudo vim /etc/profile
export JAVA_HOME=/www/server/java/jdk-21.0.2
export PATH=$JAVA_HOME/bin:$PATH
安装
# 创建存放Elasticsearch软件目录
mkdir -p /usr/local/es/
# 进入/usr/local/es/文件夹
cd /usr/local/es/
# 下载对应的glibc版本的sphinx软件包
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.17.2-linux-x86_64.tar.gz
# 解压
tar -zxvf elasticsearch-8.17.2-linux-x86_64.tar.gz
# es 禁止以root用户运行,所以我们创建一个组和用户,用于启动es
# 创建组 elasticsearch
groupadd elasticsearch
# 创建用户,指定用户所在的组
useradd youer -g elasticsearch
# 设置用户密码
passwd youer
配置
# 进入/usr/local/es/elasticsearch-8.17.2/config 配置目录,修改以下配置
cd /usr/local/es/elasticsearch-8.17.2/config
vim elasticsearch.yml
# 找到以下配置并且修改
===============================================================================
# 集群名称,用于标识一个 Elasticsearch 集群,
# 同一集群内的节点需要具有相同的集群名称才能相互通信并组成集群。
# 这里设置为 "my-es",你可以根据实际情况修改为合适的名称。
cluster.name: my-es
# 节点名称,用于在集群中唯一标识一个节点。
# 每个节点都应有一个独特的名称,这里设置为 "node-1",
# 可以根据实际需求进行调整。
node.name: node-1
# 绑定的网络地址,"0.0.0.0" 表示该节点将监听所有可用的网络接口,
# 使得其他机器可以通过网络访问该 Elasticsearch 节点。
# 这在生产环境中需要谨慎配置,以确保安全性。
network.host: 0.0.0.0
# HTTP 服务端口,Elasticsearch 通过此端口提供 HTTP 接口供外部访问,
# 这里设置为 9200,是 Elasticsearch 默认的 HTTP 端口,
# 可根据实际情况修改,但要避免与其他服务端口冲突。
http.port: 9200
# 种子节点列表,用于在节点启动时发现集群中的其他节点。
# 这里指定了 "127.0.0.1",表示在本地寻找其他节点。
# 在分布式集群环境中,应填写其他实际节点的 IP 地址。
discovery.seed_hosts: ["127.0.0.1"]
# 初始化主节点列表,用于在集群启动时确定哪些节点有资格成为主节点。
# 这里设置为 ["node-1"],意味着 node-1 节点有资格成为主节点。
# 主节点负责管理集群的状态和元数据等重要任务。
cluster.initial_master_nodes: ["node-1"]
# 这一行被注释掉了,原本是用于控制系统调用过滤器的配置。
# bootstrap.system_call_filter: false
# 禁止节点在启动时锁定内存。
# Elasticsearch 默认会尝试锁定内存以提高性能,但在某些环境下可能会失败。
# 设置为 false 表示不进行内存锁定,可根据实际情况调整。
bootstrap.memory_lock: false
# 启用 HTTP CORS(跨域资源共享)功能。
# 当设置为 true 时,允许来自不同域名的请求访问 Elasticsearch 的 HTTP 接口。
# 这在浏览器或其他跨域访问场景中很有用。
http.cors.enabled: true
# 允许的跨域来源,这里设置为 "*" 表示允许所有来源的请求。
# 在生产环境中,建议明确指定具体的域名,以提高安全性。
http.cors.allow-origin: "*"
===============================================================================
Elasticsearch配置JVM参数
- Elasticsearch配置JVM参数合理配置JVM参数对其性能和稳定性至关重要。
- -Xms 设定JVM堆内存初始大小,避免频繁内存分配;
- -Xmx 限制堆内存最大是多少,防止过度占用系统资源。建议二者设为相同值,依实际情况调整。
cd /usr/local/es/elasticsearch-8.17.2/config
vim jvm.options
# 我的设置是根据我服务器的配置来的,你们根据实际情况而定
-Xms512m
-Xmx512m
增加ES最大文件、进程数限制,保其稳定运行
- /etc/security/limits.conf 通用资源限制配置文件,可全面设限。
- /etc/security/limits.d/20-nproc.conf 用于补充细化,配置优先级或更高。
sudo vim /etc/security/limits.conf
============================================
# 指定用户设置软限制的最大打开文件描述符数量
youer soft nofile 65536
# 指定用户设置硬限制的最大打开文件描述符数量
youer hard nofile 65536
# 指定用户设置软限制的最大进程数量
youer soft nproc 4096
# 指定用户设置硬限制的最大进程数量
youer hard nproc 4096
============================================
sudo vim /etc/security/limits.conf
======================================
youer soft nproc 4096
youer hard nproc 4096
======================================
或者
=====================================
* soft nproc 4096
root soft nproc unlimited
=====================================
调整系统的内核参数,保证Elasticsearch应用跑起来
sudo vim /etc/sysctl.conf
================================
# 设置虚拟内存中最大内存映射区域的数量
# 对于像 Elasticsearch 这类应用,在启动和运行过程中会进行大量的内存映射操作
# 若该值过小,可能会导致 Elasticsearch 启动失败或运行过程中出现内存映射错误
# 这里将其设置为 655360,为应用提供更充足的内存映射资源
vm.max_map_count=655360
# 设置系统允许的最大文件数
# 系统在运行时会涉及大量文件的打开、读写等操作
# 增大该值可以确保系统有足够的资源来处理更多的文件操作
# 避免因文件描述符耗尽而导致应用无法正常工作
# 这里设置为 65535
fs.file-max = 65535
================================
# 读取配置并应用新的内核参数,使修改后的配置立即生效
sudo sysctl -p
安装ik插件
ik插件下载,下载与之对应的ik中文分词插件
cd /usr/local/es/ wget https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-8.17.2.zip unzip elasticsearch-analysis-ik-8.17.2.zip mv elasticsearch-analysis-ik-8.17.2 /usr/local/es/elasticsearch-8.17.2/plugins/
权限
# 给文件夹赋权和用户以及组
sudo chown -R youer:elasticsearch /usr/local/es/elasticsearch-8.17.2
sudo chmod -R 755 /usr/local/es/elasticsearch-8.17.2
启动
# 切换到youer用户
su youer
# 启动es
cd /usr/local/es/elasticsearch-8.17.2/bin
./elasticsearch -d
查看端口和es服务
# 查看端口
ps -ef | grep elasticsearch|grep -v "grep"
netstat -an | grep 9200
# 对外防火墙放开端口
# 查询是否开启
sudo firewall-cmd --query-port=9200/tcp
# 开启
sudo firewall-cmd --add-port=9200/tcp --permanent
# 重启生效
sudo firewall-cmd --reload
php使用
安装类库
composer require elasticsearch/elasticsearch
declare(strict_types=1);
set_time_limit(-1);
require __DIR__ . "./vendor/autoload.php";
use Elastic\Elasticsearch\ClientBuilder;
try {
$client = ClientBuilder::create()
->setHosts(['http://142.171.102.86:9200'])
->setSSLVerification(false)
->build();
// 定义用于创建 Elasticsearch 索引的参数数组
$t = $_GET['type'] ?? 1;
switch ($t) {
case 1://创建索引时配置 IK 分词器
$params = [
// 指定要创建的索引的名称为 my_ik_index
// 在 Elasticsearch 里,索引是存储文档的逻辑容器,类似关系型数据库中的数据库
// 后续对该索引的操作(如添加文档、搜索等)都会使用此名称定位
'index' => 'my_ik_index',
// 包含索引设置和映射信息的数组,是创建索引请求的核心部分
'body' => [
// 用于设置索引的各种参数,这里主要关注与分词分析相关的设置
'settings' => [
// 配置索引的分析器和分词器
// 分析器负责将文本转换为适合索引和搜索的词项,分词器是分析器的核心,负责拆分文本
'analysis' => [
// 定义自定义分析器的部分,可以配置多个分析器,每个有唯一名称
'analyzer' => [
// 自定义分析器 ik_max_word_analyzer
'ik_max_word_analyzer' => [
// 表示这是一个自定义分析器
// 除内置分析器外,可自定义组合分词器、过滤器等创建特定分析器
'type' => 'custom',
// 指定该分析器使用 ik_max_word 作为分词器
// ik_max_word 是 IK 分词器的一种模式,会对文本做最细粒度拆分
'tokenizer' => 'ik_max_word'
],
// 自定义分析器 ik_smart_analyzer
'ik_smart_analyzer' => [
// 表示这是一个自定义分析器
'type' => 'custom',
// 指定该分析器使用 ik_smart 作为分词器
// ik_smart 是 IK 分词器的另一种模式,会对文本做最粗粒度拆分
'tokenizer' => 'ik_smart'
]
]
]
],
// 定义索引中文档的结构和字段类型,以及每个字段使用的分析器
'mappings' => [
// 定义索引中各个字段的属性,每个字段有唯一名称,可配置类型、分词器等
'properties' => [
// 定义 title 字段
'title' => [
// 指定 title 字段的类型为 text,用于存储文本数据
// text 类型字段在索引时会进行分词处理以支持全文搜索
'type' => 'text',
// 指定在对 title 字段进行索引时使用 ik_max_word_analyzer 分析器
// 这样在添加文档到索引时,title 字段文本会被 ik_max_word 分词器细粒度拆分
'analyzer' => 'ik_max_word_analyzer',
// 指定在对 title 字段进行搜索时使用 ik_smart_analyzer 分析器
// 搜索时,搜索关键词会被 ik_smart 分词器粗粒度拆分以提高搜索准确性
'search_analyzer' => 'ik_smart_analyzer'
],
// 定义 content 字段
'content' => [
// 指定 content 字段的类型为 text,用于存储文本数据
'type' => 'text',
// 指定在对 content 字段进行索引时使用 ik_max_word_analyzer 分析器
// 索引时,content 字段文本会被 ik_max_word 分词器细粒度拆分
'analyzer' => 'ik_max_word_analyzer',
// 指定在对 content 字段进行搜索时使用 ik_smart_analyzer 分析器
// 搜索时,搜索关键词会被 ik_smart 分词器粗粒度拆分以提高搜索准确性
'search_analyzer' => 'ik_smart_analyzer'
]
]
]
]
];
$response = $client->indices()->create($params);
if ($response['acknowledged']) {
echo "索引创建成功!";
} else {
echo "索引创建失败!";
}
break;
case 2://添加文档
$params = [
'index' => 'my_ik_index',
'id' => '1',
'body' => [
'title' => '自然语言处理技术',
'content' => '自然语言处理是人工智能的一个重要领域。'
]
];
$response = $client->index($params);
// 判断添加文档操作是否成功
if (isset($response['result']) && $response['result'] === 'created') {
echo "文档添加成功,文档 ID 为: " . $response['_id'];
} else {
echo "文档添加失败,错误信息: " . json_encode($response);
}
break;
case 3://搜索文档
$query = "领域";
$params = [
'index' => 'my_ik_index',
'body' => [
'query' => [
'multi_match' => [
'query' => $query,
'fields' => ['title', 'content'],
'analyzer' => 'ik_smart_analyzer'
]
]
]
];
$response = $client->search($params);
if (isset($response['hits']['total']['value']) && $response['hits']['total']['value'] > 0) {
echo "搜索成功,共找到 " . $response['hits']['total']['value'] . " 个匹配文档:</br>";
foreach ($response['hits']['hits'] as $hit) {
$source = json_encode($hit['_source'], JSON_UNESCAPED_UNICODE);
$source = str_replace($query, "<span style='color: red;'>{$query}</span>",$source);
echo "文档 ID: " . $hit['_id'] . ",</br> 得分: " . $hit['_score'] . ",</br> 内容: " . $source . "</br>";
}
} elseif (isset($response['hits']['total']['value']) && $response['hits']['total']['value'] === 0) {
echo "搜索成功,但未找到匹配的文档。";
} else {
echo "搜索失败,错误信息: " . json_encode($response);
}
break;
case 4://删除文档
$params = [
'index' => 'my_index'
];
// 执行删除索引操作
$response = $client->indices()->delete($params);
// 判断删除是否成功
if (isset($response['acknowledged']) && $response['acknowledged'] === true) {
echo "索引删除成功。";
} else {
echo "索引删除失败,错误信息: " . json_encode($response);
}
break;
case 5://获取集群健康状态
// 调用 _cluster/health API 获取集群健康状态
$params = [
'index' => null, // 不指定特定索引,获取整个集群的健康状态
];
$response = $client->cluster()->health($params);
// 解析和处理返回结果
$status = $response['status'];
echo "Elasticsearch 集群健康状态: ". $status. "\n";
switch ($status) {
case 'green':
echo "集群状态良好,所有主分片和副本分片都可用。\n";
break;
case 'yellow':
echo "集群状态正常,但存在部分副本分片不可用。\n";
break;
case 'red':
echo "集群状态异常,存在主分片不可用的情况。\n";
break;
default:
echo "未知的集群健康状态。\n";
}
break;
}
} catch (Exception $e) {
echo "发生错误:" . $e->getMessage();
}
3 comments
文献引用规范,学术态度严谨,值得借鉴。
案例丰富且贴合主题,论证逻辑环环相扣。
这篇文章如同一幅色彩斑斓的画卷,每一笔都充满了独特的创意。