PHP Elasticsearch 索引管理

wen PHP项目 3

本文目录导读:

PHP Elasticsearch 索引管理

  1. 安装和初始化
  2. 索引创建
  3. 索引查询和检查
  4. 索引更新和修改
  5. 索引别名管理
  6. 索引模板管理
  7. 索引生命周期管理
  8. 索引删除操作
  9. 索引重建(Reindex)
  10. 完整使用示例
  11. 最佳实践建议

我来提供一个完整的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索引管理指南涵盖了:

  1. 基础操作:创建、删除、查询索引
  2. 配置管理:设置、映射、别名
  3. 高级功能:模板、生命周期管理
  4. 维护操作:重建、优化、清理
  5. 最佳实践:命名规范、分片设计等

建议根据实际需求选择合适的操作,并做好错误处理和日志记录,在生产环境中要注意索引的备份和恢复策略。

抱歉,评论功能暂时关闭!