在用戶操作MySQL過程中,對於一般簡單的業務邏輯或中小型程序而言,無需考慮應用MySQL事務。但在比較復雜的情況下,往往用戶在執行某些數據操作過程中,需要通過一組SQL語句執行多項並行業務邏輯或程序,這樣,就必須保證所用命令執行的同步性。使執行序列中,產生依靠關系的動作能夠同時操作成功或同時返回初始狀態。在此情況下,就需要用戶優先考慮使用MySQL事務處理。
在MySQL中,事務由單獨單元的一個或多個SQL語句組成。在這個單元中,每個MySQL語句是相互依賴的。而整個單獨單元作為一個不可分割的整體,如果單元中某條SQL語句一旦執行失敗或產生錯誤,整個單元將會回滾。所有受到影響的數據將返回到事務開始以前的狀態;如果單元中的所有SQL語句均執行成功,則事務被順利執行。
通過InnoDB和BDB類型表,MySQL事務能夠完全滿足事務安全的ACID測試,但是並不是所有表類型都支持事務,如MyISAM類型表就不能支持事務,只能通過偽事務對表實現事務處理。
MySQL事務的創建與存在周期
創建事務
創建事務的一般過程是:初始化事務、創建事務、應用SELECT語句查詢數據是否被錄入和提交事務。如果用戶不在操作數據庫完成后執行事務提交,則系統會默認執行回滾操作。如果用戶在提交事務前選擇撤銷事務,則用戶在撤銷前的所有事務將被取消,數據庫系統會回到初始狀態。
默認情況下,在MySQL中創建的數據表類型都是MyISAM,但是該類型的數據表並不能支持事務。所以,如果用戶想讓數據表支持事務處理能力,必須將當前操作數據表的類型設置為InnoDB或BDB。
在創建事務的過程中,用戶需要創建一個InnoDB或BDB類型的數據表,其基本命令結構如下:
CREATE TABLE table_name(field-defintions)TYPE=INNODB/BDB;
其中,table_name為表名,而field_defintions為表內定義的字段等屬性,TYPE為數據表的類型,既可以是InnoDB類型,同樣也可以是BDB類型。
當用戶希望已經存在的表支持事務處理,則可以應用ALTER TABLE命令指定數據表的類型實現對表的類型更改操作,使原本不支持事務的數據表更改為支持事務處理的類型。其命令如下:
ALTER TABLE table_name TYPE=INNODB/BDB;
當用戶更改完表的類型后,即可使數據表支持事務處理。
應用ALTER TABLE操作可能會導致數據庫中數據丟失,因此為了避免非預期結果出現,在使用ALTER TABLE命令之前,用戶需要創建一個表備份。
初始化事務
初始化MySQL事務,首先聲明初始化MySQL事務后所有的SQL語句為一個單元。在MySQL中,應用START TRANSACTION命令來標記一個事務的開始。初始化事務的結構如下:
START TRANSACTION;
另外,用戶也可以使用BEGIN或者BEGIN WORK命令初始化事務,通常START TRANSACTION命令后面跟隨的是組成事務的SQL語句。
在命令提示符中輸入如下命令:
start transaction;
如果在用戶輸入以上代碼后,MySQL數據庫沒有給出警告提示或返回錯誤信息,則說明事務初始化成功,用戶可以繼續執行下一步操作。
創建事務
insert into connection(email, cellphone, QQ, sid)
values('barrystephen@126.com',13456000000,187034000,3);
應用SELECT語句查看數據是否被正確輸入
SELECT * FROM connection WHERE sid=3;
ps:在用戶插入新表為"InnoDB"類型或更改原來表類型為"InnoDB"時,如果在輸入命令提示后,MySQL提示"The 'InnoDB' feature is disabled;you needInnoDB' to have it working"警告,則說明InnoDB表類型並沒有被開啟,用戶需要找到MySQL文件目錄下的"my.ini"文件,定位"skip_innodb"選項位置,將原來的"skip_innodb"改為"#skip_innodb"后保存該文件,重新啟動MySQL服務器,即可令數據庫支持"InnoDB"類型表。
提交事務
在用戶沒有提交事務之前,當其他用戶連接MySQL服務器時,應用SELECT語句查詢結果,則不會顯示沒有提交的事務。當且僅當用戶成功提交事務后,其他用戶才可能通過SELECT語句查詢事務結果,由事務的特性可知,事務具有孤立性,當事務處在處理過程中,其實MySQL並未將結果寫入磁盤中,這樣一來,這些正在處理的事務相對其他用戶是不可見的。一旦數據被正確插入,用戶可以使用COMMIT命令提交事務。提交事務的命令結構如下:
COMMIT
一旦當前執行事務的用戶提交當前事務,則其他用戶就可以通過會話查詢結果。
撤銷事務(事務回滾)
撤銷事務,又被稱作事務回滾。即事務被用戶開啟、用戶輸入的SQL語句被執行后,如果用戶想要撤銷剛才的數據庫操作,可使用ROLLBACK命令撤銷數據庫中的所有變化。ROLLBACK命令結構如下:
ROLLBACK
輸入回滾操作后,如何判斷是否執行回滾操作了呢?可以通過SELECT語句查看11.2.2小節中插入的數據是否存在.
如果執行一個回滾操作,則在輸入START TRANSACTIONA命令后的所有SQL語句都將執行回滾操作。故在執行事務回滾前,用戶需要慎重選擇執行回滾操作。如果用戶開啟事務后,沒有提交事務,則事務默認為自動回滾狀態,即不保存用戶之前的任何操作。
事務的存在周期
事務的周期由用戶在命令提示符中輸入START TRANSACTION指令開始,直至用戶輸入COMMIT結束.
事務不支持嵌套功能,當用戶在未結束第一個事務又重新打開一個事務,則前一個事務會自動提交,同樣MySQL命令中很多命令都會隱藏執行COMMIT命令。
MySQL行為
在MySQL中,存在兩個可以控制行為的變量,它們分別是AUTOCOMMIT變量和TRANSACTION ISOLACTION LEVEL變量。
自動提交
在MySQL中,如果不更改其自動提交變量,則系統會自動向數據庫提交結果,用戶在執行數據庫操作過程中,不需要使用START TRANSACTION語句開始事務,應用COMMIT或者ROLLBACK提交事務或執行回滾操作。如果用戶希望通過控制MySQL自動提交參數,可以更改提交模式,這一更改過程是通過設置AUTOCOMMIT變量來實現。
下面通過一個示例向讀者展示如何關閉自動提交參數。在命令提示符中輸入以下命令:
SET AUTOCOMMIT=0;
只有當用戶輸入COMMIT命令后,MySQL才將數據表中的資料提交到數據庫中,如果不提交事務,而終止MySQL會話,數據庫將會自動執行回滾操作。
可以通過查看"@@AUTOCOMMIT"變量來查看當前自動提交狀態,查看此變量SELECT @@AUTOCOMMIT。
事務的隔離級別
基於ANSI/ISO SQL規范,MySQL提供4種孤立級:
SERIALIZABLE(序列化)
REPEATABLE READ(可重讀)
READ COMMITTED(提交后讀)
READ UNCOMMITTED(未提交讀)
在MySQL中,可以使用TRANSACTION ISOLATION LEVEL變量來修改事務孤立級,其中,MySQL的默認隔離級別為REPEATABLE READ(可重讀),用戶可以使用SELECT命令獲取當前事務孤立級變量的值,其命令如下:
SELECT @@tx_isolation;
如果用戶想要修改事務的隔離級別,必須首先獲取SUPER優先權,以便用戶可以順利執行修改操作,set。
事務的使用技巧和注意事項
應用小事務,保證每個事務不會在執行前等待很長時間,從而避免各個事務因為互相等待而導致系統性能的大幅度下降。
選擇合適的孤立級,因為事務的性能與其對服務器產生的負載成反比,即當事務孤立級越高,其性能越低,但是其安全性也越高。只有選擇適當的孤立級,才能有效地提高MySQL系統性能和應用性。
死鎖的概念與避免,即當兩個或者多個處於不同序列的用戶打算同時更新某相同的數據庫時,因互相等待對方釋放權限而導致雙方一直處於等待狀態。在實際應用中,兩個不同序列的客戶打算同時對數據執行操作,極有可能產生死鎖。更具體地講,當兩個事務相互等待操作對方釋放所持有的資源,而導致兩個事務都無法操作對方持有的資源,這樣無限期的等待被稱作死鎖。MySQL的InnoDB表處理程序具有檢查死鎖這一功能,如果該處理程序發現用戶在操作過程中產生死鎖,該處理程序立刻通過撤銷操作來撤銷其中一個事務,以便使死鎖消失。這樣就可以使另一個事務獲取對方所占有的資源而執行邏輯操作。
MySQL偽事務
在MySQL中,InnoDB和BDB類型表可以支持事務處理,但是MySQL中MyISAM類型表並不能支持事務處理,對於某些應用該類型的數據表,用戶可以選擇應用表鎖定來替代事務。這種引用表鎖定來替代事務的事件被稱作偽事務。使用表鎖定來鎖定表的操作,可以加強非事務表在執行過程的安全性和穩定性。
用表鎖定代替事務
在MySQL的MyISAM類型數據表中,並不支持COMMIT(提交)和ROLLBACK(回滾)命令。當用戶對數據庫執行插入、刪除、更新等操作時,這些變化的數據都被立刻保存在磁盤中。這樣,在多用戶環境中,會導致諸多問題。為了避免同一時間有多個用戶對數據庫中指定表進行操作,可以應用表鎖定來避免在用戶操作數據表過程中受到干擾。當且僅當該用戶釋放表的操作鎖定后,其他用戶才可以訪問這些修改后的數據表。
設置表鎖定代替事務基本步驟如下:
(1)為指定數據表添加鎖定。其語法如下:
LOCK TABLES table_name lock_type,……
其中,table_name為被鎖定的表名,lock_type為鎖定類型,該類型包括以讀方式(READ)鎖定表,以寫方式(WRITE)鎖定表。
(2)用戶執行數據表的操作,可以添加、刪除或者更改部分數據。
(3)用戶完成對鎖定數據表的操作后,需要對該表進行解鎖操作,釋放該表的鎖定狀態。其語法如下:
UNLOCK TABLES
以讀方式鎖定數據表,該方式是設置鎖定用戶的其他方式操作,如刪除、插入、更新都不被允許,直至用戶進行解鎖操作。
lock table studentinfo read;
其中的lock_type參數中,用戶指定數據表以讀方式(READ)鎖定數據表的變體為READ LOCAL鎖定,其與READ鎖定的不同點是,該參數所指定的用戶會話可以執行INSERT操作,它是為了使用MySQL dump工具而創建的一種變體形式。
以寫方式鎖定數據表,該方式是是設置用戶可以修改數據表中的數據,但是除自己以外其他會話中的用戶不能進行任何讀操作。在命令提示符中輸入如下命令:
lock table studentinfo write;
當數據表被釋放鎖定后,其他訪問數據庫的用戶即可查看數據表的內容。
應用表鎖實現偽事務
通過使用表鎖定對MyISAM表進行鎖定操作,以此過程來代替事務型表InnoDB,即應用表鎖定來實現偽事務。實現偽事務的一般步驟如下:
(1)對數據庫中的數據表進行鎖定操作,可以對多個表做不同的方式鎖定,其代碼格式如下:
LOCK TABLE table_name1 lock_type1,table_name2 lock_type2,……
(2)執行數據庫操作,向鎖定的數據表中執行添加、刪除、修改操等操作。
如前面提到的INSERT、UPDATE、DELETE等操作。用戶可以對鎖定的數據表執行上述操作,在執行過程中,該偽事務所產生的結果是不會被其他用戶更改的。
(3)釋放鎖定的數據表,以便讓正在隊列中等待查看或操作的其他用戶可以瀏覽數據表中的數據或對操作表執行各種數據的操作。
如果存在其他會話要求訪問已鎖定的多個表格,則該會話必須被迫等待當前鎖定用戶釋放鎖定表。才允許其他會話訪問該數據表,表鎖定使不同會話執行的數據庫操作彼此獨立。應用數據表鎖定方式可以使不支持事務類型的表實現偽事務。
一、MySQL 事務
事務就是指邏輯上的一組 SQL 操作,組成這組操作的各個 SQL 語句,執行時要么全成功要么全失敗。
舉個例子,小明給小紅轉賬100元,轉賬過程實際上就是小明的賬戶減少100元,小紅的賬戶增加100元,對應的SQL語句為:
update account set money=money-5 where name='xiaoming';
update account set money=money+5 where name='xiaohong';
上述的兩條SQL操作,在事務中的操作就是要么都執行成功,要么都執行失敗,如果只有第一條成功,那么小明就損失100元,而小紅並沒有收到100元,這是不可取的,所以這就是事務,事務就是指邏輯上的一組 SQL 操作,組成這組操作的各個 SQL 語句,執行時要么全成功要么全失敗。事務處理可以用來維護數據庫的完整性,保證成批的 SQL 語句要么全部執行,要么全部不執行,在 MySQL 中只有使用了 Innodb 庫引擎的數據庫或表才支持事務,所以很多情況下我們都使用 Innodb 引擎.
事務的特性如下:
原子性:事務是一個不可分割的單位,事務中的所有 SQL 操作要么都成功,要么都失敗
一致性:事務發生前和發生后,數據的完整性必須保持一致
隔離性:當並發訪問數據庫時,一個正在執行的事務在執行完畢前,對於其它的會話是不可見的,多個並發事務之間的數據是相互隔離的
持久性:一個事務一旦被提交,它對數據庫中的數據改變就是永久性的,如果出了錯誤,事務也不允許撤銷,只能通過 "補償性事務"
mysql> begin # 開啟事務
mysql> rollback # 回滾事務
mysql> commit # 提交事務
數據庫默認事務是自動提交的,也就是說,當我們執行 select,insert,update,delete 等操作時,就會自動提交事務,如果關閉事務的自動提交,那么我們執行完 select,insert,update,delete 操作后需要再執行 commit 來提交事務,否則就不會執行
二、游標
游標是系統為用戶開設的一個數據緩沖區,存放 SQL 語句的執行結果,用法如下:
In [1]: import pymysql
In [2]: c = MySQLdb.connect(user='root', passwd='root', db='test') # 連接數據庫
In [3]: cus = c.cursor() # 創建一個游標對象
In [4]: cus.execute('select * from user;') # 使用execute()方法可以執行SQL語句,執行后的結果會存在緩沖區
Out[4]: 4L
In [5]: result1 = cus.fetchone() # 可以使用fetchone()來查看緩沖區的一條記錄
In [6]: result2 = cus.fetchmany(3) # 可以使用fetchmany()來查看緩沖區的多條記錄
In [7]: result3 = cus.fetchall() # 可以使用fetchall()來查看所有的記錄
三、根據游標執行 MySQL 事務
#!/usr/bin/env python
import pymysql
def connect_mysql():
db_config = {
'host': '127.0.0.1',
'port': 3306,
'user': 'root',
'passwd': 'pzk123'
}
c = pymysql.connect(**db_config)
return c
if __name__ == '__main__':
c = connect_mysql() # 首先連接數據庫
cus = c.cursor() # 生成游標對象
sql = 'drop database test;' # 定義要執行的SQL語句
try:
cus.execute(sql) # 執行SQL語句
c.commit() # 如果執行成功就提交事務
except Exception as e:
c.rollback() # 如果執行失敗就回滾事務
raise e
finally:
c.close() # 最后記得關閉數據庫連接