PDO连金仓数据库,我把踩过的坑整理了一下(上篇)
先说说为什么写这篇
去年做一个内部管理系统,后端用PHP,数据库要换成金仓。我当时心想:PHP用PDO连数据库不是很标准的事吗?换驱动就行了呗。
结果一搞,发现事情没那么简单。
金仓的PDO驱动需要自己编译,还要配置php.ini,跟MySQL那种装个扩展就完事的体验完全不一样。折腾了两天才跑通。今天把过程记下来,希望能帮到后面的人。
一、关于PDO和金仓驱动
1.1 PDO是什么
PDO(PHP Data Objects)是PHP官方提供的数据库抽象层。它定义了一套统一的接口,不管你连MySQL还是Oracle还是别的数据库,代码写法是一样的。
php
// 连MySQL
$pdo = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
// 连金仓,写法几乎一样
$pdo = new PDO('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版本。
bash
php -v
# 输出类似:PHP 7.4.33 (cli)
再看是线程安全还是非线程安全(NTS):
bash
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扩展目录下。扩展目录在哪?
bash
php -i | grep extension_dir
我机器上输出的是/usr/lib/php/extensions/no-debug-non-zts-20190902。把文件放进去就行。
2.4 配置php.ini
找到php.ini文件的位置:
bash
php -i | grep "Loaded Configuration File"
打开php.ini,找到extension_dir这一行,确认指向的是扩展目录。
然后添加一行:
ini
extension=pdo_kdb.so
如果之前有其他PDO驱动(比如pdo_mysql),保留也没关系,可以共存。
2.5 验证是否加载成功
bash
php -m | grep pdo_kdb
看到pdo_kdb,说明加载成功了。
如果看不到,检查:
- 驱动文件在不在扩展目录里
- php.ini里有没有写对
- 有没有重启PHP-FPM(
systemctl restart php-fpm)
三、连接数据库
3.1 DSN格式
PDO连金仓的DSN(数据源名称)格式是:
php
$dsn = 'kdb:host=localhost;dbname=TEST;port=54321';
参数说明:
| 参数 | 说明 | 默认值 |
|---|---|---|
| host | 数据库IP | localhost |
| port | 端口 | 54321 |
| dbname | 数据库名 | 无 |
| user | 用户名 | 无 |
| password | 密码 | 无 |
注意前面的kdb:,不是pgsql:也不是mysql:。
3.2 完整连接代码
php
<?php
$dsn = 'kdb:host=127.0.0.1;dbname=TEST;port=54321';
$user = 'SYSTEM';
$password = '123456';
try {
$pdo = new PDO($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 查询数据
php
$stmt = $pdo->query('SELECT id, name, age FROM users WHERE age > 18');
while ($row = $stmt->fetch()) {
echo "姓名: " . $row['name'] . ", 年龄: " . $row['age'] . "\n";
}
fetch()一次拿一行,适合结果集大的时候。数据量小的话,也可以用fetchAll()一次性全拿。
php
$rows = $stmt->fetchAll();
foreach ($rows as $row) {
echo $row['name'] . "\n";
}
4.2 带参数查询
永远不要直接拼接SQL。下面这种写法是绝对禁止的:
php
// 千万别这么写
$name = $_GET['name'];
$pdo->query("SELECT * FROM users WHERE name = '$name'");
正确的做法是用预处理语句:
php
$stmt = $pdo->prepare('SELECT * FROM users WHERE name = ?');
$stmt->execute([$_GET['name']]);
$user = $stmt->fetch();
或者用命名占位符:
php
$stmt = $pdo->prepare('SELECT * FROM users WHERE name = :name AND age > :age');
$stmt->execute([':name' => '张三', ':age' => 18]);
$users = $stmt->fetchAll();
预处理语句不光能防SQL注入,重复执行的时候性能也好。
4.3 插入数据
php
$stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
$stmt->execute(['李四', 'lisi@test.com']);
// 获取刚插入的ID(如果表有自增列)
$id = $pdo->lastInsertId();
4.4 更新和删除
php
// 更新
$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账户必须同时成功或同时失败。
php
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
<?php
$dsn = 'kdb:host=127.0.0.1;dbname=TEST;port=54321';
$user = 'SYSTEM';
$password = '123456';
try {
$pdo = new PDO($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命令批量导入导出数据。欢迎继续看。