PDO连金仓数据库,我把踩过的坑整理了一下(上篇)
先说说为什么写这篇
去年做一个内部管理系统,后端用PHP,数据库要换成金仓。我当时心想:PHP用PDO连数据库不是很标准的事吗?换驱动就行了呗。
结果一搞,发现事情没那么简单。
金仓的PDO驱动需要自己编译,还要配置php.ini,跟MySQL那种装个扩展就完事的体验完全不一样。折腾了两天才跑通。今天把过程记下来,希望能帮到后面的人。
一、关于PDO和金仓驱动
1.1 PDO是什么
PDO(PHP Data Objects)是PHP官方提供的数据库抽象层。它定义了一套统一的接口,不管你连MySQL还是Oracle还是别的数据库,代码写法是一样的。
// 连MySQL$pdo=newPDO('mysql:host=localhost;dbname=test',$user,$pass);// 连金仓,写法几乎一样$pdo=newPDO('kdb:host=localhost;dbname=test;port=54321',$user,$pass);这就是PDO的好处:换数据库,大部分代码不用改。
1.2 金仓的PDO驱动
PDO本身只是个接口,具体干活要靠数据库驱动。金仓的驱动叫pdo_kdb,Linux下是pdo_kdb.so文件,Windows下是php_pdo_kdb.dll。
这个驱动不是PHP自带的,需要单独下载安装。金仓官网提供了各个PHP版本的驱动包,得选对版本。
二、安装配置
2.1 查看PHP版本
拿到驱动包之前,先搞清楚自己的PHP版本。
php-v# 输出类似:PHP 7.4.33 (cli)再看是线程安全还是非线程安全(NTS):
php-i|grep"Thread Safety"# 或者php-m驱动包默认提供的是NTS版本的。如果是TS版本,需要找客服要。
2.2 下载对应驱动
金仓官网提供这些PHP版本的驱动:
- PHP 5.6
- PHP 7.0 到 7.4
- PHP 8.0 到 8.4
下载的时候一定要对上号。我用的是PHP 7.4,就下了7.4的驱动包。
2.3 放置驱动文件
下载解压后,会得到一个pdo_kdb.so文件(Windows下是php_pdo_kdb.dll)。
把它放到PHP扩展目录下。扩展目录在哪?
php-i|grepextension_dir我机器上输出的是/usr/lib/php/extensions/no-debug-non-zts-20190902。把文件放进去就行。
2.4 配置php.ini
找到php.ini文件的位置:
php-i|grep"Loaded Configuration File"打开php.ini,找到extension_dir这一行,确认指向的是扩展目录。
然后添加一行:
extension=pdo_kdb.so如果之前有其他PDO驱动(比如pdo_mysql),保留也没关系,可以共存。
2.5 验证是否加载成功
php-m|greppdo_kdb看到pdo_kdb,说明加载成功了。
如果看不到,检查:
- 驱动文件在不在扩展目录里
- php.ini里有没有写对
- 有没有重启PHP-FPM(
systemctl restart php-fpm)
三、连接数据库
3.1 DSN格式
PDO连金仓的DSN(数据源名称)格式是:
$dsn='kdb:host=localhost;dbname=TEST;port=54321';参数说明:
| 参数 | 说明 | 默认值 |
|---|---|---|
| host | 数据库IP | localhost |
| port | 端口 | 54321 |
| dbname | 数据库名 | 无 |
| user | 用户名 | 无 |
| password | 密码 | 无 |
注意前面的kdb:,不是pgsql:也不是mysql:。
3.2 完整连接代码
<?php$dsn='kdb:host=127.0.0.1;dbname=TEST;port=54321';$user='SYSTEM';$password='123456';try{$pdo=newPDO($dsn,$user,$password,[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC]);echo"连接成功\n";}catch(PDOException$e){echo"连接失败: ".$e->getMessage()."\n";}?>ERRMODE_EXCEPTION这个模式推荐打开。默认PDO出错只是返回false,很难排查问题。开了异常模式后,哪行代码出问题一目了然。
3.3 连接不上怎么办
如果连不上,按顺序检查:
ping一下数据库IP,看网络通不通- 用
ksql命令行连一下,看账号密码对不对 - 确认端口是不是54321(金仓默认端口,不是5432)
- 检查数据库名大小写
我遇到最多的就是端口写错,习惯了MySQL的3306,写成5432了。
四、基本操作
4.1 查询数据
$stmt=$pdo->query('SELECT id, name, age FROM users WHERE age > 18');while($row=$stmt->fetch()){echo"姓名: ".$row['name'].", 年龄: ".$row['age']."\n";}fetch()一次拿一行,适合结果集大的时候。数据量小的话,也可以用fetchAll()一次性全拿。
$rows=$stmt->fetchAll();foreach($rowsas$row){echo$row['name']."\n";}4.2 带参数查询
永远不要直接拼接SQL。下面这种写法是绝对禁止的:
// 千万别这么写$name=$_GET['name'];$pdo->query("SELECT * FROM users WHERE name = '$name'");正确的做法是用预处理语句:
$stmt=$pdo->prepare('SELECT * FROM users WHERE name = ?');$stmt->execute([$_GET['name']]);$user=$stmt->fetch();或者用命名占位符:
$stmt=$pdo->prepare('SELECT * FROM users WHERE name = :name AND age > :age');$stmt->execute([':name'=>'张三',':age'=>18]);$users=$stmt->fetchAll();预处理语句不光能防SQL注入,重复执行的时候性能也好。
4.3 插入数据
$stmt=$pdo->prepare('INSERT INTO users (name, email) VALUES (?, ?)');$stmt->execute(['李四','lisi@test.com']);// 获取刚插入的ID(如果表有自增列)$id=$pdo->lastInsertId();4.4 更新和删除
// 更新$stmt=$pdo->prepare('UPDATE users SET email = ? WHERE id = ?');$stmt->execute(['newemail@test.com',1]);// 删除$stmt=$pdo->prepare('DELETE FROM users WHERE id = ?');$stmt->execute([1]);// 影响行数echo$stmt->rowCount();五、事务处理
事务可以保证一组操作要么全部成功,要么全部失败。比如转账:扣A账户和加B账户必须同时成功或同时失败。
try{$pdo->beginTransaction();// 从A账户扣100$stmt=$pdo->prepare('UPDATE accounts SET balance = balance - 100 WHERE id = ?');$stmt->execute([1]);// 往B账户加100$stmt=$pdo->prepare('UPDATE accounts SET balance = balance + 100 WHERE id = ?');$stmt->execute([2]);$pdo->commit();echo"转账成功\n";}catch(Exception$e){$pdo->rollBack();echo"转账失败: ".$e->getMessage()."\n";}事务有几个要注意的地方:
- 表必须是InnoDB类型(金仓默认支持事务)
- 事务里不要做耗时操作,否则会长时间锁表
- 事务里不要混合MyISAM表(不支持事务)
六、一个完整示例
<?php$dsn='kdb:host=127.0.0.1;dbname=TEST;port=54321';$user='SYSTEM';$password='123456';try{$pdo=newPDO($dsn,$user,$password,[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);// 建表$pdo->exec("CREATE TABLE IF NOT EXISTS php_test ( id SERIAL PRIMARY KEY, name VARCHAR(100), email VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )");// 插入$stmt=$pdo->prepare("INSERT INTO php_test (name, email) VALUES (?, ?)");$stmt->execute(['张三','zhangsan@test.com']);echo"插入成功,ID: ".$pdo->lastInsertId()."\n";// 查询$stmt=$pdo->query("SELECT * FROM php_test");while($row=$stmt->fetch()){echo"ID:{$row['id']}, 姓名:{$row['name']}, 邮箱:{$row['email']}\n";}// 更新$stmt=$pdo->prepare("UPDATE php_test SET email = ? WHERE name = ?");$stmt->execute(['newemail@test.com','张三']);echo"更新了 ".$stmt->rowCount()." 行\n";// 删除$stmt=$pdo->prepare("DELETE FROM php_test WHERE name = ?");$stmt->execute(['张三']);echo"删除了 ".$stmt->rowCount()." 行\n";}catch(PDOException$e){echo"错误: ".$e->getMessage()."\n";}?>七、小结
上篇主要讲了:
- PDO是什么,金仓的PDO驱动叫
pdo_kdb - 怎么装驱动、配php.ini、验证是否成功
- 怎么连接数据库、写查询、插入、更新、删除
- 事务怎么用
下篇会讲一些生产环境更需要的东西:预处理语句详解、大对象操作、COPY命令批量导入导出数据。欢迎继续看。