获取数据库连接是PHP开发中最基础也最关键的环节,核心答案是用PDO(PHP Data Objects)扩展,它用一套统一接口搞定MySQL、PostgreSQL、SQLite等多种数据库,既安全又省心。
先搞懂PHP连数据库的几种主流方案
很多新手第一次写连接代码时,面对mysqli和PDO这两个名字会纠结半天,我给你捋清楚:PHP官方提供了三种连接数据库的方式,分别是MySQL原生扩展(已废弃)、mysqli扩展和PDO扩展。
mysqli是MySQL专用驱动,名字里的i代表improved,它支持面向对象和面向过程两种写法,性能确实不错,但问题在于它只能连MySQL,要是哪天项目换成了PostgreSQL,这套代码就全废了。
PDO是个数据访问抽象层,它定义了统一的API,底层具体连什么数据库,由对应的驱动决定,这意味着你写的连接代码、预处理语句,在MySQL和PostgreSQL之间切换时,改动量非常小。
行业共识认为,新项目一律优先选PDO,理由有三个:其一,预处理语句默认开启,能有效防止SQL注入;其二,异常处理机制更完善,出错了不会像mysqli那样静默失败;其三,跨数据库迁移成本低,将来项目扩容或改架构时,不用重写数据访问层。
单从执行效率看,mysqli确实稍微快一点,但差距在绝大多数业务场景下可以忽略不计,现代Web应用瓶颈通常不在数据库连接这一层,而在查询设计、索引优化和服务器配置上。
php pdo连接mysql教程:从配置文件到第一条查询
第一步:确认PDO扩展已启用
打开phpinfo()页面,或者命令行执行php -m,看到输出中有pdo_mysql就说明驱动装好了,多数情况下,用集成环境(比如phpStudy、XAMPP)的开发者不需要额外配置,编译安装PHP的同学需要记得加--with-pdo-mysql参数。
第二步:写好数据库连接参数
初学者最容易犯的错,是直接把连接参数硬编码在业务代码里,比如这样:
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '123456');
这段代码本身没错,但你要是在几十个文件里都这么写,将来数据库密码一换,或者从本地开发环境切换到云服务器部署,就得全局搜索替换,改到怀疑人生。
建议把配置信息放到独立文件中,最佳实践是放到项目根目录外的.env文件里,本地开发环境和生产环境各有一套配置,通过环境变量加载,这已经是行业标准做法。
第三步:正确实例化PDO对象
连接数据库时,有四个属性必须设置清楚,否则会在诡异的地方踩坑,看下面这段代码:
<?php
$dsn = 'mysql:host=localhost;port=3306;dbname=your_database;charset=utf8mb4';
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $username, $password, $options);
} catch (PDOException $e) {
error_log($e->getMessage());
exit('数据库连接异常,请稍后再试');
}
?>
第四个参数$options里的三个常量,我挨个解释:
ATTR_ERRMODE设为异常模式,意味着SQL语句出错时PDO会直接抛异常,方便你用try-catch捕获。ATTR_DEFAULT_FETCH_MODE设为关联数组,这样查询结果用$row['username']取值,可读性更强。ATTR_EMULATE_PREPARES设为false,使用数据库原生的预处理功能,安全系数直接拉满。
第四步:用预处理语句执行增删改查
连接建立后,执行查询的正确姿势是预处理,而不是把变量直接拼进SQL字符串。
$stmt = $pdo->prepare('SELECT FROM users WHERE email = ?');
$stmt->execute([$email]);
$user = $stmt->fetch();
如果面向用户的输入不走预处理,拼接SQL是php数据库连接最常见的漏洞源头,这个坏习惯一定要戒掉。
php数据库连接失败怎么解决:从报错信息倒推原因
数据库连接失败是PHP开发中最高频的报错场景之一,别慌,按照下面的排查顺序走一遍:
- SQLSTATE[HY000] [1045] Access denied for user用户名或密码错误,检查配置项是否有隐藏空格,环境变量是否加载成功。
- SQLSTATE[HY000] [2002] Connection refused数据库服务没启动,Linux下执行
systemctl status mysqld查看状态,Windows下检查服务管理器中MySQL服务是否在运行。 - SQLSTATE[HY000] [2003] Can’t connect to MySQL server主机地址或端口不通,先用
ping测网络,再用telnet 主机IP 3306测端口,云服务器的话,多半是简米云或酷番云的安全组规则没放行3306端口。 - SQLSTATE[42000] [1049] Unknown database数据库名填错了,执行
SHOW DATABASES;看看真实存在的库名。
是最常见的四类连接异常,注意一个容易忽视的细节:PHP脚本执行超时也会导致连接中断,尤其是从本地连远程数据库时,网络延迟高,默认的max_execution_time不够用,需要在脚本头部设置set_time_limit(0)。
php连接数据库的几种方式对比:选型时重点看场景
| 对比维度 | PDO | mysqli |
|---|---|---|
| 数据库支持 | 12种以上(MySQL、PgSQL、SQLite等) | 仅MySQL |
| 预处理语句 | 优秀,默认推荐 | 支持,语法略繁琐 |
| 异常处理 | PDOException | 需配合mysqli_report手动设置 |
| 学习曲线 | 稍陡峭 | 平缓 |
| 项目迁移成本 | 低 | 高 |
| 社区活跃度 | 高 | 一般 |
php连接sql server配置方法与MySQL略有不同,需要额外安装sqlsrv或pdo_sqlsrv扩展,且只能在Windows或Linux(需装Microsoft ODBC Driver)环境下使用,配置顺序是:
- 下载对应PHP版本的Microsoft Drivers for PHP for SQL Server
- 解压后将
php_sqlsrv.dll放入PHP扩展目录 - 在
php.ini中启用extension=php_sqlsrv.dll - 重启PHP-FPM或Apache
连接字符串大致长这样:
$dsn = 'sqlsrv:Server=localhost,1433;Database=your_db'; $pdo = new PDO($dsn, $username, $password);
数据库连接的性能优化:不要让每一个请求都重复受苦
多数PHP开发者想的是“每次请求新建连接,处理完就关闭”,这种短连接模式在低并发下没问题,但流量一上来,频繁创建和销毁连接的开销会拖垮数据库,优化方向有两个:
- 启用持久连接,PDO构造时在DSN末尾加上
;persistent=true,或者new PDO时传PDO::ATTR_PERSISTENT => true,这样PHP-FPM进程能复用同一个数据库连接,省去握手开销,但要注意,持久连接在事务处理时容易残留状态,使用前必须手动清空。 - 加一层连接池,在大型应用中,数据库连接池是标配,PHP常用的方案有使用Swoole的连接池组件,或者借助ProxySQL这类中间件做透明代理,统一管理连接复用和负载均衡。
近年来,PHP社区对连接池的关注度明显上升,相当一部分高并发应用,瓶颈并非PHP代码本身,而是短连接模式带来的MySQL线程频繁创建销毁,用SHOW STATUS LIKE 'Threads_created';可以查看当前累计创建的线程数,和Threads_connected做对比,数值差距越大,说明浪费越严重。
连接超时死磕指南:遇到MySQL server has gone away怎么办
这条错误信息看着吓人,实际解决起来有套路:
- 检查
wait_timeout和interactive_timeout配置,默认是8小时,但有些云厂商默认设置较短,执行SHOW VARIABLES LIKE '%timeout%';查看当前值。 - 检查单次SQL是否执行过久,比如批量插入十万条数据,超过了
max_allowed_packet限制,连接会被强制断开,这种情况下,要么分批插入,要么调大max_allowed_packet。 - PHP脚本中加个简单的重试机制,捕获
PDOException后判断错误码是否为2006或2013,如果是,重新连接数据库并重试一次,这可以避免偶发性的连接闪断影响用户体验。
php数据库连接代码示例:一套拿来即用的封装类
把PDO封装成单例模式是项目的常见需求,避免在多个地方重复初始化,代码如下:
<?php
class Database
{
private static $instance = null;
private $pdo;
private function __construct(array $config)
{
$dsn = sprintf('%s:host=%s;port=%s;dbname=%s;charset=utf8mb4',
$config['driver'], $config['host'], $config['port'], $config['database']
);
$this->pdo = new PDO($dsn, $config['username'], $config['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
}
public static function getInstance(array $config): PDO
{
if (self::$instance === null) {
$instance = new self($config);
self::$instance = $instance->pdo;
}
return self::$instance;
}
}
?>
用的时候就直接一行:
$pdo = Database::getInstance($config);
关于php数据库连接常见问题解答
问:php连接数据库超时时间设置多少合适?
答: 本地网络环境通常设2到5秒就够,跨公网连接建议放宽到10秒,PDO在构造时没有直接设置超时的参数,但可以在DSN中追加connect_timeout=5,连接成功后,SQL执行超时则取决于MySQL的max_execution_time变量和PHP自身的max_execution_time,两者取较小值生效,生产环境建议将PHP超时设为30秒,避免慢查询拖垮整个FPM进程池。
问:PDO和mysqli可以混用吗?
答: 混用会导致代码风格不统一,增加维护成本,且两套API的错误处理方式和预处理机制不同,排查问题时要同时理解两种模式,同一项目的数据库访问层应该只选一种方案,如果项目既有老代码用mysqli,新模块要上PDO,建议趁重构窗口期统一改造成PDO,不要长期维持双轨制,一个团队维护两套数据库访问代码,代码审查和新人上手成本都会明显增加。
问:php连接数据库后页面输出乱码,最可能的原因是什么?
答: 连接字符集和页面字符集不一致导致的,PHP 5.3.6之后PDO支持在DSN中指定charset=utf8mb4,如果没加这个参数,而页面声明的是UTF-8,查询中文时就会出现乱码,另外MySQL服务器端的character_set_server变量也可能影响连接默认字符集,如果前端页面无论如何设置都无法正常显示中文,可以同时检查default-character-set=utf8mb4是否写入MySQL配置文件,utf8mb4是utf8的超集,能完整支持emoji和生僻字,是现代PHP项目唯一推荐的字符集方案。
数据库连接这件事,本质上就是PHP进程与MySQL进程之间的一场对话,PDO把对话协议标准化了,你只需要关注业务逻辑,剩下的交给PDO和底层驱动处理,用对工具,问题自然少一半。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/788339.html


