PHP 完全指南 / 第 23 章 — 性能优化
第 23 章 — 性能优化:OPcache、JIT、Profiling 与 Blackfire
23.1 OPcache
OPcache 将 PHP 脚本编译为字节码并缓存在共享内存中,避免每次请求重新解析和编译。
; php.ini 配置
[opcache]
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=256 ; MB
opcache.interned_strings_buffer=16 ; MB
opcache.max_accelerated_files=20000
opcache.max_wasted_percentage=10
opcache.validate_timestamps=0 ; 生产环境关闭(需要手动清除缓存)
opcache.revalidate_freq=0
opcache.save_comments=1
opcache.enable_file_override=1
; 预加载(PHP 7.4+)
opcache.preload=/var/www/app/preload.php
<?php
// preload.php — 预加载常用类
opcache_compile_file('/var/www/app/src/Models/User.php');
opcache_compile_file('/var/www/app/src/Services/AuthService.php');
OPcache 性能对比
| 场景 | 无 OPcache | 有 OPcache | 提升 |
|---|---|---|---|
| 简单 API | 5ms | 2ms | 60% |
| 复杂页面 | 50ms | 25ms | 50% |
| 启动时间 | 100ms | 10ms | 90% |
23.2 JIT 编译器
PHP 8.0 引入的 JIT(Just-In-Time)编译器将热点代码编译为机器码。
[opcache]
opcache.jit=1255 ; 模式配置
opcache.jit_buffer_size=128 ; MB
JIT 配置详解
| 字段 | 值 | 说明 |
|---|---|---|
| 第 1 位 | 1 | 启用 |
| 第 2 位 | 2 | 按请求触发 |
| 第 3 位 | 5 | 总是在调用时 JIT |
| 第 4 位 | 5 | 使用寄存器分配 |
JIT 主要受益于 CPU 密集型任务,对 I/O 密集型 Web 请求提升有限。
23.3 Profiling(性能分析)
23.3.1 Xdebug Profiling
[xdebug]
xdebug.mode=profile
xdebug.output_dir=/tmp/xdebug
xdebug.profiler_output_name=cachegrind.out.%p
xdebug.start_with_request=trigger ; 需要 ?XDEBUG_PROFILE=1 触发
23.3.2 Blackfire.io
# 安装 Blackfire
curl -sS https://packages.blackfire.io/gpg.key | sudo apt-key add -
echo "deb http://packages.blackfire.io/debian any main" | sudo tee /etc/apt/sources.list.d/blackfire.list
sudo apt update && sudo apt install blackfire-php
<?php
// 手动触发 Profiling
blackfire()->enable();
// 要分析的代码
blackfire()->disable();
23.4 优化技巧
23.4.1 数据库优化
<?php
// ❌ N+1 查询问题
$users = $pdo->query('SELECT * FROM users')->fetchAll();
foreach ($users as $user) {
$orders = $pdo->query("SELECT * FROM orders WHERE user_id = {$user['id']}")->fetchAll();
}
// ✅ 批量查询
$userIds = array_column($users, 'id');
$placeholders = implode(',', array_fill(0, count($userIds), '?'));
$stmt = $pdo->prepare("SELECT * FROM orders WHERE user_id IN ({$placeholders})");
$stmt->execute($userIds);
$allOrders = $stmt->fetchAll();
// 按 user_id 分组
$ordersByUser = [];
foreach ($allOrders as $order) {
$ordersByUser[$order['user_id']][] = $order;
}
23.4.2 缓存策略
<?php
class CacheManager
{
private Redis $redis;
public function remember(string $key, int $ttl, callable $callback): mixed
{
$cached = $this->redis->get($key);
if ($cached !== false) {
return json_decode($cached, true);
}
$value = $callback();
$this->redis->setex($key, $ttl, json_encode($value));
return $value;
}
}
// 使用
$users = $cache->remember('users:active', 3600, function () use ($db) {
return $db->query('SELECT * FROM users WHERE active = 1')->fetchAll();
});
23.4.3 代码级优化
<?php
// 1. 使用 strict_types(避免隐式转换开销)
declare(strict_types=1);
// 2. 避免在循环中调用函数
// ❌
for ($i = 0; $i < count($array); $i++) {}
// ✅
$length = count($array);
for ($i = 0; $i < $length; $i++) {}
// 3. 使用 isset 代替 array_key_exists
isset($array['key']); // 更快
// 4. 避免不必要的对象创建
// 5. 使用生成器处理大数据
// 6. 字符串拼接用 implode 代替 .=
// 7. 使用 SplFixedArray 替代普通数组(已知大小时)
$arr = new SplFixedArray(1000); // 性能更好
23.5 内存优化
<?php
// 监控内存使用
echo memory_get_usage(); // 当前使用量
echo memory_get_peak_usage(); // 峰值使用量
echo memory_get_usage(true); // 实际分配量
// 释放变量
unset($largeArray);
$largeArray = null; // 更快释放
// 生成器替代数组
function processLargeFile(string $file): Generator
{
$handle = fopen($file, 'r');
while (($line = fgets($handle)) !== false) {
yield $line;
}
fclose($handle);
}
// 流式处理
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$stmt = $pdo->query('SELECT * FROM huge_table');
while ($row = $stmt->fetch()) {
// 逐行处理
}
23.6 业务场景:性能监控
<?php
class PerformanceMonitor
{
private float $startTime;
private array $metrics = [];
public function __construct()
{
$this->startTime = microtime(true);
}
public function record(string $label, callable $callback): mixed
{
$start = microtime(true);
$startMemory = memory_get_usage();
$result = $callback();
$this->metrics[] = [
'label' => $label,
'elapsed_ms' => round((microtime(true) - $start) * 1000, 2),
'memory_bytes'=> memory_get_usage() - $startMemory,
];
return $result;
}
public function getReport(): array
{
return [
'total_elapsed_ms' => round((microtime(true) - $this->startTime) * 1000, 2),
'peak_memory' => memory_get_peak_usage(),
'metrics' => $this->metrics,
];
}
}
23.7 扩展阅读
上一章:第 22 章 — 安全 下一章:第 24 章 — 框架概览