在使用 PHP 开发涉及 MySQL 的应用时,有些开发者会通过 mysql_info() 或 mysqli_info() 来获取执行某些 SQL 操作后的补充信息,比如批量插入、更新或删除语句影响的行数等。然而,有时你可能会发现返回的数据并不准确,甚至在某些操作之后直接返回 false 或空字符串。这种“不靠谱”的行为是怎么回事?本文将从其工作原理出发,分析原因,并提供应对策略。
mysql_info() 主要用于在执行某些非 SELECT 语句后获取执行状态的简要说明,比如:
$link = mysqli_connect("localhost", "user", "pass", "testdb");
mysqli_query($link, "UPDATE users SET status = 1 WHERE id < 100");
echo mysqli_info($link);
返回内容可能是:
Rows matched: 100 Changed: 100 Warnings: 0
这个输出信息在调试批量操作时非常有用,比如判断多少行被匹配,多少行被修改。但这依赖于底层 MySQL 返回的状态信息。
不是所有的非 SELECT 操作都会生成 info 信息,MySQL 官方文档也明确说明只有以下语句类型可能提供这些信息:
INSERT INTO ... SELECT ...
UPDATE
DELETE
LOAD DATA
像普通的 INSERT、REPLACE 等语句,尤其是没有通过子查询插入数据的场景,往往不会填充 info 缓存。
PHP 使用的 MySQL 客户端库(libmysql 或 mysqlnd)也会影响 info 的获取。使用 mysqlnd(MySQL native driver)时,mysqli_info() 返回的内容更加可靠。而如果是 libmysql,部分信息可能不会被记录或延迟返回。
如果在一个连接中执行了多个 SQL 语句(例如批处理),mysqli_info() 只返回最后一条语句的 info 信息。也就是说:
mysqli_query($link, "UPDATE table1 SET name = 'A'; UPDATE table2 SET name = 'B';");
echo mysqli_info($link); // 只会返回 table2 的执行信息
这让很多开发者误以为之前的操作没有执行或没有效果。
不同的 MySQL 服务器版本在返回 info 内容方面有差异。例如,旧版本可能不会统计 Rows matched 或 Warnings,或者存在 bug 返回错误的行数信息。此外,如果启用了某些性能优化选项,可能跳过了一些统计逻辑。
确保 PHP 环境中启用了 mysqlnd 驱动,可以通过如下方式检查:
if (function_exists('mysqli_get_client_stats')) {
echo "mysqlnd 已启用";
}
mysqlnd 提供了更全面和标准化的 info 支持,比 libmysql 更推荐用于生产环境。
不要在多语句(multi_query)中依赖 info 的返回,应该将每个语句单独执行,并立即读取对应的 info 信息,以防止被后续语句覆盖。
mysqli_query($link, "DELETE FROM logs WHERE created_at < NOW() - INTERVAL 30 DAY");
$logInfo = mysqli_info($link);
echo "日志清理结果:" . $logInfo;
对于一些重要的 SQL 操作,建议不要完全依赖 mysql_info() 返回的文本描述,而是根据业务逻辑做一次额外的查询或日志记录来验证。例如清理前后行数差值,或通过事务控制确保精确执行。
注意 Changed: 0 并不意味着 SQL 没有成功执行,可能只是数据本身未发生变化(例如更新成了相同的值)。这种场景在 UPDATE 中很常见,也解释了为什么你看到有 Rows matched 却 Changed: 0 的情况。
为了更有效地调试和记录,可以使用如下方式将 info 信息写入日志或展示到后台界面:
$sql = "UPDATE users SET status = 0 WHERE active = 0";
mysqli_query($link, $sql);
file_put_contents("/var/log/mysql_ops.log", date('Y-m-d H:i:s') . " " . $sql . " => " . mysqli_info($link) . "\n", FILE_APPEND);
也可以将这些操作记录通过接口发送到远程监控系统:
$url = "https://m66.net/log_sql_info";
$data = ['sql' => $sql, 'info' => mysqli_info($link)];
// 使用 curl 或 Guzzle 发送 POST 请求
mysql_info() 或 mysqli_info() 是一个有用但有限的工具,它提供的是对 SQL 操作的补充性信息,不应成为判断逻辑成功与否的唯一依据。在不同驱动、MySQL 版本和语句类型的条件下,它可能表现不一。开发者应深入理解其局限,结合日志、事务、返回码等方式进行全面监控和容错设计,才能构建健壮的数据库操作流程。