PDO连金仓数据库,我把踩过的坑整理了一下(上篇)

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 连接不上怎么办

如果连不上,按顺序检查:

  1. ping一下数据库IP,看网络通不通
  2. ksql命令行连一下,看账号密码对不对
  3. 确认端口是不是54321(金仓默认端口,不是5432)
  4. 检查数据库名大小写

我遇到最多的就是端口写错,习惯了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";
}
?>

七、小结

上篇主要讲了:

  1. PDO是什么,金仓的PDO驱动叫pdo_kdb
  2. 怎么装驱动、配php.ini、验证是否成功
  3. 怎么连接数据库、写查询、插入、更新、删除
  4. 事务怎么用

下篇会讲一些生产环境更需要的东西:预处理语句详解、大对象操作、COPY命令批量导入导出数据。欢迎继续看。

相关推荐
用户34232323763172 小时前
大规模采集架构——从单台网关到千点集群
后端
我登哥MVP2 小时前
SpringCloud 核心组件解析:服务链路追踪
java·spring boot·后端·spring·spring cloud·java-ee·maven
晓杰在写后端2 小时前
从0到1实现Balatro游戏后端(7):Boss Blind与特殊规则实现
后端·游戏开发
用户298698530142 小时前
Java 处理 Word 文档:如何批量修改超链接地址与显示文本
java·后端
爱勇宝2 小时前
《置身钉内》之后:普通前端的出路在哪里?
前端·后端·程序员
Tenaryo3 小时前
从 178ms 到 1ms:当 Store-to-Load Forwarding 卡住你的 for 循环
后端·面试
卷无止境3 小时前
PM4Py 入门教程:用 Python 做流程挖掘
后端
Asize3 小时前
重生之我在 Vibe Coding 时代当程序员:第十五课,正则表达式和 HTTP 请求:规则不是背出来的,是拆出来的
前端·javascript·后端
惜缘破军3 小时前
基于 Spring Boot 3 和 Spring Cloud 2023 的微服务基础框架 hdfk7-boot
spring boot·后端·微服务