mysql 事務處理(表的引擎必須是 innodb / BDB)
主要是兩種兩法:推薦用第一種
1.用 begin,rollback,commit 來實現
begin 開始一個事務
rollback 事務回滾
commit 事務提交
2.直接用 set 來改變 mysql的自動提交模式,系統默認是自動提交的
set autocommit = 0; 禁止自動提交
set autocommit = 1; 開啓自動提交
用第一種方式可這樣:
<?php
/*
表信息 innodb(id,text)
*/
require_once('conn.php'); // 數據庫鏈接信息
$flag = 1; // 標識是完成提交仍是回滾 1.提交完成 0.失敗回滾
//由於是自動提交的(默認),因此要想用事務,就必須手動開始事務,並手動提交
//開啓事務
mysql_query("BEGIN"); // 或者 mysql_query("START TRANSACTION"); 或者 mysql_query("begin work");
$sql1= "insert into innodb values('', 'xxx')";
$sql2 = "insert into innodb values('', 'iiii')";
$sql3 = "insert into innodb values('', 'eee'ee')";
if(!mysql_query($sql1)){$flag = 0;}
if(!mysql_query($sql2)){$flag = 0;}
if(!mysql_query($sql3)){$flag = 0;}
if($flag)
{
mysql_query("commit");
echo "commit";
}else
{
mysql_query("rollback");
echo "rollback";
}
// 由於 $sql3有誤,因此是沒法插入以上數據的
//結束這事務
mysql_query("END");
----------------
第二種方式可用:
<?php
require_once('conn.php'); // 數據庫鏈接信息
// 至關於開啓了事務,禁止自動提交
mysql_query("set autocommit=0", $conn);
$flag = 1; // 標識是完成提交仍是回滾 1.提交完成 0.失敗回滾
/*
表信息 innodb(id,text)
*/
$sql1= "insert into innodb values('', 'xxx')";
$sql2 = "insert into innodb values('', 'iiii')";
$sql3 = "insert into innodb values('', 'eee'ee')";
if(!mysql_query($sql1)){$flag = 0;}
if(!mysql_query($sql2)){$flag = 0;}
if(!mysql_query($sql3)){$flag = 0;}
if($flag)
{
mysql_query("commit");
echo "commit";
}else
{
mysql_query("rollback");
echo "rollback";
}
mysql_query("END", $conn); // 結束事務
mysql_query("set autocommit=1", $conn); // 還原自動提交,取消事務
// 由於 $sql3有誤,因此是沒法插入以上數據的
/*必定在提交或者回滾後,要調用 mysql_query("END", $conn);和mysql_query("set autocommit=1", $conn);,否則會把後面的 查詢也當作事務來處理,不提交時就沒法執行*/php
事務應具有的ACID特徵.
Atomic(原子性), Consistent(一致性),Isolated(隔離性),Durable(持續性)
原子性:組成事務處理的語句造成了一個邏輯單元,不能只執行其中的一部分。換
句話說,事務是不可分割的最小單元。好比:銀行轉賬過程當中,必須同時從一個賬
戶減去轉賬金額,並加到另外一個賬戶中,只改變一個賬戶是不合理的。
一致性:在事務處理執行先後,MySQL數據庫是一致的。也就是說,事務應該正確
的轉換系統狀態。好比:銀行轉賬過程當中,要麼轉賬金額從一個賬戶轉入另外一個賬
戶,要麼兩個賬戶都不變,沒有其餘的狀況。
隔離性:一個事務處理對另外一個事務處理沒有影響。就是說任何事務都不可能看到
一個處在不完整狀態下的事務。好比說,銀行轉賬過程當中,在轉賬事務沒有提交之
前,另外一個轉賬事務只能處於等待狀態。
持續性:事務處理的效果可以被永久保存下來。反過來講,事務應當可以承受全部
的失敗,包括服務器、進程、通訊以及媒體失敗等等。好比:銀行轉賬過程當中,轉
賬後賬戶的狀態要能被保存下來。
爲了一致性,可用到 select * ... for update;(默認是行鎖定,必須明確是主鍵
,否則變成了表鎖了)[be careful](做用於 update / delete 操做)[此時是悲觀鎖]
如:
begin;
//id爲主鍵,此行被鎖定,直到這個事務提交,否則不讓別的線程來更新此行數據
select * from tablename where id=1 for update;
//沒有指定主鍵,會鎖住個表
select * from tablename for update;
//明確指定主鍵,但無此數據,則無 lock
select * from tablename where id = 0 for update;
//無主鍵,表鎖
select * from tablename where name='xx' for update;
//主鍵不明確,鎖表
select * from tablename where id <> '1' for update;
select * from tablename where id like '1' for update;
例子:在兩個命令窗口可測試:
鏈接1:
begin;
select * from tablename where id=1 for update;
若是此時沒有 commit或rollback;
此時開如鏈接2:
update tablename set name="xxx" where id=2;
必定要等到鏈接1實現了 commit或rollback,鏈接2纔會真正執行,否則一直在等待,直到超時或提交或回滾
for update獨佔鎖
lock in share mode 共享鎖模式(會形成死鎖)
例如:
鏈接A:
begin;
select * from tablename where id=1 for update;
接着打開鏈接B:
begin;
update tablename set name="xx" where id=1;
或者 select * from tablename where id=1 for update;
此時必須等鏈接A中 commit或rollback鏈接B更新或刪除纔會執行生效
即 for update老是先以鏈接A爲基準點
lock in share mode:
鏈接C:
begin;
select * from tablename where id=1 lock in share mode;
接着打開鏈接D:
begin;
select * from tablename where id=1 lock in share mode;
再繼續執行鏈接C中的以下語句:
update tablename set name="xx" where id=1;
再往下執行鏈接D中的以下語句:
update tablename set name="yyyy" where id=1; ////此時出現死鎖
若是要避免這種狀況就得在鏈接D中先實現提交,再繼續執行
這樣理解:
若是是 for update,就得先完成最開始的事務,若是是 lock in share mode,就
得先完成最近的事務鎖mysql
myisam引擎(必定是)可實現鎖表法:
lock tables tablename write;
insert into tablename values();
unlock tables;
若是上述中沒有 unlock的話,另外的線程沒法 curd,直到上面執行 unlock
若是 :
lock tables tablename read;
此時該線程只能執行查詢,不能寫入操做,另外的線程能夠查詢,但也沒法寫入,直到unlocksql
以上鎖可概括爲悲歡鎖數據庫
樂觀鎖則是這樣的:服務器
通常在數據表增長一個表示狀態的字段,如開始狀態了1,則A鏈接,還沒來得更新,此時B鏈接上來,並修改了其中的數據,此時,該狀態變爲2,而後,A再更新數據,結果發現此時的狀態與以前的鏈接的不一致,變爲2了,說明已經變動過了,因此會通知A鏈接更新失敗。測試