Mysql对事务的支持
http://apps.hi.baidu.com/share/detail/31961419
?
MySQL與事務
歡迎訪問火丁筆記:http://huoding.com/
火丁的訂閱地址:http://huoding.com/feed
作者:老王
MySQL5.X都已經(jīng)發(fā)布好久了,但是還有很多人認為MySQL是不支持事務處理的,這不得不怪他們是孤陋寡聞的,其實,只要你的MySQL版本支持BDB或InnoDB表類型,那么你的MySQL就具有事務處理的能力。這里面,又以InnoDB表類型用的最多,雖然后來發(fā)生了諸如Oracle收購InnoDB等令MySQL不爽的事情,但那些商業(yè)上的斗爭與技術無關,下面以InnoDB表類型為例簡單說一下MySQL中的事務。
先來明確一下事務涉及的相關知識:
事務都應該具備ACID特征。所謂ACID是Atomic(原子性),Consistent(一致性),Isolated(隔離性),Durable(持續(xù)性)四個詞的首字母所寫,下面以“銀行轉帳”為例來分別說明一下它們的含義:
原子性:組成事務處理的語句形成了一個邏輯單元,不能只執(zhí)行其中的一部分。換句話說,事務是不可分割的最小單元。比如:銀行轉帳過程中,必須同時從一個帳戶減去轉帳金額,并加到另一個帳戶中,只改變一個帳戶是不合理的。
一致性:在事務處理執(zhí)行前后,數(shù)據(jù)庫是一致的。也就是說,事務應該正確的轉換系統(tǒng)狀態(tài)。比如:銀行轉帳過程中,要么轉帳金額從一個帳戶轉入另一個帳戶,要么兩個帳戶都不變,沒有其他的情況。
隔離性:一個事務處理對另一個事務處理沒有影響。就是說任何事務都不可能看到一個處在不完整狀態(tài)下的事務。比如說,銀行轉帳過程中,在轉帳事務沒有提交之前,另一個轉帳事務只能處于等待狀態(tài)。
持續(xù)性:事務處理的效果能夠被永久保存下來。反過來說,事務應當能夠承受所有的失敗,包括服務器、進程、通信以及媒體失敗等等。比如:銀行轉帳過程中,轉帳后帳戶的狀態(tài)要能被保存下來。
再來看看哪些問題會用到事務處理:
?
這里不說“銀行轉帳”的例子了,說一個大家實際更容易遇到的“網(wǎng)上購書”的例子。先假設一下問題的背景:網(wǎng)上購書,某書(數(shù)據(jù)庫編號為123)只剩最后一本,而這個時候,兩個用戶對這本書幾乎同時發(fā)出了購買請求,讓我們看看整個過程:
在具體分析之前,先來看看數(shù)據(jù)表的定義:
-------------------------------------------------------------------------------
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也行
-------------------------------------------------------------------------------
對于用戶甲來說,他的動作稍微比乙快一點點,其購買過程所觸發(fā)的動作大致是這樣的:
-------------------------------------------------------------------------------
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;
購書成功
-------------------------------------------------------------------------------
而對于用戶乙來說,他的動作稍微比甲慢一點點,其購買過程所觸發(fā)的動作和甲相同:
-------------------------------------------------------------------------------
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;
購書成功
-------------------------------------------------------------------------------
表面上看甲乙的操作都成功了,他們都買到了書,但是庫存只有一本,他們怎么可能都成功呢?再看看數(shù)據(jù)表里book_number的內容,已經(jīng)變成“-1”了,這當然是不能允許的(實際上,聲明這樣的列類型應該加上unsigned的屬性,以保證其不能為負,這里是為了說明問題所以沒有這樣設置)
好了,問題陳述清楚了,再來看看怎么利用事務來解決這個問題,打開MySQL手冊,可以看到想用事務來保護你的SQL正確執(zhí)行其實很簡單,基本就是三個語句:開始,提交,回滾。
-------------------------------------------------------------------------------
開始:START TRANSACTION或BEGIN語句可以開始一項新的事務
提交:COMMIT可以提交當前事務,是變更成為永久變更
回滾:ROLLBACK可以回滾當前事務,取消其變更
此外,SET AUTOCOMMIT = {0 | 1}可以禁用或啟用默認的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;
-------------------------------------------------------------------------------
答案是否定了,這樣依然不能避免問題的發(fā)生,如果想避免這樣的情況,實際應該如下:
-------------------------------------------------------------------------------
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請求的時候就會處于等待狀態(tài),直到上一個事務結束,它才能繼續(xù),從而避免了問題的發(fā)生,需要注意的是,如果你其他的事務使用的是不帶FOR UPDATE的SELECT語句,將得不到這種保護。
最后看看PHP + MySQL事務操作的代碼演示:
實際LAMP應用中,一般PHP使用AdoDB操作MySQL,下面給出AdoDB相應的代碼方便大家查閱:
-------------------------------------------------------------------------------
<?php
//?...
$adodb->startTrans();
//實際,getOne所調用的查詢也可以直接放到rowLock來進行,這里只是為了演示效果能更明顯些。
$adodb->rowLock('book',?'book_id?=?123');
$bookNumber?=?$adodb->getOne("SELECT?book_number?FROM?book?WHERE??book_id?=?123");
$adodb->execute("UPDATE?book?SET?book_number?=?book_number?-?1?WHERE??book_id?=?123");
$adodb->completeTrans();
//?...
?>
-------------------------------------------------------------------------------
其中,rowLock的方法就是調用的FOR UPDATE來實現(xiàn)的行鎖,你可能會想把“FOR UPDATE”直接寫到$adodb->getOne()調用的那條SQL語句里面去實現(xiàn)行鎖的功能,不錯,那樣確實可以,但是并不是所有的數(shù)據(jù)庫都使用“FOR UPDATE”語法來實現(xiàn)行鎖功能,比如Sybase使用“HOLDLOCK”的語法來實現(xiàn)行鎖功能,所以為了你的數(shù)據(jù)庫抽象層保持可移植性,我還是勸你用rowLock來實現(xiàn)行鎖功能,至于可移植性就交給AdoDB好了,嗯,有點扯遠了,今兒就說到這里了。
-------------------------------------------------------------------------------
附:
AdoDB中存在一個setTransactionMode()方法,能夠設置事務的隔離級別,如下:
SetTransactionMode allows you to pass in the transaction mode to use for all subsequent transactions for that connection session. Note: if you have persistent connections and using mysql or mssql, you might have to explicitly reset your transaction mode at the beginning of each page request. This is only supported in postgresql, mssql, mysql with InnoDB and oci8 currently. For example:
$db->SetTransactionMode("SERIALIZABLE");
$db->BeginTrans();
$db->Execute(...); $db->Execute(...);
$db->CommiTrans();
$db->SetTransactionMode(""); // restore to default
$db->StartTrans();
$db->Execute(...); $db->Execute(...);
$db->CompleteTrans();
Supported values to pass in:
??? * READ UNCOMMITTED (allows dirty reads, but fastest)
??? * READ COMMITTED (default postgres, mssql and oci8)
??? * REPEATABLE READ (default mysql)
??? * SERIALIZABLE (slowest and most restrictive)
You can also pass in database specific values such as 'SNAPSHOT' for mssql or 'READ ONLY' for oci8/postgres.
為了您的安全,請只打開來源可靠的網(wǎng)址打開網(wǎng)站????取消
來自: http://hi.baidu.com/thinkinginlamp/blog/item/d677cffcb7098482b901a014.html?
總結
以上是生活随笔為你收集整理的Mysql对事务的支持的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring配置JDBC事务
- 下一篇: spring的view