本文目录导读:

我来提供一个完整的PHP Elasticsearch索引管理指南,涵盖常用的索引管理操作。
安装和初始化
<?php
require_once 'vendor/autoload.php';
use Elastic\Elasticsearch\ClientBuilder;
class ElasticsearchManager {
private $client;
public function __construct($hosts = ['localhost:9200']) {
$this->client = ClientBuilder::create()
->setHosts($hosts)
->setBasicAuthentication('username', 'password') // 如果有认证
->setSSLVerification(false) // 如果使用HTTPS且不需要验证证书
->build();
}
public function getClient() {
return $this->client;
}
}
索引创建
class IndexManager {
private $client;
public function __construct($client) {
$this->client = $client;
}
// 创建索引
public function createIndex($indexName, $mappings = [], $settings = []) {
$params = [
'index' => $indexName,
'body' => [
'settings' => array_merge([
'number_of_shards' => 3,
'number_of_replicas' => 2
], $settings),
'mappings' => $mappings
]
];
try {
$response = $this->client->indices()->create($params);
return $response->asArray();
} catch (\Exception $e) {
throw new \Exception("创建索引失败: " . $e->getMessage());
}
}
// 创建索引(带完整配置示例)
public function createUserIndex() {
$mappings = [
'properties' => [
'id' => ['type' => 'long'],
'name' => [
'type' => 'text',
'fields' => [
'keyword' => ['type' => 'keyword']
]
],
'email' => ['type' => 'keyword'],
'age' => ['type' => 'integer'],
'created_at' => ['type' => 'date'],
'tags' => ['type' => 'keyword'],
'bio' => [
'type' => 'text',
'analyzer' => 'ik_max_word' // 中文分词器
]
]
];
$settings = [
'number_of_shards' => 2,
'number_of_replicas' => 1,
'analysis' => [
'analyzer' => [
'default' => [
'type' => 'ik_max_word'
]
]
]
];
return $this->createIndex('users', $mappings, $settings);
}
// 仅当索引不存在时创建
public function createIndexIfNotExists($indexName, $mappings = [], $settings = []) {
if (!$this->indexExists($indexName)) {
return $this->createIndex($indexName, $mappings, $settings);
}
return ['acknowledged' => true, 'message' => '索引已存在'];
}
}
索引查询和检查
class IndexManager {
// 检查索引是否存在
public function indexExists($indexName) {
$params = ['index' => $indexName];
return $this->client->indices()->exists($params)->asBool();
}
// 获取索引信息
public function getIndexInfo($indexName) {
$params = ['index' => $indexName];
return $this->client->indices()->get($params)->asArray();
}
// 获取索引设置
public function getIndexSettings($indexName) {
$params = [
'index' => $indexName,
'include_defaults' => true
];
return $this->client->indices()->getSettings($params)->asArray();
}
// 获取索引映射
public function getIndexMapping($indexName) {
$params = ['index' => $indexName];
return $this->client->indices()->getMapping($params)->asArray();
}
// 获取索引统计信息
public function getIndexStats($indexName) {
$params = [
'index' => $indexName,
'metric' => 'docs,store,indexing,get,search'
];
return $this->client->indices()->stats($params)->asArray();
}
// 获取所有索引列表
public function getAllIndices() {
$params = [
'index' => '*',
'expand_wildcards' => 'all'
];
return $this->client->indices()->get($params)->asArray();
}
// 获取索引健康状态
public function getIndexHealth($indexName = null) {
$params = [];
if ($indexName) {
$params['index'] = $indexName;
}
return $this->client->cluster()->health($params)->asArray();
}
}
索引更新和修改
class IndexManager {
// 更新索引设置
public function updateIndexSettings($indexName, $settings) {
$params = [
'index' => $indexName,
'body' => [
'settings' => $settings
]
];
try {
return $this->client->indices()->putSettings($params)->asArray();
} catch (\Exception $e) {
throw new \Exception("更新索引设置失败: " . $e->getMessage());
}
}
// 添加映射字段
public function addMapping($indexName, $properties) {
$params = [
'index' => $indexName,
'body' => [
'properties' => $properties
]
];
try {
return $this->client->indices()->putMapping($params)->asArray();
} catch (\Exception $e) {
throw new \Exception("添加映射失败: " . $e->getMessage());
}
}
// 更新现有映射(注意:只能添加新字段,不能修改已有字段)
public function updateMapping($indexName, $newFields) {
try {
$existingMapping = $this->getIndexMapping($indexName);
$existingProperties = $existingMapping[$indexName]['mappings']['properties'] ?? [];
// 合并字段
$mergedProperties = array_merge($existingProperties, $newFields);
return $this->addMapping($indexName, $mergedProperties);
} catch (\Exception $e) {
throw new \Exception("更新映射失败: " . $e->getMessage());
}
}
// 刷新索引
public function refreshIndex($indexName) {
$params = ['index' => $indexName];
return $this->client->indices()->refresh($params)->asArray();
}
// 强制合并段
public function forceMerge($indexName, $maxSegments = 1) {
$params = [
'index' => $indexName,
'max_num_segments' => $maxSegments,
'wait_for_completion' => true
];
return $this->client->indices()->forcemerge($params)->asArray();
}
// 清空索引缓存
public function clearCache($indexName) {
$params = ['index' => $indexName];
return $this->client->indices()->clearCache($params)->asArray();
}
// 开启或关闭索引
public function openIndex($indexName) {
return $this->client->indices()->open(['index' => $indexName]);
}
public function closeIndex($indexName) {
return $this->client->indices()->close(['index' => $indexName]);
}
}
索引别名管理
class IndexManager {
// 创建别名
public function createAlias($indexName, $aliasName) {
$params = [
'index' => $indexName,
'name' => $aliasName
];
return $this->client->indices()->putAlias($params)->asArray();
}
// 创建多个别名
public function createAliases($actions) {
$params = ['body' => ['actions' => $actions]];
return $this->client->indices()->updateAliases($params)->asArray();
}
// 删除别名
public function deleteAlias($indexName, $aliasName) {
$params = [
'index' => $indexName,
'name' => $aliasName
];
return $this->client->indices()->deleteAlias($params)->asArray();
}
// 获取别名信息
public function getAliases($indexName = null) {
$params = [];
if ($indexName) {
$params['index'] = $indexName;
}
return $this->client->indices()->getAliases($params)->asArray();
}
// 别名切换(用于索引重建)
public function switchAlias($oldIndex, $newIndex, $aliasName) {
$params = [
'body' => [
'actions' => [
['remove' => ['index' => $oldIndex, 'alias' => $aliasName]],
['add' => ['index' => $newIndex, 'alias' => $aliasName]]
]
]
];
return $this->client->indices()->updateAliases($params)->asArray();
}
}
索引模板管理
class IndexManager {
// 创建索引模板
public function createTemplate($templateName, $template) {
$params = [
'name' => $templateName,
'body' => $template
];
return $this->client->indices()->putTemplate($params)->asArray();
}
// 创建示例模板
public function createUserTemplate() {
$template = [
'index_patterns' => ['users-*'],
'settings' => [
'number_of_shards' => 2,
'number_of_replicas' => 1
],
'mappings' => [
'properties' => [
'name' => ['type' => 'text'],
'email' => ['type' => 'keyword'],
'created_at' => ['type' => 'date']
]
],
'aliases' => [
'all-users' => []
]
];
return $this->createTemplate('users_template', $template);
}
// 获取模板
public function getTemplate($templateName) {
return $this->client->indices()->getTemplate(['name' => $templateName])->asArray();
}
// 删除模板
public function deleteTemplate($templateName) {
return $this->client->indices()->deleteTemplate(['name' => $templateName])->asArray();
}
// 获取所有模板
public function getAllTemplates() {
return $this->client->indices()->getTemplate()->asArray();
}
// 更新模板
public function updateTemplate($templateName, $newTemplate) {
return $this->createTemplate($templateName, $newTemplate);
}
}
索引生命周期管理
class IndexManager {
// 创建生命周期策略
public function createILMPolicy($policyName, $policy) {
$params = [
'name' => $policyName,
'body' => $policy
];
return $this->client->ilm()->putLifecycle($params)->asArray();
}
// 创建示例生命周期策略
public function createDefaultILMPolicy() {
$policy = [
'policy' => [
'phases' => [
'hot' => [
'actions' => [
'rollover' => [
'max_size' => '50gb',
'max_age' => '30d'
]
]
],
'delete' => [
'min_age' => '90d',
'actions' => [
'delete' => []
]
]
]
]
];
return $this->createILMPolicy('logs-lifecycle', $policy);
}
// 应用生命周期策略到索引
public function applyILMPolicy($indexName, $policyName) {
$params = [
'index' => $indexName,
'body' => [
'index' => [
'lifecycle' => [
'name' => $policyName
]
]
]
];
return $this->updateIndexSettings($indexName, $params['body']);
}
}
索引删除操作
class IndexManager {
// 删除单个索引
public function deleteIndex($indexName) {
$params = ['index' => $indexName];
try {
return $this->client->indices()->delete($params)->asArray();
} catch (\Exception $e) {
throw new \Exception("删除索引失败: " . $e->getMessage());
}
}
// 删除多个索引
public function deleteIndices($indices) {
$params = ['index' => implode(',', $indices)];
return $this->client->indices()->delete($params)->asArray();
}
// 按模式删除索引
public function deleteByPattern($pattern) {
$allIndices = $this->getAllIndices();
$toDelete = [];
foreach (array_keys($allIndices) as $indexName) {
if (fnmatch($pattern, $indexName)) {
$toDelete[] = $indexName;
}
}
if (!empty($toDelete)) {
return $this->deleteIndices($toDelete);
}
return ['deleted' => 0, 'message' => '没有匹配的索引'];
}
// 清理旧索引(基于日期)
public function cleanupOldIndices($prefix, $daysToKeep) {
$allIndices = $this->getAllIndices();
$cutoffDate = strtotime("-{$daysToKeep} days");
$toDelete = [];
foreach (array_keys($allIndices) as $indexName) {
if (strpos($indexName, $prefix) === 0) {
$dateStr = str_replace($prefix, '', $indexName);
$timestamp = strtotime($dateStr);
if ($timestamp && $timestamp < $cutoffDate) {
$toDelete[] = $indexName;
}
}
}
if (!empty($toDelete)) {
return $this->deleteIndices($toDelete);
}
return ['deleted' => 0, 'message' => '没有需要清理的索引'];
}
}
索引重建(Reindex)
class IndexManager {
// 重建索引
public function reindex($sourceIndex, $targetIndex, $query = []) {
$params = [
'body' => [
'source' => ['index' => $sourceIndex],
'dest' => ['index' => $targetIndex]
]
];
if (!empty($query)) {
$params['body']['source']['query'] = $query;
}
return $this->client->reindex($params)->asArray();
}
// 异步重建索引
public function reindexAsync($sourceIndex, $targetIndex, $query = []) {
$params = [
'wait_for_completion' => false,
'body' => [
'source' => ['index' => $sourceIndex],
'dest' => ['index' => $targetIndex]
]
];
if (!empty($query)) {
$params['body']['source']['query'] = $query;
}
return $this->client->reindex($params)->asArray();
}
// 获取重建任务状态
public function getTaskStatus($taskId) {
return $this->client->tasks()->get(['task_id' => $taskId])->asArray();
}
// 取消重建任务
public function cancelTask($taskId) {
return $this->client->tasks()->cancel(['task_id' => $taskId])->asArray();
}
// 零停机索引重建(使用别名)
public function reindexWithAlias($indexName, $newIndexPrefix, $aliasName) {
$newIndexName = $newIndexPrefix . '-' . date('Y-m-d-H-i-s');
// 1. 创建新索引
$oldMapping = $this->getIndexMapping($indexName);
$oldSettings = $this->getIndexSettings($indexName);
// 只复制必要设置
$settings = $oldSettings[$indexName]['settings']['index'];
unset($settings['uuid'], $settings['creation_date'], $settings['version']);
$this->createIndexIfNotExists($newIndexName, $oldMapping[$indexName]['mappings'], $settings);
// 2. 复制数据
$this->reindex($indexName, $newIndexName);
// 3. 切换别名
$this->switchAlias($indexName, $newIndexName, $aliasName);
// 4. 删除旧索引
$this->deleteIndex($indexName);
return $newIndexName;
}
}
完整使用示例
class ElasticsearchDemo {
public function run() {
// 初始化
$esManager = new ElasticsearchManager(['https://localhost:9200']);
$client = $esManager->getClient();
$indexManager = new IndexManager($client);
// 1. 创建索引
echo "=== 创建索引 ===\n";
$result = $indexManager->createUserIndex();
echo json_encode($result, JSON_PRETTY_PRINT) . "\n";
// 2. 检查索引是否存在
echo "=== 检查索引 ===\n";
$exists = $indexManager->indexExists('users');
echo "users索引存在: " . ($exists ? '是' : '否') . "\n";
// 3. 获取索引信息
echo "=== 获取索引信息 ===\n";
$info = $indexManager->getIndexInfo('users');
echo json_encode($info, JSON_PRETTY_PRINT) . "\n";
// 4. 添加映射字段
echo "=== 添加映射字段 ===\n";
$result = $indexManager->addMapping('users', [
'phone' => ['type' => 'keyword']
]);
echo json_encode($result, JSON_PRETTY_PRINT) . "\n";
// 5. 创建别名
echo "=== 创建别名 ===\n";
$result = $indexManager->createAlias('users', 'all-users');
echo json_encode($result, JSON_PRETTY_PRINT) . "\n";
// 6. 创建索引模板
echo "=== 创建索引模板 ===\n";
$result = $indexManager->createUserTemplate();
echo json_encode($result, JSON_PRETTY_PRINT) . "\n";
// 7. 获取索引统计
echo "=== 获取索引统计 ===\n";
$stats = $indexManager->getIndexStats('users');
echo json_encode($stats, JSON_PRETTY_PRINT) . "\n";
// 8. 清理示例(最后执行)
// echo "=== 清理索引 ===\n";
// $indexManager->deleteIndex('users');
// $indexManager->deleteTemplate('users_template');
}
}
// 执行示例
$demo = new ElasticsearchDemo();
$demo->run();
最佳实践建议
class IndexBestPractices {
// 索引命名规范
public function namingConventions() {
return [
'格式' => '<prefix>-<type>-<date>',
'示例' => [
'logs-app-2024-01-01',
'logs-system-2024-01-01',
'users-v1',
'transactions-2024-01'
],
'建议' => [
'使用小写字母',
'使用连字符分隔',
'避免特殊字符',
'包含日期用于区分'
]
];
}
// 分片设置建议
public function shardRecommendations() {
return [
'分片数量' => '每个分片大小控制在20-50GB',
'副本数量' => '生产环境至少1个副本',
'计算公式' => '分片数 = 预计存储量 / 30GB',
'注意事项' => [
'分片数量创建后不可修改',
'分片过多会降低搜索性能',
'分片过少会影响扩展性'
]
];
}
// 映射设计建议
public function mappingDesign() {
return [
'字段类型' => [
'避免使用text类型做精确匹配',
'使用keyword类型做精确匹配',
'数值字段使用合适的数值类型'
],
'分析器' => [
'中文使用ik_max_word或smartcn',
'英文使用standard分析器',
'自定义分析器适合特殊需求'
],
'索引选项' => [
'不需要搜索的字段关闭index',
'需要排序的字段使用doc_values',
'大字段考虑关闭store'
]
];
}
// 索引维护建议
public function maintenanceRecommendations() {
return [
'定期操作' => [
'优化:force merge',
'清理:删除过期索引',
'备份:定期做快照',
'监控:查看索引健康度'
],
'监控指标' => [
'索引大小',
'文档数量',
'查询延迟',
'磁盘使用率'
],
'紧急处理' => [
'索引只读:处理磁盘满',
'强制刷新:处理数据不一致',
'重建索引:解决映射问题'
]
];
}
}
这个完整的PHP Elasticsearch索引管理指南涵盖了:
- 基础操作:创建、删除、查询索引
- 配置管理:设置、映射、别名
- 高级功能:模板、生命周期管理
- 维护操作:重建、优化、清理
- 最佳实践:命名规范、分片设计等
建议根据实际需求选择合适的操作,并做好错误处理和日志记录,在生产环境中要注意索引的备份和恢复策略。