1. PHP与MySQL基础交互原理
PHP与MySQL的交互本质上是通过客户端-服务器模型实现的。当PHP脚本调用MySQL函数时,实际上是在通过MySQL客户端库与远程或本地的MySQL服务器建立连接通道。这个过程中有几个关键组件在协同工作:
- MySQL客户端库:PHP通过mysql/mysqli/pdo等扩展内置的客户端库
- TCP/IP连接:默认使用3306端口建立网络连接
- 查询协议:MySQL自定义的通信协议用于传输SQL语句和结果集
典型的交互流程如下:
- 建立连接(mysql_connect)
- 选择数据库(mysql_select_db)
- 发送查询(mysql_query)
- 处理结果(mysql_fetch_array等)
- 释放资源(mysql_free_result)
- 关闭连接(mysql_close)
重要提示:虽然这些函数现在仍能使用,但官方已标记为废弃(deprecated),建议使用mysqli或PDO扩展替代
2. 核心函数详解与安全实践
2.1 连接管理函数组
// 基础连接示例 $link = mysql_connect('localhost', 'mysql_user', 'mysql_password'); if (!$link) { die('Could not connect: ' . mysql_error()); } echo 'Connected successfully'; mysql_close($link);连接参数说明:
- 主机名:可以是IP、域名或localhost
- 用户名:MySQL用户账号
- 密码:对应账号的密码
- new_link:是否强制新建连接(默认为false)
- client_flags:连接选项标志位
安全建议:
- 永远不要将连接信息硬编码在脚本中
- 使用配置文件并设置适当权限(如400)
- 考虑使用持久连接mysql_pconnect()时要注意连接数限制
2.2 查询执行与结果处理
查询执行的基本模式:
$result = mysql_query("SELECT * FROM users WHERE id = 1"); if (!$result) { die('Invalid query: ' . mysql_error()); } while ($row = mysql_fetch_assoc($result)) { echo $row['username']; } mysql_free_result($result);结果获取函数对比:
| 函数 | 返回类型 | 特点 |
|---|---|---|
| mysql_fetch_row | 枚举数组 | 数字索引,访问快 |
| mysql_fetch_assoc | 关联数组 | 字段名作为键 |
| mysql_fetch_array | 混合数组 | 可同时用数字和字段名 |
| mysql_fetch_object | 对象 | 面向对象风格 |
2.3 关键辅助函数
mysql_real_escape_string()的安全使用:
$user_input = $_POST['username']; $safe_input = mysql_real_escape_string($user_input); $query = "SELECT * FROM users WHERE username = '$safe_input'";注意:必须先建立连接才能正确转义,因为要考虑当前连接的字符集
事务处理示例:
mysql_query("START TRANSACTION"); $q1 = mysql_query("UPDATE accounts SET balance = balance - 100 WHERE user = 1"); $q2 = mysql_query("UPDATE accounts SET balance = balance + 100 WHERE user = 2"); if ($q1 && $q2) { mysql_query("COMMIT"); } else { mysql_query("ROLLBACK"); }3. 现代替代方案与迁移指南
3.1 mysqli扩展的优势
- 面向对象和面向过程两种接口
- 支持预处理语句(防SQL注入)
- 支持多语句和事务
- 性能优化
迁移示例:
// 旧版 $link = mysql_connect("localhost", "user", "pass"); mysql_select_db("database", $link); // 新版mysqli $mysqli = new mysqli("localhost", "user", "pass", "database");3.2 PDO的跨数据库支持
PDO提供统一的API支持多种数据库:
try { $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'); $stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id"); $stmt->execute([':id' => $_GET['id']]); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { echo "Error: " . $e->getMessage(); }3.3 函数对照表
| 旧函数 | mysqli替代 | PDO替代 |
|---|---|---|
| mysql_connect | mysqli_connect/new mysqli | new PDO |
| mysql_query | mysqli_query | PDO::query |
| mysql_fetch_array | mysqli_fetch_array | PDOStatement::fetch |
| mysql_real_escape_string | mysqli_real_escape_string | PDO::quote |
| mysql_error | mysqli_error | PDO::errorInfo |
4. 性能优化与调试技巧
4.1 查询优化实践
- 使用EXPLAIN分析查询:
$result = mysql_query("EXPLAIN SELECT * FROM large_table WHERE...");- 索引使用原则:
- WHERE子句中的字段
- JOIN条件中的字段
- ORDER BY/GROUP BY字段
- 避免SELECT *,只查询需要的列
4.2 连接池管理
持久连接的正确使用方式:
$link = mysql_pconnect('localhost', 'user', 'pass'); if (!mysql_ping($link)) { $link = mysql_connect('localhost', 'user', 'pass'); }4.3 调试与日志
错误处理最佳实践:
// 开发环境设置 ini_set('display_errors', 1); error_reporting(E_ALL); // 生产环境设置 ini_set('display_errors', 0); ini_set('log_errors', 1); ini_set('error_log', '/path/to/php_errors.log'); // 自定义错误处理 function database_error_handler($errno, $errstr) { error_log("Database error: $errstr"); // 发送警报邮件等 } set_error_handler('database_error_handler');5. 安全防护深度实践
5.1 SQL注入全面防御
危险模式:
// 绝对禁止这样写! $query = "SELECT * FROM users WHERE id = $_GET[id]";多层防御方案:
- 输入验证(白名单原则)
- 参数化查询(mysqli或PDO预处理)
- 最小权限原则(数据库账号权限控制)
- Web应用防火墙(WAF)
5.2 敏感数据处理
密码存储规范:
// 旧式不安全做法(已淘汰) $password = md5($_POST['password']); // 现代安全做法 $hashed_password = password_hash($_POST['password'], PASSWORD_DEFAULT); // 验证 if (password_verify($input_password, $stored_hash)) { // 登录成功 }5.3 连接安全配置
SSL加密连接示例:
$link = mysql_connect('localhost', 'user', 'pass', false, MYSQL_CLIENT_SSL); if (!$link) { die('SSL connection failed: ' . mysql_error()); }安全配置检查清单:
- 禁用MySQL的root远程登录
- 修改默认3306端口
- 定期轮换数据库凭据
- 启用二进制日志审计
6. 实战案例:用户管理系统实现
6.1 数据库设计
CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password CHAR(60) NOT NULL, -- 存储bcrypt哈希 email VARCHAR(100) NOT NULL UNIQUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, is_active BOOLEAN DEFAULT TRUE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;6.2 完整CRUD实现
用户注册示例:
function register_user($username, $password, $email) { $link = mysql_connect(DB_HOST, DB_USER, DB_PASS); mysql_select_db(DB_NAME, $link); $clean_user = mysql_real_escape_string($username); $clean_email = mysql_real_escape_string($email); $hash = password_hash($password, PASSWORD_BCRYPT); $query = "INSERT INTO users (username, password, email) VALUES ('$clean_user', '$hash', '$clean_email')"; $result = mysql_query($query, $link); if (!$result) { error_log("Registration failed: " . mysql_error()); return false; } mysql_close($link); return true; }6.3 分页查询优化
高效分页实现:
function get_users($page = 1, $per_page = 10) { $link = mysql_connect(DB_HOST, DB_USER, DB_PASS); mysql_select_db(DB_NAME, $link); $offset = ($page - 1) * $per_page; $query = "SELECT id, username, email FROM users WHERE is_active = 1 ORDER BY created_at DESC LIMIT $offset, $per_page"; $result = mysql_query($query, $link); $users = array(); while ($row = mysql_fetch_assoc($result)) { $users[] = $row; } // 获取总数用于分页 $count_result = mysql_query("SELECT COUNT(*) FROM users WHERE is_active = 1"); $total = mysql_result($count_result, 0); mysql_close($link); return [ 'users' => $users, 'total' => $total, 'pages' => ceil($total / $per_page) ]; }7. 常见问题排查手册
7.1 连接问题
错误:Can't connect to MySQL server
排查步骤:
- 检查MySQL服务是否运行
- 验证连接参数(主机、端口、用户名、密码)
- 检查防火墙设置
- 查看MySQL错误日志(通常位于/var/log/mysql.log)
7.2 查询错误
错误:You have an error in your SQL syntax
处理方法:
- 打印出完整SQL语句检查
- 使用mysql_real_escape_string处理所有变量
- 验证表名和字段名是否正确
7.3 性能问题
现象:查询缓慢
优化步骤:
- 使用EXPLAIN分析查询计划
- 添加适当的索引
- 考虑查询缓存
- 优化表结构(规范化/反规范化)
7.4 字符编码问题
现象:中文乱码
解决方案:
- 建立连接后立即设置字符集
mysql_set_charset('utf8mb4', $link);- 确保数据库、表和字段都使用utf8mb4
- HTML页面添加meta charset标签
8. 现代化迁移策略
8.1 逐步迁移方案
- 新功能使用mysqli/PDO开发
- 旧功能逐步重写
- 使用适配器模式过渡
示例适配器类:
class LegacyDB { private $link; public function __construct() { $this->link = mysql_connect(DB_HOST, DB_USER, DB_PASS); mysql_select_db(DB_NAME, $this->link); } public function query($sql) { return mysql_query($sql, $this->link); } // 其他方法封装... }8.2 自动化迁移工具
- 使用Rector进行代码自动重构
- 自定义脚本批量替换函数调用
- 静态分析工具检测遗留代码
8.3 测试策略
- 单元测试覆盖所有数据库操作
- 比较测试(新旧实现结果对比)
- 性能基准测试
迁移后的验证清单:
- 所有查询结果一致
- 错误处理正常
- 性能指标达标
- 安全防护到位