如何配置ThinkPHP应用使用ElasticSearch搜索_客户端单例封装与索引建立

星宇君_9553

星宇君_9553

2026-06-03

350人浏览

原创

ThinkPHP中应通过容器单例管理Elasticsearch客户端,避免多次new;索引需显式定义mapping并用命令行创建;ES仅作搜索辅助,先查ID再查MySQL主库;分页需处理track_total_hits或改用search_after。

如何配置thinkphp应用使用elasticsearch搜索_客户端单例封装与索引建立

ThinkPHP 里怎么用 Elasticsearch 客户端?别 new 多次

TP 应用里直接 new Elasticsearch\Client() 是最常见也最危险的做法——每次请求都新建连接,会迅速耗尽 PHP 进程的 socket 资源,尤其在并发稍高时出现 Connection refused 或超时。必须封装成单例,且生命周期绑定到请求上下文。

推荐在 app/common.php 或服务提供者中注册:

// app/provider/ElasticsearchServiceProvider.php
use Elasticsearch\ClientBuilder;
<p>return [
'elasticsearch' => [
'class' => \Elasticsearch\Client::class,
'shared' => true, // ThinkPHP 6+ 容器默认 shared,但显式写更安全
'constructor' => [
'hosts' => ['<a href="https://www.php.cn/link/31917677a66c6eddd3ab1f68b0679e2f">https://www.php.cn/link/31917677a66c6eddd3ab1f68b0679e2f</a>'],
],
'extend' => function ($client) {
// 可加全局 header,比如带认证
$client->setTransport(new \Elasticsearch\Transport($client->getConnectionPool(), [
'headers' => ['Authorization' => 'Basic ' . base64_encode('user:pass')],
]));
return $client;
}
]
];</p>
  • 务必确认 shared 为 true,否则容器每次 get() 都返回新实例
  • 不要在控制器或模型里手动 new Client(),哪怕只调一次——后续维护者大概率会复制粘贴
  • 如果用了 Swoole 或 Hyperf 长生命周期环境,还需额外处理连接池复用,TP 默认不支持,得换 elasticsearch/elasticsearch v8+ 的 AsyncClient 或自己加连接池管理

索引怎么建才不踩坑?PUT /index_name 不是万能的

直接发 PUT /my_index 创建索引看似简单,但没指定 mapping 就写入数据,ES 会按字段值自动推断类型(比如把 "123" 当成 long,后续写 "abc" 就报 illegal_argument_exception)。线上环境必须显式定义 mapping。

建议把索引创建逻辑抽成命令行任务,在部署时执行:

// app/command/CreateIndex.php
protected function configure()
{
    $this->setName('es:create-index')->setDescription('Create ES index with strict mapping');
}
<p>protected function execute(Input $input, Output $output)
{
$client = \think\Container::get('elasticsearch');
$client->indices()->create([
'index' => 'article',
'body' => [
'settings' => [
'number_of_shards' => 1,
'number_of_replicas' => 0,
],
'mappings' => [
'properties' => [
'id' => ['type' => 'long'],
'title' => ['type' => 'text', 'analyzer' => 'ik_smart'],
'content' => ['type' => 'text'],
'created_at' => ['type' => 'date', 'format' => 'strict_date_optional_time'],
]
]
]
]);
}</p>
  • 别依赖 auto_create_index 配置——它会让 ES 自动建索引,但 mapping 不可控,查不到数据时连问题在哪都难定位
  • 中文分词器(如 ik)必须提前装好,且在 mapping 中明确指定 analyzer,否则 text 字段默认用 standard,搜中文基本没结果
  • ES 7.x+ 已废弃 type,mapping 里别再写 '_doc' 或 'properties' 套一层 type

TP 模型怎么和 ES 索引联动?别硬套 ORM 思维

ThinkPHP 的 Db 或模型是面向关系数据库的,ES 是文档型,强行让 ArticleModel::search() 返回 Collection 对象,会导致 ID 映射错乱、关联查询失效、分页参数传错——这不是封装得好不好,是范式根本不同。

正确做法是分层:业务层调用 ES 获取 ID 列表,再用 TP 的 whereIn('id', $ids) 查 MySQL 主库取完整数据:

elasticsearch-9.4.1-linux
elasticsearch-9.4.1-linux

Elasticsearch 9.4.1 Linux 版本现已开放下载,这是官方最新发布的分布式搜索与分析引擎。Linux 版本全面支持 x86_64 与 aarch64 架构,提供 .tar.gz、.deb 及 .rpm 多种安装包格式,可灵活适配 Ubuntu、CentOS、Debian 等主流发行版。该版本延续了 9.4 系列的核心特性,包括原生 Prometheus 支持、正式版 Elast

下载
// 搜索服务类
class ArticleSearchService
{
    public function search($keyword, $page = 1, $size = 10)
    {
        $client = \think\Container::get('elasticsearch');
        $result = $client->search([
            'index' => 'article',
            'body' => [
                'query' => ['match' => ['title' => $keyword]],
                'from' => ($page - 1) * $size,
                'size' => $size,
            ]
        ]);
<pre class="brush:php;toolbar:false;">    $ids = array_column($result['hits']['hits'], '_id');
    $ids = array_map('intval', $ids); // ES 返回 _id 是 string,MySQL 主键是 int

    return ArticleModel::whereIn('id', $ids)->select();
}

}

  • ES 返回的 _id 是字符串,而 TP 模型默认主键是 int,不转类型会导致 whereIn 查不到数据
  • 不要把 ES 当主库用——它不保证强一致性,更新延迟、删除不即时、事务不可靠,只适合查,不负责写
  • 如果真要实时同步,用 logstash 或监听 MySQL binlog(如 canal),而不是在 TP 的 save() 里手动 push 到 ES

为什么搜索结果总少几条?检查 track_total_hits 和分页逻辑

ES 默认只统计前 10000 条命中数,超过就返回 "total": {"value": 10000, "relation": "gte"},但 TP 分页组件看到 total 是 10000 就以为只有这么多,导致最后几页空白。这不是数据丢了,是统计被截断了。

两种解法选其一:

  • 在搜索请求里加 'track_total_hits' => true(ES 7.0+),强制精确统计,但大数据量下性能下降明显
  • 更推荐改分页逻辑:用 search_after 替代 from/size,避免深度分页,尤其当用户翻到第 100 页时
  • TP 的 paginate() 无法直接对接 search_after,得自己封装分页器,把上一页最后一条的 sort 值作为下一页的 search_after 参数传过去

容易被忽略的是:ES 的 highlight 高亮字段默认不返回原始内容,要显式加 '_source' => ['title', 'content'],否则前端拿到空数据还怪后端没传。

php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

thinkphp elasticsearch

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
php文件怎么打开
php文件怎么打开

打开php文件步骤:1、选择文本编辑器;2、在选择的文本编辑器中,创建一个新的文件,并将其保存为.php文件;3、在创建的PHP文件中,编写PHP代码;4、要在本地计算机上运行PHP文件,需要设置一个服务器环境;5、安装服务器环境后,需要将PHP文件放入服务器目录中;6、一旦将PHP文件放入服务器目录中,就可以通过浏览器来运行它。

2023.09.01

10164

6

php怎么取出数组的前几个元素
php怎么取出数组的前几个元素

取出php数组的前几个元素的方法有使用array_slice()函数、使用array_splice()函数、使用循环遍历、使用array_slice()函数和array_values()函数等。本专题为大家提供php数组相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.11

6001

5

php反序列化失败怎么办
php反序列化失败怎么办

php反序列化失败的解决办法检查序列化数据。检查类定义、检查错误日志、更新PHP版本和应用安全措施等。本专题为大家提供php反序列化相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.11

2095

5

php怎么连接mssql数据库
php怎么连接mssql数据库

连接方法:1、通过mssql_系列函数;2、通过sqlsrv_系列函数;3、通过odbc方式连接;4、通过PDO方式;5、通过COM方式连接。想了解php怎么连接mssql数据库的详细内容,可以访问下面的文章。

2023.10.23

3788

4

php连接mssql数据库的方法
php连接mssql数据库的方法

php连接mssql数据库的方法有使用PHP的MSSQL扩展、使用PDO等。想了解更多php连接mssql数据库相关内容,可以阅读本专题下面的文章。

2023.10.23

4474

6

html怎么上传
html怎么上传

html通过使用HTML表单、JavaScript和PHP上传。更多关于html的问题详细请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.03

3511

9

PHP出现乱码怎么解决
PHP出现乱码怎么解决

PHP出现乱码可以通过修改PHP文件头部的字符编码设置、检查PHP文件的编码格式、检查数据库连接设置和检查HTML页面的字符编码设置来解决。更多关于php乱码的问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.09

4997

8

php文件怎么在手机上打开
php文件怎么在手机上打开

php文件在手机上打开需要在手机上搭建一个能够运行php的服务器环境,并将php文件上传到服务器上。再在手机上的浏览器中输入服务器的IP地址或域名,加上php文件的路径,即可打开php文件并查看其内容。更多关于php相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.13

3922

8

sprintf函数用法详解
sprintf函数用法详解

sprintf函数的用法:1、格式化字符串;2、指定输出宽度和精度;3、返回值。更多关于sprintf函数用法详解的内容,大家可以阅读下面的文章。

2023.11.27

11882

4

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程