如何使用 Elasticsearch 让 PHP 运行存储业务有 100 倍的性能提升

953 阅读10分钟

今天我要跟大家分享一个可以让你的 PHP 应用性能大幅提升的秘密武器——Elasticsearch(简称 ES)。无论你是开发新项目还是优化现有应用,学会使用 ES 都能帮助你在数据存储和查询方面取得显著的性能提升。本文将通过实例代码,详细阐述如何使用 ES 提升 PHP 存储业务的性能,帮助大家告别“卡顿”的烦恼。

什么是 Elasticsearch?

首先,给大家简单介绍一下 Elasticsearch。它是一个分布式的搜索和分析引擎,基于 Apache Lucene 构建,特别适合处理大规模数据。ES 在处理全文搜索、结构化搜索、分析和可视化方面表现优异,是现代应用中常用的数据处理工具。

为什么选择 Elasticsearch?

你可能会问,为什么要选择 ES 呢?这里有几个重要的理由:

  1. 高性能:ES 可以在毫秒级别返回查询结果,大幅提升应用的响应速度。
  2. 强大的查询功能:支持全文搜索、结构化搜索等多种查询方式。
  3. 分布式架构:能够轻松扩展,处理海量数据。
  4. 实时数据处理:支持实时数据索引和查询,适合需要即时处理和分析数据的应用。
  5. 丰富的生态系统:与众多大数据工具兼容,如 Kibana、Logstash 等,形成完整的数据处理解决方案。

使用 Elasticsearch 提升 PHP 应用性能

接下来,我们进入实战环节,通过一个简单的示例,展示如何使用 ES 提升 PHP 应用的存储和查询性能。

环境准备

首先,确保你已经安装并配置好 Elasticsearch 和 PHP。你可以使用 Composer 安装官方的 PHP 客户端:


composer require elasticsearch/elasticsearch

建立连接

在 PHP 中建立与 Elasticsearch 的连接非常简单:


require 'vendor/autoload.php';

use Elasticsearch\ClientBuilder;

$client = ClientBuilder::create()->build();

创建索引

接下来,我们要创建一个索引(相当于数据库中的表)来存储数据:


$params = [
    'index' => 'my_index',
    'body' => [
        'mappings' => [
            'properties' => [
                'name' => [
                    'type' => 'text'
                ],
                'age' => [
                    'type' => 'integer'
                ]
            ]
        ]
    ]
];

$response = $client->indices()->create($params);
print_r($response);

数据存储

有了索引之后,我们可以开始存储数据了:


$params = [
    'index' => 'my_index',
    'body' => [
        'name' => 'John Doe',
        'age' => 29
    ]
];

$response = $client->index($params);
print_r($response);

数据查询

存储好数据后,我们可以使用 ES 强大的查询功能:


$params = [
    'index' => 'my_index',
    'body' => [
        'query' => [
            'match' => [
                'name' => 'John Doe'
            ]
        ]
    ]
];

$response = $client->search($params);
print_r($response);

小红书上的经验分享

接下来,我们从多方面探讨如何利用 Elasticsearch 提升 PHP 应用的性能,分享一些实践经验和心得体会。

快速响应,提高用户体验

通过使用 Elasticsearch,数据查询的响应时间可以大幅缩短,从传统数据库的秒级甚至分钟级,直接提升到毫秒级别。这种提升对用户体验的改善是显而易见的。试想一下,用户再也不用等待漫长的加载时间,点击搜索按钮后,结果几乎瞬间就能呈现。这对于电商、社交平台等需要频繁搜索的应用来说,用户体验将会大大提升。

扩展性强,轻松应对大数据

ES 的分布式架构让你可以轻松扩展,当数据量增长时,只需要增加节点即可。这对于业务快速增长的公司来说,简直就是福音。传统的关系型数据库在面对数据量迅速增加时,往往需要进行复杂的分库分表操作,甚至需要重新设计数据库架构,而 ES 则能通过增加节点轻松应对。

强大的全文搜索功能

ES 的全文搜索功能极其强大,可以提供非常精准和快速的搜索结果。对于需要频繁搜索的应用,例如电商网站、社交媒体等,ES 可以显著提高搜索的精确度和速度。ES 支持多种查询类型,包括精确匹配、模糊匹配、范围查询等,能够满足各种复杂的搜索需求。

实时数据分析

ES 不仅仅是一个搜索引擎,还支持实时的数据分析功能。通过聚合查询,你可以快速生成报表,实时监控业务数据。例如,在电商网站中,你可以实时统计商品的销量、用户的搜索习惯等,从而做出更精准的业务决策。

实例代码详解

创建索引模板

在实际应用中,我们可能需要定义多个索引,为了简化配置,我们可以使用索引模板:


$params = [
    'index' => 'my_template',
    'body' => [
        'index_patterns' => ['my_index_*'],
        'settings' => [
            'number_of_shards' => 1,
            'number_of_replicas' => 1
        ],
        'mappings' => [
            'properties' => [
                'name' => [
                    'type' => 'text'
                ],
                'age' => [
                    'type' => 'integer'
                ]
            ]
        ]
    ]
];

$response = $client->indices()->putTemplate($params);
print_r($response);

批量插入数据

为了提高数据写入的效率,我们可以使用批量插入(bulk)操作:


$params = ['body' => []];

for ($i = 1; $i <= 1000; $i++) {
    $params['body'][] = [
        'index' => [
            '_index' => 'my_index',
            '_id' => $i
        ]
    ];
    $params['body'][] = [
        'name' => 'User ' . $i,
        'age' => rand(18, 65)
    ];
}

$response = $client->bulk($params);
print_r($response);

复杂查询

ES 支持非常复杂的查询,例如多条件组合查询、排序、分页等:


$params = [
    'index' => 'my_index',
    'body' => [
        'query' => [
            'bool' => [
                'must' => [
                    'match' => ['name' => 'User']
                ],
                'filter' => [
                    'range' => [
                        'age' => [
                            'gte' => 30,
                            'lte' => 40
                        ]
                    ]
                ]
            ]
        ],
        'sort' => [
            'age' => ['order' => 'asc']
        ],
        'from' => 0,
        'size' => 10
    ]
];

$response = $client->search($params);
print_r($response);

日志分析

ES 也非常适合日志分析。通过 Logstash 或 Filebeat 等工具,你可以轻松将应用日志收集到 ES 中,然后使用 Kibana 进行可视化分析。这样,你可以实时监控系统的运行状态,快速定位问题,提高运维效率。

使用 Logstash 将日志导入 ES

安装和配置 Logstash:

bin/logstash -e 'input { stdin { } } output { elasticsearch { hosts => ["localhost:9200"] } }'

配置 Logstash 管道,将日志文件导入 ES:


input {
    file {
        path => "/path/to/logfile.log"
        start_position => "beginning"
    }
}

filter {
    grok {
        match => { "message" => "%{COMBINEDAPACHELOG}" }
    }
}

output {
    elasticsearch {
        hosts => ["localhost:9200"]
        index => "logs-%{+YYYY.MM.dd}"
    }
}

数据备份与恢复

在生产环境中,数据备份和恢复是非常重要的环节。ES 提供了简单的快照与恢复功能,可以方便地进行数据备份和恢复。

创建快照仓库

首先,我们需要创建一个快照仓库,用于存储备份:


$params = [
    'repository' => 'my_backup',
    'body' => [
        'type' => 'fs',
        'settings' => [
            'location' => '/mount/backups/my_backup',
            'compress' => true
        ]
    ]
];

$response = $client->snapshot()->createRepository($params);
print_r($response);

创建快照

创建快照用于备份当前数据:

$params = [
    'repository' => 'my_backup',
    'snapshot' => 'snapshot_1',
    'body' => [
    'indices' => 'my_index
    'indices' => 'my_index', 
    'ignore_unavailable' => true, 
    'include_global_state' => false 
    ] 
    ]; 
$response = $client->snapshot()->create($params);
print_r($response);

恢复快照

当需要恢复数据时,可以使用已经创建的快照:


$params = [
    'repository' => 'my_backup',
    'snapshot' => 'snapshot_1',
    'body' => [
        'indices' => 'my_index',
        'ignore_unavailable' => true,
        'include_global_state' => false
    ]
];

$response = $client->snapshot()->restore($params);
print_r($response);

优化性能的最佳实践

为了充分发挥 Elasticsearch 的性能优势,这里有一些最佳实践建议:

调整分片数

Elasticsearch 默认会为每个索引创建 5 个主分片和 1 个副本分片。根据实际数据量和查询负载,可以适当调整分片数:


$params = [
    'index' => 'my_index',
    'body' => [
        'settings' => [
            'number_of_shards' => 3,
            'number_of_replicas' => 1
        ]
    ]
];

$response = $client->indices()->create($params);
print_r($response);

使用适当的数据类型

在定义索引的 mappings 时,尽量选择合适的数据类型。例如,对于数值类型,选择 integer 或 long,而不是 text。这样可以减少存储空间,提高查询性能。

启用索引刷新

ES 默认会每隔 1 秒刷新一次索引。对于写操作频繁的场景,可以调整刷新间隔,以提高写入性能:


$params = [
    'index' => 'my_index',
    'body' => [
        'settings' => [
            'refresh_interval' => '30s'
        ]
    ]
];

$response = $client->indices()->putSettings($params);
print_r($response);

合理使用缓存

ES 有多种缓存机制,可以显著提高查询性能。使用合理的缓存策略,可以减少磁盘 I/O,提高响应速度。例如,对于频繁使用的查询结果,可以使用请求缓存:


$params = [
    'index' => 'my_index',
    'body' => [
        'query_cache' => [
            'enabled' => true
        ]
    ]
];

$response = $client->indices()->putSettings($params);
print_r($response);

集成 Kibana 进行数据可视化

Kibana 是 Elasticsearch 的数据可视化工具。通过 Kibana,可以轻松创建各种图表和仪表盘,对数据进行可视化分析。

安装和配置 Kibana

首先,下载并安装 Kibana。安装完成后,编辑配置文件 kibana.yml,指定 Elasticsearch 的地址:


elasticsearch.hosts: ["http://localhost:9200"]

启动 Kibana 后,打开浏览器访问 http://localhost:5601,即可进入 Kibana 控制台。

创建索引模式

在 Kibana 中,创建一个索引模式来匹配 Elasticsearch 的索引:

  1. 打开 Kibana 控制台,点击 "Management" -> "Index Patterns"。
  2. 点击 "Create index pattern",输入索引模式,例如 my_index*
  3. 选择时间字段,点击 "Create index pattern"。

创建可视化

  1. 打开 Kibana 控制台,点击 "Visualize" -> "Create new visualization"。
  2. 选择一个可视化类型,例如 "Line chart"。
  3. 选择数据源,设置查询条件和显示字段,即可创建图表。

实时数据处理与分析

ES 支持实时数据处理和分析,这对于需要即时处理和分析数据的应用非常重要。例如,在电商网站中,可以实时统计商品的销量、用户的搜索习惯等,从而做出更精准的业务决策。

使用 Logstash 实时处理数据

Logstash 是一个数据收集和处理的工具,可以将各种数据源的数据实时导入 Elasticsearch。以下是一个简单的 Logstash 配置示例,将 MySQL 数据库的数据实时导入 ES:


input {
    jdbc {
        jdbc_connection_string => "jdbc:mysql://localhost:3306/my_database"
        jdbc_user => "user"
        jdbc_password => "password"
        jdbc_driver_class => "com.mysql.jdbc.Driver"
        statement => "SELECT * FROM my_table"
    }
}

output {
    elasticsearch {
        hosts => ["localhost:9200"]
        index => "my_index"
    }
}

启动 Logstash 后,它会定期从 MySQL 数据库中读取数据并导入 ES,实现数据的实时同步。

应用场景与案例分析

接下来,我们通过几个实际应用场景,进一步探讨如何使用 Elasticsearch 提升 PHP 应用的性能。

电商网站搜索优化

在电商网站中,商品搜索是一个非常关键的功能。传统的关系型数据库在处理复杂搜索时往往效率低下,而 Elasticsearch 能够提供高效、精准的搜索服务。

示例代码:商品搜索

假设我们有一个电商网站,需要实现商品搜索功能。我们可以使用以下代码将商品数据导入 ES,并实现搜索功能:


// 导入商品数据
$params = [
    'index' => 'products',
    'body' => [
        'mappings' => [
            'properties' => [
                'name' => ['type' => 'text'],
                'description' => ['type' => 'text'],
                'price' => ['type' => 'float'],
                'category' => ['type' => 'keyword']
            ]
        ]
    ]
];

$response = $client->indices()->create($params);
print_r($response);

$products = [
    ['name' => 'Product 1', 'description' => 'Description 1', 'price' => 99.99, 'category' => 'Category A'],
    ['name' => 'Product 2', 'description' => 'Description 2', 'price' => 199.99, 'category' => 'Category B'],
    // ... 更多商品数据
];

foreach ($products as $product) {
    $params = [
        'index' => 'products',
        'body' => $product
    ];
    $client->index($params);
}

// 实现搜索功能
$searchParams = [
    'index' => 'products',
    'body' => [
        'query' => [
            'bool' => [
                'must' => [
                    'match' => ['name' => 'Product']
                ],
                'filter' => [
                    'term' => ['category' => 'Category A']
                ]
            ]
        ],
        'sort' => [
            'price' => ['order' => 'asc']
        ]
    ]
];

$response = $client->search($searchParams);
print_r($response);

社交平台实时分析

在社交平台中,用户的动态、评论等数据量巨大,实时分析这些数据对于了解用户行为和兴趣非常重要。Elasticsearch 的实时数据处理和分析能力,可以帮助我们快速分析用户动态。

示例代码:用户动态分析

假设我们有一个社交平台,需要实时分析用户的动态。我们可以使用以下代码实现数据的实时采集和分析:


// 创建索引
$params = [
    'index' => 'user_activities',
    'body' => [
        'mappings' => [
            'properties' => [
                'user_id' => ['type' => 'integer'],
                'activity' => ['type' => 'text'],
                'timestamp' => ['type' => 'date']
            ]
        ]
    ]
];

$response = $client->indices()->create($params);
print_r($response);

// 实时采集用户动态
$activities = [
    ['user_id' => 1, 'activity' => 'Posted a status update', 'timestamp' => '2023-06-01T12:34:56Z'],
    ['user_id' => 2, 'activity' => 'Liked a post', 'timestamp' => '2023-06-01T12:35:56Z'],
    // ... 更多用户动态
];

foreach ($activities as $activity) {
    $params = [
        'index' => 'user_activities',
        'body' => $activity
    ];
    $client->index($params);
}

// 实时分析用户动态
$analysisParams = [
    'index' => 'user_activities',
    'body' => [
        'query' => [
            'range' => [
                'timestamp' => [
                    'gte' => 'now-1h/h',
                    'lt' => 'now/h'
                ]
            ]
        ],
        'aggs' => [
            'activity_count' => [
                'terms' => [
                    'field' => 'activity.keyword'
                ]
            ]
        ]
    ]
];

$response = $client->search($analysisParams);
print_r($response);

深入了解 Elasticsearch 内部机制

为了更好地使用 Elasticsearch,我们还需要了解一些其内部机制和工作原理。

索引与文档

在 Elasticsearch 中,数据以文档的形式存储在索引中。每个文档是一个 JSON 对象,包含多个字段。索引类似于关系型数据库中的表,在索引中存储的数据文档通过字段进行结构化和非结构化的查询。一个索引可以包含许多文档,而每个文档可以包含不同的字段。了解索引与文档的关系有助于我们更好地设计数据模型和查询策略。

分片与副本

Elasticsearch 的分布式特性主要通过分片和副本机制实现。每个索引可以分为多个分片(shard),分片是数据的基本存储单元。每个分片又可以有多个副本(replica),副本用于提高数据的可用性和容错能力。

主分片与副本分片
  • 主分片(Primary Shard) :存储原始数据,是数据写入的入口。
  • 副本分片(Replica Shard) :是主分片的副本,用于提高数据的读取性能和容灾能力。

默认情况下,每个索引有 5 个主分片和 1 个副本分片(每个主分片有一个副本)。你可以在创建索引时根据实际需求调整这些参数:


$params = [
    'index' => 'my_index',
    'body' => [
        'settings' => [
            'number_of_shards' => 3,
            'number_of_replicas' => 2
        ]
    ]
];

$response = $client->indices()->create($params);
print_r($response);

倒排索引

倒排索引是 Elasticsearch 高效搜索的核心机制。它将文档中的词条(term)映射到包含这些词条的文档 ID 列表,从而支持快速的全文搜索。了解倒排索引的工作原理,有助于我们优化查询性能。

安全与权限管理

在生产环境中,安全和权限管理至关重要。Elasticsearch 提供了一套完善的安全机制,包括用户认证、权限控制、数据加密等。

用户认证与权限控制

通过 Elasticsearch 的安全插件,可以实现用户认证和权限控制。你可以创建不同的用户角色,并为每个角色分配不同的权限。例如,管理员角色可以对所有索引进行读写操作,而普通用户只能读取特定索引的数据。

示例代码:创建用户和角色

假设我们需要创建一个普通用户角色,并为其分配读取权限:


# 创建角色
curl -X POST "localhost:9200/_security/role/read_only_role" -H "Content-Type: application/json" -d'
{
  "indices": [
    {
      "names": [ "my_index" ],
      "privileges": [ "read" ]
    }
  ]
}
'

# 创建用户并分配角色
curl -X POST "localhost:9200/_security/user/read_only_user" -H "Content-Type: application/json" -d'
{
  "password" : "password",
  "roles" : [ "read_only_role" ],
  "full_name" : "Read Only User",
  "email" : "readonly@example.com"
}
'

数据加密

为了确保数据传输的安全性,可以启用 SSL/TLS 加密。你需要生成和配置 SSL 证书,并在 Elasticsearch 配置文件中启用加密传输。

示例代码:配置 SSL/TLS

编辑 Elasticsearch 配置文件 elasticsearch.yml,启用 SSL/TLS 加密:


xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: /path/to/your/keystore.jks
xpack.security.transport.ssl.truststore.path: /path/to/your/truststore.jks
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: /path/to/your/keystore.jks
xpack.security.http.ssl.truststore.path: /path/to/your/truststore.jks

实战案例:使用 Elasticsearch 优化一个大型 PHP 应用

通过一个实际案例,展示如何使用 Elasticsearch 优化一个大型 PHP 应用的性能。

背景

假设我们有一个大型社交媒体平台,用户可以发布动态、评论、点赞等。随着用户数量的增加,传统关系型数据库在处理用户动态查询和分析时变得越来越慢。我们决定使用 Elasticsearch 来优化数据存储和查询性能。

数据建模与索引设计

首先,我们需要设计适合 Elasticsearch 的数据模型和索引结构。假设每个用户动态包含以下字段:用户 ID、内容、发布时间、点赞数等。

创建索引

$params = [
    'index' => 'user_posts',
    'body' => [
        'mappings' => [
            'properties' => [
                'user_id' => ['type' => 'integer'],
                'content' => ['type' => 'text'],
                'created_at' => ['type' => 'date'],
                'likes' => ['type' => 'integer']
            ]
        ]
    ]
];

$response = $client->indices()->create($params);
print_r($response);

数据存储

将用户发布的动态数据存储到 Elasticsearch 中:


$post = [
    'user_id' => 1,
    'content' => 'This is a test post',
    'created_at' => '2023-06-01T12:34:56Z',
    'likes' => 10
];

$params = [
    'index' => 'user_posts',
    'body' => $post
];

$response = $client->index($params);
print_r($response);

数据查询与分析

用户动态查询:


$params = [
    'index' => 'user_posts',
    'body' => [
        'query' => [
            'match' => [
                'content' => 'test'
            ]
        ]
    ]
];

$response = $client->search($params);
print_r($response);

用户动态分析:

$analysisParams = [
    'index' => 'user_posts',
    'body' => [
        'query' => [
            'range' => [
                'created_at' => [
                    'gte' => 'now-1d/d',
                    'lt' => 'now/d'
                ]
            ]
        ],
        'aggs' => [
            'most_liked_posts' => [
                'terms' => [
                    'field' => 'likes',
                    'size' => 5
                ]
            ]
        ]
    ]
];

$response = $client->search($analysisParams);
print_r($response);

实时数据同步

为了确保数据的一致性,我们需要将 MySQL 数据库中的用户动态实时同步到 Elasticsearch。可以使用 Logstash 或者自己编写 PHP 脚本实现数据同步。

示例代码:PHP 实时同步脚本

// 连接 MySQL 数据库
$pdo = new PDO('mysql:host=localhost;dbname=my_database', 'user', 'password');

// 连接 Elasticsearch
$client = ClientBuilder::create()->build();

// 查询最新的用户动态
$query = $pdo->query("SELECT * FROM user_posts WHERE created_at > NOW() - INTERVAL 1 MINUTE");
$posts = $query->fetchAll(PDO::FETCH_ASSOC);

foreach ($posts as $post) {
    $params = [
        'index' => 'user_posts',
        'body' => $post
    ];
    $client->index($params);
}

结论

通过本文的详细介绍和实例代码,相信大家已经对如何使用 Elasticsearch 提升 PHP 应用的性能有了全面的了解。从数据存储、查询优化到实时数据分析,Elasticsearch 为我们提供了一整套高效的数据处理解决方案。希望大家能从中获得启发,并在实际项目中尝试应用。如果你有任何疑问或者心得,欢迎在评论区与我交流。记得点赞、收藏哦!谢谢大家!