文章851
标签121
分类10

PHP 报错 “Error while sending STMT_PREPARE packet” 故障分析与排查经验

🧩 问题背景

在某日凌晨,线上服务的 PHP 日志中频繁出现如下报错:


\[01-May-2024 06:07:27 Asia/Shanghai] PHP Warning:  Error while sending STMT\_PREPARE packet. PID=26439

该错误由 mysqli->prepare() 方法抛出,影响到了部分数据库相关的服务逻辑。


📌 初步分析方向

根据历史经验和网上资料,造成该错误的可能性较多,主要包括:

  • MySQL 连接超时
  • 数据库连接数过多
  • max_allowed_packet 配置过小
  • wait_timeout 超时断开连接
  • 网络波动
  • 数据库崩溃或 mysqld 崩溃重启
  • PHP 连接实例未关闭或未重连

🧪 运维排查流程(DBA 配合)

运维同学按以下顺序排查和调整配置:

✅ 检查并调整 MySQL 参数:

  • max_allowed_packet
    增大至 64M,防止 SQL 包体积过大造成 STMT_PREPARE 失败。
  • wait_timeoutinteractive_timeout
    从默认 28800 秒(8 小时)调低为 3600 秒,确认是否为连接空闲时间过长导致的问题。
  • max_connections
    增加连接数上限,确保不是连接数打满导致新连接异常。
  • 查看慢查询日志、error log
    无明显异常,MySQL 实例运行正常。

✅ 检查服务器层面:

  • 网络无抖动,连接稳定。
  • MySQL 实例未出现重启或崩溃。
  • PHP-FPM 无大量进程挂起或爆炸增长。

🔍 最终问题定位

随着逐步排除数据库和系统参数问题,最终将问题定位到PHP 脚本中的数据库连接行为

问题根因:

  • 脚本中的某段逻辑在 未命中某个条件之前,不会执行任何数据库操作
  • 由于该脚本常驻运行,MySQL 连接在启动后可能长时间未使用,直到触发业务逻辑再调用 mysqli->prepare()
  • 此时 MySQL 已因超时断开连接(默认 8 小时),而 PHP 端仍持有旧连接实例未重连,导致 STMT_PREPARE packet 发送失败。

✅ 最终解决方案

  1. 在数据库操作前判断连接是否有效,若已断开则手动重连。
  2. 使用短连接(每次操作都重新连接)使用连接池方案 管理连接。
  3. 定期在脚本中执行轻量级 ping 检查保持连接活性:
   if (!$mysqli->ping()) {
       $mysqli->close();
       $mysqli = new mysqli(...);
   }
  1. 优化业务逻辑,避免脚本长时间空闲又不释放连接。

📖 经验总结

  • 报错信息模糊时,采用排除法逐层缩小定位范围是非常有效的手段。
  • 长连接在 PHP 脚本中使用需慎重,特别是常驻脚本,需考虑连接保活或断线重连机制。
  • 建议对所有 mysqliPDO 类连接封装健康检查方法,提升系统健壮性。
  • 不要轻信“是数据库的问题”,PHP 应用层连接管理同样关键。

📌 参考

🚧 避免 VARCHAR 排序陷阱:当数字变成字符串

在日常开发中,我们经常会碰到“看起来像数字”的字段存储在 VARCHAR 类型的数据库列中。乍一看没问题,结果排序一查,全乱了。

❓ 问题背景

假设我们有一个数据库字段用来记录某种“订单号”或“流水号”,形式都是数字,例如:

992687129
1001404894

字段类型是 VARCHAR(50)(出于兼容性或历史原因)。我们希望按照这个字段升序排序,拿到最小或最大的值。

但执行如下查询后却发现结果不符合预期:

SELECT some_id_column
FROM some_table
WHERE some_filter_column = 'xxx'
ORDER BY order_id_column ASC;

结果排序却是这样:

992687129
1001404894

1001404894992687129 大,但却排在后面。


🔍 问题分析

根本原因是:字符串排序 ≠ 数字排序

MySQL 中 VARCHAR 字段默认按照**字典序(字符串顺序)**排序:

  • "100""2" 前面,因为 "1""2" 小。
  • 同理 "1001404894" 会被认为小于 "992687129"

✅ 解决方法一:CAST 成数字排序

如果字段中确实只保存了数字字符(没有字母或特殊符号),可以直接在查询中使用 CASTCONVERT 强制转成数字排序:

SELECT some_id_column
FROM some_table
WHERE some_filter_column = 'xxx'
ORDER BY CAST(order_id_column AS UNSIGNED) ASC;

这样排序就变成:

992687129
1001404894

完全符合数字的实际大小。


✅ 解决方法二:修改字段类型(更彻底)

如果你确定字段本质是数字,建议在数据库中直接将其从 VARCHAR 类型修改为 BIGINTUNSIGNED BIGINT

ALTER TABLE some_table
MODIFY COLUMN order_id_column BIGINT UNSIGNED;

这不仅解决了排序问题,还能提升性能和减少存储空间。


✅ 解决方法三:创建虚拟列(兼容性强)

在某些情况下你可能不方便直接修改原字段类型(比如已上线、历史数据复杂),这时可以新增一个虚拟列用于排序:

ALTER TABLE some_table
ADD COLUMN order_id_num BIGINT UNSIGNED
GENERATED ALWAYS AS (CAST(order_id_column AS UNSIGNED)) STORED;

CREATE INDEX idx_order_id_num ON some_table(order_id_num);

这样既保留了原始字段,又可以享受数字排序的便利,并通过索引提升性能。


🔚 总结

方法可行性特点
CAST 查询中转换✅ 简单适合临时处理
修改字段类型✅ 推荐长期方案,性能最佳
虚拟列 + 索引✅ 稳妥兼顾稳定性与效率,适合迁移期

💡 最佳实践建议

  • 避免使用 VARCHAR 存储数字,除非确实有业务理由(如支持前导零、混合字母等)。
  • 数据建模时,字段类型要与数据含义保持一致。
  • 字段类型一旦设计错了,问题往往会在排序、比较、聚合时爆发。

如果你在生产环境中也遇到了类似的问题,不妨检查一下你的数据结构,或许一次小小的字段优化,就能避免不少“意想不到的结果”。

Shell 脚本中实现随机等待 1 到 10 分钟后执行 代碼

Shell 脚本中,并且实现随机等待 1 到 10 分钟后执行,你可以按照以下步骤创建一个新的 Shell 脚本。

Shell 脚本示例

#!/bin/bash

# 生成 1 到 10 之间的随机数(单位:分钟)
random_time=$(shuf -i 1-10 -n 1)

# 输出等待时间
echo "等待时间:$random_time 分钟"

# 转换分钟为秒
sleep_time=$((random_time * 60))

# 等待指定时间
sleep $sleep_time

# 执行 PHP 脚本命令
echo "执行 PHP 脚本:php xxx.php"
php xxx.php

# 输出执行完成信息
echo "PHP 脚本已执行完成"

解释:

  1. 生成随机时间:使用 shuf -i 1-10 -n 1 随机生成 1 到 10 之间的整数(代表分钟)。
  2. 等待时间:将随机的分钟数转换为秒,并通过 sleep 命令暂停脚本执行。
  3. 执行 PHP 脚本:在等待时间过去后,执行你指定的 PHP 脚本命令 php xxx.php

如何使用:

  1. 保存脚本:将上面的脚本内容保存为一个 .sh 文件,例如 run_script.sh
  2. 赋予执行权限

    chmod +x run_script.sh
  3. 执行脚本

    ./run_script.sh

示例输出:

每次你运行这个脚本时,它将先随机等待 1 到 10 分钟,然后执行你提供的 PHP 脚本命令。

等待时间:3 分钟
执行 PHP 脚本:php xxx.php
PHP 脚本已执行完成

CentOS 上使用 `yum` 安装 Node.js

在 CentOS 上使用 yum 安装 Node.js 非常简单。可以按照以下步骤进行:

1. 添加 NodeSource 仓库

NodeSource 提供了 Node.js 的官方仓库。首先,添加你需要的 Node.js 版本仓库,例如 Node.js 16.x:

curl -sL https://rpm.nodesource.com/setup_16.x | sudo bash -

2025-05-14T01:12:03.png

2. 安装 Node.js

接下来,使用 yum 安装 Node.js:

sudo yum install -y nodejs

2025-05-14T01:13:09.png

3. 验证安装

安装完成后,检查 Node.js 和 npm 版本以确认安装成功:

# node -v
v16.20.2

# npm -v
8.19.4

4. 更新 npm(可选)

如果需要更新 npm 到最新版本,可以使用以下命令:

sudo npm install -g npm@latest
">