PHP `Elasticsearch` `Shards` / `Replicas` / `Mapping` 调优与集群管理

各位听众,大家好!我是今天的讲师,咱们今天聊聊PHP结合Elasticsearch,特别是关于Shards(分片)、Replicas(副本)、Mapping(映射)的调优,以及集群管理的那些事儿。这就像烹饪一样,食材(数据)有了,火候(配置)得掌握好,才能做出美味佳肴(高性能搜索)。

一、 Elasticsearch 基础概念回顾:别再傻傻分不清

在正式开始“烹饪”之前,咱们先简单回顾几个Elasticsearch的基础概念,保证大家不会在接下来的内容里一脸懵逼。

  • Index(索引): 相当于数据库里的“表”,用来存储相关文档。比如说,你可以创建一个名为 products 的索引来存储你的产品信息。

  • Document(文档): 相当于数据库里的“行”,是可被索引的基本单元。每个文档都是一个JSON对象,包含多个字段。

  • Field(字段): 相当于数据库里的“列”,是文档中的一个属性。比如 product_namepricedescription 等。

  • Shards(分片): 一个索引会被分成多个分片,每个分片都是一个独立的Lucene实例。分片的主要目的是水平扩展,让你可以存储海量数据,并支持并行处理。 想象一下,你要搬100箱苹果,一个人搬肯定累死,分成10个人搬是不是轻松多了?每个分片就像其中一个人负责搬的10箱苹果。

  • Replicas(副本): 每个分片可以有多个副本。副本的主要目的是提高可用性和容错性。如果某个分片挂了,它的副本可以立即顶上,保证服务不中断。 还是搬苹果的例子,如果其中一个人病了,其他人可以顶替他的工作,保证苹果能按时搬完。

  • Mapping(映射): 定义了索引中每个字段的数据类型以及如何索引这些字段。就像给每个苹果贴上标签,告诉你这是什么品种、产自哪里、有多重。

二、 Shards(分片)调优:分多分少,是个问题

分片数量的选择直接影响到Elasticsearch的性能和可扩展性。分片太少,单个分片压力过大,查询慢;分片太多,管理成本高,资源浪费。

  • 分片过多带来的问题:

    • 资源浪费: 每个分片都是一个Lucene实例,会占用CPU、内存和磁盘空间。分片越多,资源消耗越大。
    • 管理成本增加: Elasticsearch需要管理更多的分片,增加了集群的开销。
    • 查询性能下降: 查询需要查询多个分片,然后合并结果,增加了查询的延迟。
  • 分片过少带来的问题:

    • 单点瓶颈: 单个分片承担了过多的数据和查询压力,容易成为性能瓶颈。
    • 扩展性受限: 无法充分利用集群的资源,难以应对数据量的增长。
  • 如何选择合适的分片数量?

    • 根据数据量估算: 通常建议每个分片的大小在 20GB 到 40GB 之间。如果你的数据量是 200GB,那么可以考虑创建 5 到 10 个分片。
    • 考虑硬件资源: 分片数量还受到硬件资源的限制。如果你的服务器CPU和内存都很充足,可以适当增加分片数量。
    • 进行性能测试: 通过实际的性能测试来验证分片数量是否合适。可以尝试不同的分片数量,然后观察查询的性能指标。
    • 使用公式估算: 一个比较常用的公式是:分片数量 = 节点数量 * (CPU核心数 / 2)。 但这只是一个参考,具体情况还需要根据实际情况进行调整。
  • 创建索引时指定分片数量:

    <?php
    require 'vendor/autoload.php'; // 引入 Composer 自动加载
    
    use ElasticsearchClientBuilder;
    
    $client = ClientBuilder::create()->build();
    
    $params = [
        'index' => 'products',
        'body' => [
            'settings' => [
                'number_of_shards' => 5, // 设置分片数量为5
                'number_of_replicas' => 1  // 设置副本数量为1
            ],
            'mappings' => [
                'properties' => [
                    'product_name' => ['type' => 'text'],
                    'price' => ['type' => 'float'],
                    'description' => ['type' => 'text']
                ]
            ]
        ]
    ];
    
    $response = $client->indices()->create($params);
    
    print_r($response);
    ?>

三、 Replicas(副本)调优:备份虽好,不要贪多

副本的主要作用是提高可用性和容错性。但是,副本也会占用额外的存储空间,并且在写入数据时,需要同步到所有副本,会增加写入延迟。

  • 副本过多带来的问题:

    • 存储空间浪费: 每个副本都会占用额外的存储空间。
    • 写入延迟增加: 写入数据时,需要同步到所有副本,增加了写入延迟。
    • 资源消耗增加: 同步副本需要消耗CPU和网络资源。
  • 副本过少带来的问题:

    • 可用性降低: 如果某个分片挂了,而且没有副本,那么该分片上的数据将无法访问。
    • 容错性降低: 如果某个节点挂了,可能会导致数据丢失。
  • 如何选择合适的副本数量?

    • 考虑可用性需求: 如果你的应用对可用性要求很高,可以增加副本数量。一般来说,至少需要设置一个副本。
    • 考虑硬件资源: 副本数量还受到硬件资源的限制。如果你的存储空间不足,可以适当减少副本数量。
    • 进行性能测试: 通过实际的性能测试来验证副本数量是否合适。可以尝试不同的副本数量,然后观察查询和写入的性能指标。
    • 一般建议: 通常建议设置 1 到 2 个副本。
  • 更新副本数量:

    <?php
    require 'vendor/autoload.php';
    
    use ElasticsearchClientBuilder;
    
    $client = ClientBuilder::create()->build();
    
    $params = [
        'index' => 'products',
        'body' => [
            'number_of_replicas' => 2 // 修改副本数量为2
        ]
    ];
    
    $response = $client->indices()->putSettings($params);
    
    print_r($response);
    ?>

四、 Mapping(映射)调优:贴标签也是门学问

Mapping定义了索引中每个字段的数据类型以及如何索引这些字段。正确的Mapping可以提高搜索的效率和准确性。

  • 常见的数据类型:

    • text:用于存储文本数据,会被分词器处理。
    • keyword:用于存储精确匹配的字符串,不会被分词器处理。
    • integer:用于存储整数。
    • float:用于存储浮点数。
    • date:用于存储日期和时间。
    • boolean:用于存储布尔值。
    • geo_point:用于存储地理坐标。
  • Mapping 调优策略:

    • 选择合适的数据类型: 根据字段的实际用途选择合适的数据类型。比如,如果一个字段只需要进行精确匹配,那么应该选择 keyword 类型,而不是 text 类型。
    • 使用合适的分词器: 分词器用于将文本数据分割成多个词条。选择合适的分词器可以提高搜索的准确性。常用的分词器包括 standardik_max_wordik_smart 等。
    • 禁用不需要的索引: 对于不需要进行搜索的字段,可以禁用索引,以减少存储空间和提高写入性能。
    • 使用动态 Mapping: Elasticsearch 默认会根据文档中的字段自动创建 Mapping。但是,动态 Mapping 可能会导致数据类型不正确或者索引方式不合理。建议手动定义 Mapping,以确保数据类型和索引方式符合预期。
  • 手动定义 Mapping:

    <?php
    require 'vendor/autoload.php';
    
    use ElasticsearchClientBuilder;
    
    $client = ClientBuilder::create()->build();
    
    $params = [
        'index' => 'products',
        'body' => [
            'mappings' => [
                'properties' => [
                    'product_name' => [
                        'type' => 'text',
                        'analyzer' => 'ik_max_word' // 使用 ik_max_word 分词器
                    ],
                    'price' => ['type' => 'float'],
                    'description' => [
                        'type' => 'text',
                        'index' => false // 禁用索引
                    ],
                    'create_time' => ['type' => 'date', 'format' => 'yyyy-MM-dd HH:mm:ss']
                ]
            ]
        ]
    ];
    
    $response = $client->indices()->putMapping($params);
    
    print_r($response);
    ?>
  • 动态 Mapping 设置:

    <?php
    require 'vendor/autoload.php';
    
    use ElasticsearchClientBuilder;
    
    $client = ClientBuilder::create()->build();
    
    $params = [
        'index' => 'my_index',
        'body' => [
            'mappings' => [
                'dynamic' => 'strict', //  strict - 如果尝试索引未知字段,则抛出异常。true - 允许动态添加字段(默认行为)。false - 忽略新字段。
                'properties' => [
                    'known_field' => ['type' => 'text']
                ]
            ]
        ]
    ];
    
    $response = $client->indices()->create($params);
    
    print_r($response);
    ?>

五、 Elasticsearch 集群管理:运筹帷幄,决胜千里

Elasticsearch 集群管理涉及到节点管理、监控、备份和恢复等方面。一个稳定可靠的集群是高性能搜索的基础。

  • 节点管理:

    • 添加节点: 可以通过修改 Elasticsearch 的配置文件,然后启动新的 Elasticsearch 实例来添加节点。
    • 删除节点: 可以先将节点上的分片迁移到其他节点,然后关闭该节点。
    • 重启节点: 可以先关闭节点,然后重新启动。
  • 监控:

    • 使用 Elasticsearch API: Elasticsearch 提供了丰富的 API,可以用于监控集群的状态、节点的状态、索引的状态等。
    • 使用监控工具: 常用的 Elasticsearch 监控工具包括 Kibana、ElasticHQ、Grafana 等。
  • 备份和恢复:

    • 使用 Snapshot API: Elasticsearch 提供了 Snapshot API,可以用于创建索引的快照。快照可以存储在本地文件系统或者远程存储服务(如 S3)上。
    • 使用 Curator: Curator 是一个用于管理 Elasticsearch 集群的 Python 库。它可以用于创建快照、删除快照、恢复索引等。
  • 一些常用的集群管理命令(PHP):

    • 获取集群健康状态:

      <?php
      require 'vendor/autoload.php';
      
      use ElasticsearchClientBuilder;
      
      $client = ClientBuilder::create()->build();
      
      $params = [
          'index' => 'products',
      ];
      
      $response = $client->cluster()->health($params);
      
      print_r($response);
      ?>
    • 获取节点信息:

      <?php
      require 'vendor/autoload.php';
      
      use ElasticsearchClientBuilder;
      
      $client = ClientBuilder::create()->build();
      
      $response = $client->nodes()->info();
      
      print_r($response);
      ?>
    • 创建快照:

      <?php
      require 'vendor/autoload.php';
      
      use ElasticsearchClientBuilder;
      
      $client = ClientBuilder::create()->build();
      
      $params = [
          'repository' => 'my_backup',  // 仓库名称,需要在 elasticsearch.yml 中配置
          'snapshot' => 'snapshot_1',   // 快照名称
          'body' => [
              'indices' => 'products'  // 要备份的索引
          ]
      ];
      
      $response = $client->snapshot()->create($params);
      
      print_r($response);
      ?>

六、 PHP 代码示例: Elasticsearch 基本操作

为了让大家更直观地了解如何在 PHP 中使用 Elasticsearch,这里提供一些基本的代码示例。

  • 索引文档:

    <?php
    require 'vendor/autoload.php';
    
    use ElasticsearchClientBuilder;
    
    $client = ClientBuilder::create()->build();
    
    $params = [
        'index' => 'products',
        'id'    => '1',
        'body'  => [
            'product_name' => 'iPhone 13',
            'price' => 7999,
            'description' => 'The latest iPhone'
        ]
    ];
    
    $response = $client->index($params);
    
    print_r($response);
    ?>
  • 获取文档:

    <?php
    require 'vendor/autoload.php';
    
    use ElasticsearchClientBuilder;
    
    $client = ClientBuilder::create()->build();
    
    $params = [
        'index' => 'products',
        'id'    => '1'
    ];
    
    $response = $client->get($params);
    
    print_r($response);
    ?>
  • 搜索文档:

    <?php
    require 'vendor/autoload.php';
    
    use ElasticsearchClientBuilder;
    
    $client = ClientBuilder::create()->build();
    
    $params = [
        'index' => 'products',
        'body' => [
            'query' => [
                'match' => [
                    'product_name' => 'iPhone'
                ]
            ]
        ]
    ];
    
    $response = $client->search($params);
    
    print_r($response);
    ?>

七、总结与最佳实践

好了,今天的“烹饪”课就到这里。 咱们一起学习了 Elasticsearch 的 Shards、Replicas、Mapping 的调优,以及集群管理的一些基本知识。

  • 记住: 没有万能的配置,只有最适合你的配置。需要根据实际情况进行调整和优化。
  • 最佳实践:

    • 监控: 密切关注集群的健康状态和性能指标,及时发现和解决问题。
    • 备份: 定期进行数据备份,以防止数据丢失。
    • 测试: 在生产环境之前,进行充分的测试,确保配置的正确性和性能。
    • 文档: 编写清晰的文档,记录配置和操作步骤,方便维护和管理。

希望今天的分享能帮助大家更好地使用 PHP 和 Elasticsearch,打造高性能的搜索应用。 谢谢大家! 祝大家编程愉快!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注