使用PHP连接MSSQL处理事务确保事务一致性
在PHP开发中,当需要与MSSQL数据库进行交互式数据操作时,事务是保证数据一致性的核心机制。事务能够将多个数据库操作捆绑为一个原子性的执行单元,要么全部操作成功提交,要么全部操作失败回滚,避免出现数据不完整或部分更新导致的业务异常。
一、PHP连接MSSQL的常用方式
目前PHP连接MSSQL主要有两种主流方式,分别是使用PDO扩展和SQLSRV扩展,两种方式都支持事务操作,你可以根据服务器环境选择合适的方案。
1. PDO方式连接MSSQL
PDO是PHP官方提供的数据库抽象层,支持多种数据库,使用PDO连接MSSQL需要服务器安装pdo_dblib或者pdo_sqlsrv扩展(Windows环境常用pdo_sqlsrv,Linux环境常用pdo_dblib)。以下是PDO连接MSSQL的示例代码:
<?php
$host = '127.0.0.1';
$port = 1433;
$dbname = 'test_db';
$username = 'sa';
$password = 'your_password';
try {
// 拼接DSN字符串,不同扩展的DSN格式略有差异,此处为pdo_sqlsrv格式
$dsn = "sqlsrv:Server={$host},{$port};Database={$dbname}";
$pdo = new PDO($dsn, $username, $password);
// 设置错误模式为异常,方便捕获错误
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "MSSQL数据库连接成功";
} catch (PDOException $e) {
die("数据库连接失败:" . $e->getMessage());
}
?>2. SQLSRV方式连接MSSQL
SQLSRV是微软官方提供的PHP扩展,仅支持Windows环境下的MSSQL连接,针对性更强,性能表现也较为稳定。以下是SQLSRV连接MSSQL的示例代码:
<?php
$serverName = "127.0.0.1,1433";
$connectionInfo = array(
"Database" => "test_db",
"UID" => "sa",
"PWD" => "your_password"
);
$conn = sqlsrv_connect($serverName, $connectionInfo);
if ($conn === false) {
die("数据库连接失败:" . print_r(sqlsrv_errors(), true));
}
echo "MSSQL数据库连接成功";
?>二、MSSQL事务的基本操作逻辑
无论使用哪种连接方式,MSSQL事务的核心操作都包含三个步骤:开启事务、执行数据库操作、根据操作结果提交或回滚事务。以下是事务操作的核心逻辑说明:
开启事务:通知数据库进入事务模式,后续的所有写操作(INSERT、UPDATE、DELETE)都不会立即生效,而是暂存在事务缓冲区中。
执行操作:在事务中执行需要的数据库操作,如果出现错误,立即终止后续操作进入回滚流程。
提交/回滚:如果所有操作都执行成功,调用提交方法让所有操作永久生效;如果有任意操作失败,调用回滚方法撤销所有未提交的暂存操作。
三、PDO方式实现MSSQL事务一致性
PDO扩展提供了原生的事务支持方法,分别是beginTransaction()开启事务、commit()提交事务、rollBack()回滚事务。以下是一个转账场景的示例,确保转出和转入两个操作要么同时成功,要么同时失败:
<?php
$host = '127.0.0.1';
$port = 1433;
$dbname = 'test_db';
$username = 'sa';
$password = 'your_password';
try {
$dsn = "sqlsrv:Server={$host},{$port};Database={$dbname}";
$pdo = new PDO($dsn, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 开启事务
$pdo->beginTransaction();
// 模拟转账操作:用户A转出500,用户B转入500
$sql1 = "UPDATE user_account SET balance = balance - 500 WHERE user_id = 1";
$stmt1 = $pdo->prepare($sql1);
$stmt1->execute();
// 检查第一个操作影响的行数,避免用户不存在的情况
if ($stmt1->rowCount() != 1) {
throw new Exception("用户A账户扣除余额失败");
}
$sql2 = "UPDATE user_account SET balance = balance + 500 WHERE user_id = 2";
$stmt2 = $pdo->prepare($sql2);
$stmt2->execute();
if ($stmt2->rowCount() != 1) {
throw new Exception("用户B账户增加余额失败");
}
// 所有操作成功,提交事务
$pdo->commit();
echo "转账操作成功,事务已提交";
} catch (Exception $e) {
// 捕获到异常,回滚事务
if (isset($pdo) && $pdo->inTransaction()) {
$pdo->rollBack();
}
echo "操作失败,事务已回滚:" . $e->getMessage();
}
?>四、SQLSRV方式实现MSSQL事务一致性
SQLSRV扩展需要通过执行MSSQL的原生事务语句来实现事务控制,分别是执行BEGIN TRANSACTION开启事务、COMMIT TRANSACTION提交事务、ROLLBACK TRANSACTION回滚事务。同样以转账场景为例,实现代码如下:
<?php
$serverName = "127.0.0.1,1433";
$connectionInfo = array(
"Database" => "test_db",
"UID" => "sa",
"PWD" => "your_password"
);
$conn = sqlsrv_connect($serverName, $connectionInfo);
if ($conn === false) {
die("数据库连接失败:" . print_r(sqlsrv_errors(), true));
}
// 标记事务是否成功
$transSuccess = false;
try {
// 开启MSSQL事务
$beginTrans = sqlsrv_query($conn, "BEGIN TRANSACTION");
if ($beginTrans === false) {
throw new Exception("开启事务失败");
}
// 用户A转出500
$sql1 = "UPDATE user_account SET balance = balance - 500 WHERE user_id = 1";
$stmt1 = sqlsrv_query($conn, $sql1);
if ($stmt1 === false) {
throw new Exception("用户A扣除余额失败:" . print_r(sqlsrv_errors(), true));
}
$rows1 = sqlsrv_rows_affected($stmt1);
if ($rows1 != 1) {
throw new Exception("用户A账户不存在或扣除失败");
}
// 用户B转入500
$sql2 = "UPDATE user_account SET balance = balance + 500 WHERE user_id = 2";
$stmt2 = sqlsrv_query($conn, $sql2);
if ($stmt2 === false) {
throw new Exception("用户B增加余额失败:" . print_r(sqlsrv_errors(), true));
}
$rows2 = sqlsrv_rows_affected($stmt2);
if ($rows2 != 1) {
throw new Exception("用户B账户不存在或增加失败");
}
// 所有操作成功,提交事务
$commitTrans = sqlsrv_query($conn, "COMMIT TRANSACTION");
if ($commitTrans === false) {
throw new Exception("提交事务失败");
}
$transSuccess = true;
echo "转账操作成功,事务已提交";
} catch (Exception $e) {
// 操作失败,回滚事务
sqlsrv_query($conn, "ROLLBACK TRANSACTION");
echo "操作失败,事务已回滚:" . $e->getMessage();
}
// 关闭数据库连接
sqlsrv_close($conn);
?>五、事务使用的注意事项
在实际开发中使用PHP处理MSSQL事务时,需要注意以下几点,才能确保事务一致性:
事务仅对支持事务的存储引擎生效,MSSQL的默认存储引擎支持事务,无需额外配置,但如果使用了特殊的表设置,需要确认事务支持情况。
事务中尽量避免执行耗时较长的操作(如大量循环、外部接口调用等),长时间占用事务会导致数据库锁持有时间变长,影响其他业务的并发性能。
需要在事务中执行的操作,要避免在事务外提前执行部分写操作,否则未纳入事务的操作无法被回滚,会破坏事务的原子性。
错误捕获要全面,不仅捕获PHP层面的异常,还要检查数据库操作返回的错误信息,避免部分操作失败但未被检测到导致事务错误提交。
如果使用的是PDO连接,需要注意
beginTransaction()不能嵌套调用,MSSQL本身也不支持嵌套事务,避免重复开启事务导致错误。
事务是保障数据库操作一致性的重要手段,但不是所有场景都必须使用事务。对于单个简单的写操作,或者不需要原子性保证的查询操作,不需要开启事务,避免不必要的性能开销。