mysql怎么实现事务序列化_MySQL 架构 - 事务处理
事務處理
在事務處理之前,你不能知道數據庫系統有如此多的高級功能。一個事物就是一組SQL查詢。這一組被看做是原子的。也就是一個單獨的工作單元。如果數據庫引擎可以應用整個組的查詢,就執行完畢。但是如果其中一條語句出現問題,整個組的語句都不會被執行。也就是要么全部執行,要么全部不執行。
這部分所講到的事物很少是針對MySQL的,如果你已經熟悉了ACID事務處理,可以跳過這一部分。
銀行的應用是解釋為什么需要事務處理的經典案例。假設銀行數據有兩張表。checking以及savings.從Jane的checking賬戶轉賬200到她的saving賬戶。至少要三步
確定她的checking賬戶至少有200
從checking賬戶扣除200
把200添加到她的savings賬戶中。
整個操作封裝為了一個事物。因此其中一個操作失敗,整個操作都會回滾。
開始一個事物的語句是START TRANSACTION以及修改成功用COMMIT或者放棄改變用ROLLBACK。因此這個例子的SQL語句如下
START TRANSACTION;
SELECT balance FROM checking WHERE customer_id = 10233276;
UPDATE checking SET balance = balance - 200.00 WHERE customer_id = 10233276;
UPDATE savings SET balance = balance + 200.00 WHERE customer_id = 10233276;
COMMIT;
但是單獨的事物并不能代表全部。如果在數據庫服務器在執行到第四行的時候掛掉呢?用戶可能就白白損失了200。以及如果有個處理出現在了3,4行之間,而不會去扣除checking帳戶的余額。那么銀行就白白給了用戶200.
在系統沒有通過ACID的測試之前,僅僅有事物還是不夠的。ACID的意思是原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)。這些都是一個好的事務處理系統所要遵循的標準。
原子性(Atomicity):
一個事物必須做為一個單獨不可分割的工作單元來運行。因此整個事物要么全部成功要么全部失敗。當事物具有原子性的時候,不能一部分的執行。要么全部執行,要么全部失敗。
一致性(Consistency):
數據庫應該總是從一個一致的狀態到另一個。在我們的例子中,一致性要確保程序在3,4行崩潰并不會使checking帳戶減少200。因為事物沒有提交。數據庫沒有任何變化。
隔離性(Isolation):
當這個事物沒有完成的時候,它的結果對于其他的事物是不可見的。這點確保了在運行完第3行,以及在第4行之前。200還是在checking帳戶中。當我們討論隔離性的時候,我們常常用到不可見(invisible)這個詞。
持久性(Durability):
當事物提交,這個事物的改變就被持久化了。意思就是更改被記錄了。數據不會丟失了。持久性是個挺模糊的概念。因為它有很多級別。一些持久性策略比其他的有更強的安全性保證。但是絕對沒有100%的持久。我們會在以后的章節討論在MySQL中持久性的意思。
ACID事物保證了銀行不會損失錢。這點在業務邏輯上很難或者根本做不到。一個有ACID的數據庫服務器已經把各種各樣復雜的事情都解決了,使你沒有必要自己去確保ACID。
隨著鎖的顆粒度上升,數據庫服務器要在安全性上做更多的工作了。數據庫的ACID事物也需要更多的CPU,內存,硬盤空間。我們多次說過MySQL存儲引擎架構的優勢。你可以決定到底是否使用事物。如果不需要可以選擇更好的沒有事務支持的存儲引擎。在沒有事物的時候,你可以使用LOCK TABLES來保護數據。決定權在于你。
隔離級別
隔離要比看上去復雜的多。SQL定義了4種隔離級別。這些規則讓更改對事物的內部和外部可見和不可見。低級別的隔離可以高并發低消耗。
(每個存儲引擎的隔離級別實現都是不同的,如果你過去經常使用其他數據庫產品,存儲引擎不一定符合你的需求,你應該看手冊決定使用哪個存儲引擎。)
未提交讀(READ UNCOMMITTED)
在這個級別中,事物可以看到未提交事物的結果。這個級別有很多問題會發生,除非你真的真的明白你在做什么以及有足夠的理由去做。這級別在實踐中很少使用。因為性能相對于其他級別也沒什么優勢。讀取未提交的數據,也叫臟讀(dirty read)
已提交讀(READ COMMITTED)
有許多數據庫系統默認的隔離級別都是已提交讀(MySQL并不是)。它滿足了早期使用的隔離簡單定義:一個事物可以看到事物提交后的改變。這種改變在提交之前對于其他事物是不可見的。這級別也經常叫做不可重復讀(nonrepeatable read)。意思就是你運行同一語句兩次,可以看到不同的數據。
可重復讀(REPEATABLE READ)
可重復讀解決了未提交讀的問題。它保證了在同一個事物中,連續任意行的讀的數據都是相同的。但這種方式也引起了其他惡心的問題。幻讀(phantom reads.)。簡單地說就是,當你選擇一定范圍的行,另一個事物想這個范圍新增加了一行。之后有查詢相同的范圍。你就會發現出現了像幻覺一樣的一行。InnoDB和Falcon用多版本并發控制來解決了幻讀的問題。
可重復讀是MySQL默認的事物隔離級別。InnoDB和Falcon也是遵循這個設置。以后會講到怎樣修改這個設置。其他的引擎也是這樣的,但是決定權在于引擎。
可序列化(SERIALIZABLE)
最高的隔離級別。解決的幻讀的問題。強迫事物是有序的。因此他們不可能沖突。簡單的意思就是可序列化把每一行加一個鎖。這一級別許多超時和鎖的競爭將出現。我們很少看見人們使用這個級別的隔離。你的應用也有可能強迫你把隔離級別設置那么高,而忽視并發性。重視數據的穩定性。
隔離級別
臟讀
不可重復讀
幻讀
讀取鎖
未提交讀
是
是
是
否
已提交讀
否
是
是
否
可重復讀
否
否
是
否
可序列化
否
否
否
是
死鎖(Deadlocks)
死鎖就是當兩個以上的事物相互的等待和請求同一資源,產生了循環依賴就導致死鎖。死鎖存在事物嘗試不同的順序去鎖定資源的時候。在任何時候多事物鎖定同一資源都會發生死鎖。舉個例子。。有兩個事物運行在StockPrice的表。
事物一
START TRANSACTION;
UPDATE StockPrice SET close = 45.50 WHERE stock_id = 4 and date = '2002-05-01';
UPDATE StockPrice SET close = 19.80 WHERE stock_id = 3 and date = '2002-05-02';
COMMIT;
事物二
START TRANSACTION;
UPDATE StockPrice SET high = 20.12 WHERE stock_id = 3 and date = '2002-05-02';
UPDATE StockPrice SET high = 47.20 WHERE stock_id = 4 and date = '2002-05-01';
COMMIT;
如果你足夠的倒霉,可能會發生如下情況。每個事物都完成第一條語句更新了StockPrice表。給它加了鎖。每個事物都試圖執行第二行。發現它被加鎖了。這兩個事物都會等待除自己另外一個事物結束。除非有操作能破壞這個死鎖狀況。
為了解決這個問題,數據庫系統實現了許多種死鎖的檢測和超時處理。在更復雜的系統中,如InnoDB存儲引擎。會發現循環依賴并且及時的返回錯誤。真是非常好的方式,否則死鎖會降低語句的執行效率。其他的方式是在鎖等待超時之后,放棄操作。這個方式不太好。InnoDB處理死鎖的方式是,回滾有最少行級鎖的事物。
鎖的行為和順序是存儲引擎特定的。因此有些存儲引擎可能在特定的一系列語句發生死鎖,其他的也許不會。死鎖有雙重特性:一些是不可避免的,因為確實是數據沖突。一些是由于存儲引擎工作方式所引起的。
事務日志
事物日志的幫助可以讓事物更有效率。存儲引擎更改存儲在內存中的數據拷貝,取代每次修改都要更新硬盤上的表。這種方法非常高效。之后,存儲引擎就向事務日志添加一條記錄。這個日志存放在硬盤中,因此是持久的。這個操作相對來說能快點。因為附加記錄事件使用小范圍的連續IO取代了大范圍的隨機IO。之后,在晚一點,會更新硬盤上的表。所以大部分存儲引擎都使用這個技術(write-ahead logging 預寫式記錄),會兩次向硬盤寫入更改的信息。
在更新日志之后,更改數據之前發生了錯誤。存儲引擎仍然在重啟后恢復更改。各個存儲引擎的恢復方法各不相同。
MySQL中的事物
MySQL提供了三種支持事物的存儲引擎:?InnoDB, NDB Cluster, Falcon。許多第三方的引擎也可以使用,比較有名的就是solidDB 以及PBXT。在下一部分會詳細介紹這些存儲引擎。
AUTOCOMMIT
MySQL的AUTOCOMMIT是默認的。意思就是無論你是否開始一個事物,在每個語句都會自動執行。當然你可以設置這個AUTOCOMMIT的變量,方法如下
1和ON是一樣的。0也就是OFF。當AUTOCOMMIT=0的時候,你必須COMMIT或ROLLBACK,不然的話你就總在一個事物中,語句不會執行。如果表的存儲引擎不支持事物,改變AUTOCOMMIT的值,不會有任何的效果。這些存儲引擎是MyISAM或者Memory.它們總是自動提交。
一些命令,當開始一個事物,在執行之前,MySQL會自動提交事物。這些命令就是DDL。如果ALTER TABLE等,還有一些特殊如LOCK TABLES也會有這樣的效果。具體的查看數據庫版本的文檔。來查看哪些命令是自動提交的。
MySQL允許設置隔離級別。命令是SET TRANSACTION ISOLATION LEVEL 。這會影響下一個事物的開始時間。你也可以通過配置文件來設置整個服務器的隔離級別,這個將在以后說。也可以針對當前會話。如
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
MySQL能識別所有標準的隔離級別,以及InnoDB存儲引擎支持這些隔離級別。其他的存儲引擎不同程度上支持不同的隔離級別。
在事物中混合使用存儲引擎
在服務器級別,MySQL不能管理事物。而事物是由存儲引擎所實現的。意思就是你不能在一個單獨事物中混合使用不同的存儲引擎。MySQL AB給服務器添加了一個更高級別的事務管理服務。這樣就會在一個事物中,混合和匹配事務表更加安全。在此之前一定要小心。
如果在一個事物中混合了事物和非事物表。如果一切正常,這個事物就沒有問題,但是如果執行回滾操作,非事物表改變的數據并不會回滾。數據庫的一致性遭到了破壞,很難恢復和呈現完整的事物。這就是為什么給表選擇存儲引擎是如此的重要。
如果你在非事物表上做事物操作,MySQL并不會提示或者拋出異常。有的時候,回滾會有一定的提示。“Some nontransactional changed tables couldn’t be rolled back”。但是一般都不會有什么提示。
隱式和顯式的鎖定
InnoDB使用兩階段鎖定協議(two-phase locking protocol)。它能任意時間在一個事物之中獲得鎖。但是只要執行COMMIT或ROLLBACK才會釋放鎖。它總在同一時刻釋放鎖。鎖機制的描述都是隱式的。InnoDB根據你設置的隔離級別自動處理鎖。
然而,InnoDB也支持顯式加鎖。SQL的標準并沒有提到:
? SELECT ... LOCK IN SHARE MODE
? SELECT ... FOR UPDATE
MySQL也支持LOCK TABLES 和UNLOCK TABLES命令。這個是由服務器實現的。并不是存儲引擎。這個可以使用,但它并不不能取代事物。如果你要用事物,請使用支持事物的存儲引擎。
我們看到很多應用從MyISAM轉到InnoDB,但是還在使用LOCK TABLES.這沒什么必要,因為行級別的鎖定。LOCK TABLE也會引起性能問題。
(LOCK TABLES和事物之間的交互是復雜的。可能有些意想不到的異常出現。所以我們建議除非你不使用事物和把AUTOCOMMIT關閉,否則永遠不要使用LOCK TABLES。)
大小: 17.8 KB
分享到:
2009-06-01 17:01
瀏覽 1859
評論
總結
以上是生活随笔為你收集整理的mysql怎么实现事务序列化_MySQL 架构 - 事务处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql函数数组参数_MYSQL数组聚
- 下一篇: pil获得图片_OpenCV读取图片与P