事務都應該具備acid特徵。所謂acid是atomic(原子性),consistent(一致性),isolated(隔離性),durable(持續性)四個詞的首字母所寫,下面以「銀行轉帳」為例來分別說明一下它們的含義:
原子性:組成事務處理的語句形成了乙個邏輯單元,不能只執行其中的一部分。換句話說,事務是不可分割的最小單元。比如:銀行轉帳過程中,必須同時從乙個帳戶減去轉帳金額,並加到另乙個帳戶中,只改變乙個帳戶是不合理的。
一致性:在事務處理執行前後,資料庫是一致的。也就是說,事務應該正確的轉換系統狀態。比如:銀行轉帳過程中,要麼轉帳金額從乙個帳戶轉入另乙個帳戶,要麼兩個帳戶都不變,沒有其他的情況。
隔離性:乙個事務處理對另乙個事務處理沒有影響。就是說任何事務都不可能看到乙個處在不完整狀態下的事務。比如說,銀行轉帳過程中,在轉帳事務沒有提交之前,另乙個轉帳事務只能處於等待狀態。
持續性:事務處理的效果能夠被永久儲存下來。反過來說,事務應當能夠承受所有的失敗,包括伺服器、程序、通訊以及**失敗等等。比如:銀行轉帳過程中,轉帳後帳戶的狀態要能被儲存下來。
再來看看哪些問題會用到事務處理:
這裡不說「銀行轉帳」的例子了,說乙個大家實際更容易遇到的「網上購書」的例子。先假設一下問題的背景:網上購書,某書(資料庫編號為123)只剩最後一本,而這個時候,兩個使用者對這本書幾乎同時發出了購買請求,讓我們看看整個過程:
在具體分析之前,先來看看資料表的定義:
create table book
(book_id unsigned int(10) not null auto_increment,
book_name varchar(100) not null,
book_price float(5, 2) not null, #我假設每本書的**不會超過999.99元
book_number int(10) not null,
primary key (book_id)
)type = innodb; #engine = innodb也行
對於使用者甲來說,他的動作稍微比乙快一點點,其購買過程所觸發的動作大致是這樣的:
1. select book_number from book where book_id = 123;
book_number大於零,確認購買行為並更新book_number
2. update book set book_number = book_number - 1 where book_id = 123;
購書成功
而對於使用者乙來說,他的動作稍微比甲慢一點點,其購買過程所觸發的動作和甲相同:
1. select book_number from book where book_id = 123;
這個時候,甲剛剛進行完第一步的操作,還沒來得及做第二步操作,所以book_number一定大於零
2. update book set book_number = book_number - 1 where book_id = 123;
購書成功
表面上看甲乙的操作都成功了,他們都買到了書,但是庫存只有一本,他們怎麼可能都成功呢?再看看資料表裡book_number的內容,已經變成 「-1」了,這當然是不能允許的(實際上,宣告這樣的列型別應該加上unsigned的屬性,以保證其不能為負,這裡是為了說明問題所以沒有這樣設定)
好了,問題陳述清楚了,再來看看怎麼利用事務來解決這個問題,開啟mysql手冊,可以看到想用事務來保護你的sql正確執行其實很簡單,基本就是三個語句:開始,提交,回滾。
開始:start transaction或begin語句可以開始一項新的事務
提交:commit可以提交當前事務,是變更成為永久變更
回滾:rollback可以回滾當前事務,取消其變更
此外,set autocommit = 可以禁用或啟用預設的autocommit模式,用於當前連線。
那是不是只要用事務語句包一下我們的sql語句就能保證正確了呢?比如下面**:
begin;
select book_number from book where book_id = 123;
update book set book_number = book_number - 1 where book_id = 123;
commit;
答案是否定了,這樣依然不能避免問題的發生,如果想避免這樣的情況,實際應該如下:
begin;
select book_number from book where book_id = 123 for update ;
update book set book_number = book_number - 1 where book_id = 123;
commit;
由於加入了for update,所以會在此條記錄上加上乙個行鎖,如果此事務沒有完全結束,那麼其他的事務在使用select ... for update請求的時候就會處於等待狀態,直到上乙個事務結束,它才能繼續,從而避免了問題的發生,需要注意的是,如果你其他的事務使用的是不帶for update的select語句,將得不到這種保護。
學習參考如下
由於innodb預設是row-level lock,所以只有「明確」的指定主鍵,mysql才會執行row lock (只鎖住被選取的資料例) ,否則mysql將會執行table lock (將整個資料表單給鎖住)。
舉個例子:
假設有個表單products ,裡面有id跟name二個字段,id是主鍵。
例1: (明確指定主鍵,並且有此筆資料,row lock)
select * from products where id='3' for update;
例2: (明確指定主鍵,若查無此筆資料,無lock)
select * from products where id='-1' for update;
例2: (無主鍵,table lock)
select * from products where name='mouse' for update;
例3: (主鍵不明確,table lock)
select * from products where id<>'3' for update;
例4: (主鍵不明確,table lock)
select * from products where id like '3' for update;
注1: for update僅適用於innodb,且必須在交易區塊(begin/commit)中才能生效。
注2: 要測試鎖定的狀況,可以利用mysql的command mode ,開二個視窗來做測試。
關於yii的事務處理
$transaction->commit();//提交事務
$transaction->rollback();//回滾事務
// 以下例項將需要事務處理的操作放進try裡
try catch (exception $e)
PHP 事務處理
在對資料庫會進行多次操作的時候會使用到事務 transaction 其中的底層邏輯如下 1 開啟事務 begin 2 取消資料庫自動寫入 set autocommit 0 3 sql1賦值 第一句所要執行的內容 4 sql2賦值 等等 5 執行mysql query sql1 sqln 6 判斷sq...
PHP與MYSQL事務處理
mysql的事務處理主要有兩種方法。1 用begin,rollback,commit來實現 begin 開始乙個事務 rollback 事務回滾 commit 事務確認 2 直接用set來改變mysql的自動提交模式 mysql預設是自動提交的,也就是你提交乙個query,它就直接執行!我們可以通過...
PHP與MYSQL事務處理
mysql的事務處理主要有兩種方法。1 用begin,rollback,commit來實現 begin 開始乙個事務 rollback 事務回滾 commit 事務確認 2 直接用set來改變mysql的自動提交模式 conn mysql connect localhost root root or...