直接选pdo,除非明确依赖mysql特有功能(如mysqli_poll);pdo预处理复用、统一异常模式、驱动抽象更利于压测变量控制,但需手动启用持久连接并避免连接数超限。

PHP 基准测试脚本该用 mysqli 还是 PDO?
直接选 PDO,除非你明确依赖 MySQL 特有功能(比如 mysqli_poll)。PDO 的预处理语句复用、错误模式统一(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)、驱动抽象能力,在多轮压测中更易控制变量。但注意:默认 PDO 不启用持久连接,要手动加 PDO::ATTR_PERSISTENT => true,否则每次 new PDO() 都会建立新 TCP 连接,测的其实是连接开销而非 SQL 执行性能。
常见错误:在循环里反复 new PDO() 却没关掉 PDO::ATTR_PERSISTENT,结果数据库连接数暴涨,max_connections 很快打满,错误变成 "SQLSTATE[HY000] [1040] Too many connections"。
- 压测前先确认 MySQL 的
max_connections值(SHOW VARIABLES LIKE 'max_connections';),脚本并发数别超过它 × 0.7 - 用
PDO时,显式设置PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4",避免字符集隐式转换拖慢查询 - 不要在压测循环里做
$pdo->prepare()—— 预处理语句对象应复用,只调$stmt->execute($params)
如何模拟真实并发而不被 PHP 自身阻塞?
PHP 默认是同步阻塞 I/O,file_get_contents 或 curl_exec 会卡住整个进程。想测“100 个用户同时查订单”,不能写 100 个 for 循环串行执行 —— 那只是 100 次排队请求。
真正可行的路只有两条:
- 用
pcntl_fork()启多个子进程(Linux/macOS 可用),每个子进程独立连 DB 并跑固定次数的查询;父进程用pcntl_wait()收集耗时。注意:Windows 不支持pcntl,且 fork 后未exit()容易导致僵尸进程 - 用
cURL multi(curl_multi_init())发异步 HTTP 请求到一个轻量 API 端点(比如/api/bench?op=select&count=50),让 Web 服务器(如 PHP-FPM)本身处理并发。这种方式绕过 PHP CLI 的单线程限制,更贴近生产环境
别碰 sleep() + 多线程(PHP 没原生线程),也别信某些“协程压测库”——它们底层仍是同步调度,只是语法糖。
INSERT 基准测试为什么总比 SELECT 慢得多?
不是代码问题,是 InnoDB 的事务和刷盘机制在起作用。INSERT 默认走事务,每条都触发 redo log 写入,如果没关 innodb_flush_log_at_trx_commit=1,性能会断崖下跌。而 SELECT 是纯内存/缓冲池操作,不涉及磁盘 IO。
压测 INSERT 时必须调整策略:
- 把多条
INSERT合并在一个事务里($pdo->beginTransaction()→ 批量execute()→$pdo->commit()),单事务插 100 行比 100 个单行事务快 10 倍以上 - 用
INSERT INTO ... VALUES (),(),()多值语法,而不是循环调INSERT INTO ... VALUES (?) - 确认 MySQL 的
innodb_buffer_pool_size足够大(建议设为物理内存 50%–75%),否则频繁刷脏页会拖垮吞吐
如果用 LOAD DATA INFILE 测试导入性能,记得提前关掉唯一键检查(SET UNIQUE_CHECKS=0)和自动提交(SET AUTOCOMMIT=0),否则速度差异会误导你对应用层 SQL 性能的判断。
怎么让压测结果不被缓存“骗”?
MySQL 查询缓存(Query Cache)在 8.0 已移除,但 5.7 及以前默认开启,会导致第二次执行相同 SELECT 几乎零耗时,数据完全失真。必须关掉:SET GLOBAL query_cache_size = 0;(需 SUPER 权限)或启动时加 --query-cache-size=0。
还有两个隐形缓存常被忽略:
- InnoDB 缓冲池(
innodb_buffer_pool):首次查冷数据要从磁盘读,第二次查热数据走内存。压测前运行一次全表扫描(SELECT COUNT(*) FROM table_name)把数据预热进缓冲池,再开始计时 - PHP OPcache:如果压测脚本本身被 OPcache 缓存,
include开销极小,但实际线上可能因配置不同失效。压测时加opcache.enable=0到 php.ini,或用php -d opcache.enable=0 bench.php
最危险的是没清空缓冲池就反复跑不同 SQL —— 前一个测试把索引页加载进内存,后一个测试直接受益,你以为是 SQL 写得好,其实是缓存余温。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











