重慶分公司,新征程啟航
為企業提供網站建設、域名注冊、服務器等服務
為企業提供網站建設、域名注冊、服務器等服務
如果是JDBC其實很簡單,就是先setAutoCommit(false);然后把你的數據庫操作語句都執行完了,再手動調用commit方法就行了,所謂的事務其實原則上很簡單,尤其是關系型數據庫,說白了就是所有的SQL成功了才提交,不成功就rollback僅此而已
創新互聯專業為企業提供安多網站建設、安多做網站、安多網站設計、安多網站制作等企業網站建設、網頁設計與制作、安多企業網站模板建站服務,10年安多做網站經驗,不只是建網站,更提供有價值的思路和整體網絡服務。
當多個用戶訪問同一份數據時,一個用戶在更改數據的過程中,可能有其他用戶同時發起更改請求,為保證數據庫記錄的更新從一個一致性狀態變為另外一個一致性狀態,使用事務處理是非常必要的,事務具有以下四個特性:
MySQL 提供了多種事務型存儲引擎,如 InnoDB 和 BDB 等,而 MyISAM 不支持事務。為了支持事務,InnoDB 存儲引擎引入了與事務處理相關的 REDO 日志和 UNDO 日志,同時事務依賴于 MySQL 提供的鎖機制
事務執行時需要將執行的事務日志寫入日志文件,對應的文件為 REDO 日志。當每條 SQL 進行數據更新操作時,首先將 REDO 日志寫進日志緩沖區。當客戶端執行 COMMIT 命令提交時,日志緩沖區的內容將被刷新到磁盤,日志緩沖區的刷新方式或者時間間隔可以通過參數 innodb_flush_log_at_trx_commit 控制
REDO 日志對應磁盤上的 ib_logifleN 文件,該文件默認為 5MB,建議設置為 512MB,以便容納較大的事務。MySQL 崩潰恢復時會重新執行 REDO 日志的記錄,恢復最新數據,保證已提交事務的持久性
與 REDO 日志相反,UNDO 日志主要用于事務異常時的數據回滾,具體內容就是記錄數據被修改前的信息到 UNDO 緩沖區,然后在合適的時間將內容刷新到磁盤
假如由于系統錯誤或者 rollback 操作而導致事務回滾,可以根據 undo 日志回滾到沒修改前的狀態,保證未提交事務的原子性
與 REDO 日志不同的是,磁盤上不存在單獨的 UNDO 日志文件,所有的 UNDO 日志均存在表空間對應的 .ibd 數據文件中,即使 MySQL 服務啟動了獨立表空間
在 MySQL 中,可以使用 BEGIN 開始事務,使用 COMMIT 結束事務,中間可以使用 ROLLBACK 回滾事務。MySQL 通過 SET AUTOCOMMIT、START TRANSACTION、COMMIT 和 ROLLBACK 等語句支持本地事務
MySQL 定義了四種隔離級別,指定事務中哪些數據改變其他事務可見、哪些數據該表其他事務不可見。低級別的隔離級別可以支持更高的并發處理,同時占用的系統資源更少
InnoDB 系統級事務隔離級別可以使用以下語句設置:
查看系統級事務隔離級別:
InnoDB 會話級事務隔離級別可以使用以下語句設置:
查看會話級事務隔離級別:
在該隔離級別,所有事務都可以看到其他未提交事務的執行結果。讀取未提交的數據稱為臟讀(Dirty Read),即是:首先開啟 A 和 B 兩個事務,在 B 事務更新但未提交之前,A 事務讀取到了更新后的數據,但由于 B 事務回滾,導致 A 事務出現了臟讀現象
所有事務只能看見已經提交事務所做的改變,此級別可以解決臟讀,但也會導致不可重復讀(Nonrepeatable Read):首先開啟 A 和 B 兩個事務,A事務讀取了 B 事務的數據,在 B 事務更新并提交后,A 事務又讀取到了更新后的數據,此時就出現了同一 A 事務中的查詢出現了不同的查詢結果
MySQL 默認的事務隔離級別,能確保同一事務的多個實例在并發讀取數據時看到同樣的數據行,理論上會導致一個問題,幻讀(Phontom Read)。例如,第一個事務對一個表中的數據做了修改,這種修改會涉及表中的全部數據行,同時第二個事務也修改這個表中的數據,這次的修改是向表中插入一行新數據,此時就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行
InnoDB 通過多版本并發控制機制(MVCC)解決了該問題:InnoDB 通過為每個數據行增加兩個隱含值的方式來實現,這兩個隱含值記錄了行的創建時間、過期時間以及每一行存儲時間發生時的系統版本號,每個查詢根據事務的版本號來查詢結果
通過強制事務排序,使其不可能相互沖突,從而解決幻讀問題。簡而言之,就是在每個讀的數據行上加上共享鎖實現,這個級別會導致大量的超時現象和鎖競爭,一般不推薦使用
為了解決數據庫并發控制問題,如走到同一時刻客戶端對同一張表做更新或者查詢操作,需要對并發操作進行控制,因此產生了鎖
共享鎖的粒度是行或者元組(多個行),一個事務獲取了共享鎖以后,可以對鎖定范圍內的數據執行讀操作
排他鎖的粒度與共享鎖相同,一個事務獲取排他鎖以后,可以對鎖定范圍內的數據執行寫操作
有兩個事務 A 和 B,如果事務 A 獲取了一個元組的共享鎖,事務 B 還可以立即獲取這個元組的共享鎖,但不能獲取這個元組的排他鎖,必須等到事務 A 釋放共享鎖之后。如果事務 A 獲取了一個元組的排他鎖,事務 B 不能立即獲取這個元組的共享鎖,也不能立即獲取這個元組的排他鎖,必須等到 A 釋放排他鎖之后
意向鎖是一種表鎖,鎖定的粒度是整張表,分為意向共享鎖和意向排他鎖。意向共享鎖表示一個事務有意對數據上共享鎖或者排他鎖。有意表示事務想執行操作但還沒真正執行
鎖的粒度主要分為表鎖和行鎖
表鎖的開銷最小,同時允許的并發量也是最小。MyISAM 存儲引擎使用該鎖機制。當要寫入數據時,整個表記錄被鎖,此時其他讀/寫動作一律等待。一些特定的動作,如 ALTER TABLE 執行時使用的也是表鎖
行鎖可以支持最大的并發,InnoDB 存儲引擎使用該鎖機制。如果要支持并發讀/寫,建議采用 InnoDB 存儲引擎
什么是事務? \x0d\x0a\x0d\x0a事務是邏輯上的一組操作,組成這組操作的各個單元,要不全都成功要不全都失敗,這個特性就是事務 \x0d\x0a\x0d\x0a注意:mysql數據支持事務,但是要求必須是innoDB存儲引擎 \x0d\x0a\x0d\x0a解決這個問題: \x0d\x0a\x0d\x0amysql的事務解決這個問題,因為mysql的事務特性,要求這組操作,要不全都成功,要不全都失敗,這樣就避免了某個操作成功某個操作失敗。利于數據的安全 \x0d\x0a\x0d\x0a如何使用: \x0d\x0a\x0d\x0a(1)在執行sql語句之前,我們要開啟事務 start transaction; \x0d\x0a\x0d\x0a(2)正常執行我們的sql語句 \x0d\x0a\x0d\x0a(3)當sql語句執行完畢,存在兩種情況: \x0d\x0a\x0d\x0a1,全都成功,我們要將sql語句對數據庫造成的影響提交到數據庫中,committ \x0d\x0a\x0d\x0a2,某些sql語句失敗,我們執行rollback(回滾),將對數據庫操作趕緊撤銷 \x0d\x0a\x0d\x0a(注意:mysql數據支持事務,但是要求必須是innoDB存儲引擎) \x0d\x0amysql create table bank(name varchar(20),money decimal(5,1))engine=innodb defau \x0d\x0alt charset=utf8; \x0d\x0a\x0d\x0amysql inset into bank values('shaotuo',1000),('laohu',5000); \x0d\x0a\x0d\x0amysql select*from bank; \x0d\x0a+---------+--------+ \x0d\x0a| name | money | \x0d\x0a+---------+--------+ \x0d\x0a| shaotuo | 1000.0 | \x0d\x0a| laohu | 5000.0 | \x0d\x0a+---------+--------+ \x0d\x0a\x0d\x0a------沒有成功“回滾”執行rollback \x0d\x0amysql start transaction; //開啟事務 \x0d\x0aQuery OK, 0 rows affected (0.00 sec) \x0d\x0a\x0d\x0amysql update bank set money=money+500 where name='shaotuo'; \x0d\x0aQuery OK, 1 row affected (0.00 sec) \x0d\x0aRows matched: 1 Changed: 1 Warnings: 0 \x0d\x0a\x0d\x0amysql update bank set moey=money-500 where name='laohu'; \x0d\x0aERROR 1054 (42S22): Unknown column 'moey' in 'field list' \x0d\x0amysql rollback; //只要有一個不成功,執行rollback操作 \x0d\x0aQuery OK, 0 rows affected (0.01 sec) \x0d\x0a\x0d\x0amysql select*from bank; \x0d\x0a+---------+--------+ \x0d\x0a| name | money | \x0d\x0a+---------+--------+ \x0d\x0a| shaotuo | 1000.0 | \x0d\x0a| laohu | 5000.0 | \x0d\x0a+---------+--------+ \x0d\x0a------成功之后 進行commit操作 \x0d\x0amysql start transaction; //開啟事務 \x0d\x0aQuery OK, 0 rows affected (0.00 sec) \x0d\x0a\x0d\x0amysql update bank set money=money+500 where name='shaotuo'; \x0d\x0aQuery OK, 1 row affected (0.01 sec) \x0d\x0aRows matched: 1 Changed: 1 Warnings: 0 \x0d\x0a\x0d\x0amysql update bank set money=money-500 where name='laohu'; \x0d\x0aQuery OK, 1 row affected (0.00 sec) \x0d\x0aRows matched: 1 Changed: 1 Warnings: 0 \x0d\x0a\x0d\x0amysql commit; //兩個都成功后執行commit(只要不執行commit,sql語句不會對真實的數據庫造成影響) \x0d\x0aQuery OK, 0 rows affected (0.05 sec) \x0d\x0a\x0d\x0amysql select*from bank; \x0d\x0a+---------+--------+ \x0d\x0a| name | money | \x0d\x0a+---------+--------+ \x0d\x0a| shaotuo | 1500.0 | \x0d\x0a| laohu | 4500.0 | \x0d\x0a+---------+--------+
6.7 MySQL 事務與鎖定命令
6.7.1 BEGIN/COMMIT/ROLLBACK 句法
缺省的,MySQL 運行在 autocommit 模式。這就意味著,當你執行完一個更新時,MySQL 將立刻將更新存儲到磁盤上。
如果你使用事務安全表 (例如 InnoDB、BDB),通過下面的命令,你可以設置 MySQL 為非 autocommit 模式:
SET AUTOCOMMIT=0
在此之后,你必須使用 COMMIT 來存儲你的更改到磁盤上,或者使用 ROLLBACK ,如果你希望忽略從你的事務開始所做的更改。
如果你希望為一系列語句從 AUTOCOMMIT 模式轉換,你可以使用 START TRANSACTION 或 BEGIN 或 BEGIN WORK 語句:
START TRANSACTION;
SELECT @A:=SUM(salary) FROM table1 WHERE type=1;
UPDATE table2 SET summmary=@A WHERE type=1;
COMMIT;
START TRANSACTION 在 MySQL 4.0.11 中被加入;這是被推薦的開始一個特別(ad-hoc)事務的方式,因為這是 ANSI SQL 句法。
注意,如果你使用的是一個非事務安全表,更改會立刻被存儲,不受 autocommit 模式狀態的約束。
當你更新了一個非事務表后,如果你執行一個 ROLLBACK,你將得到一個錯誤 (ER_WARNING_NOT_COMPLETE_ROLLBACK) 作為一個警告。所有事務安全表將被恢復,但是非事務安全表將不會改變。
如果你使用 START TRANSACTION 或 SET AUTOCOMMIT=0,你應該使用 MySQL
二進制日志做備份以代替老的更新日志。事務處理被以一個大塊形式存儲在二進制日志中,在 COMMIT
上面,為了保護回滾的事務,而不是被存儲的。查看章節 4.9.4 二進制日志。 如果您使用起動事務處理或集AUTOCOMMIT=0
,您應該使用MySQL 二進制日志為備份代替更舊的更新日志。 事務處理存儲在二進制登錄一大塊,做,保證, 滾的事務處理不存儲。 參見部分4
。9.4 二進制日志。
下列命令自動的結束一個事務 (就好像你在執行這個命令之前,做了一個 COMMIT):
命令 命令 命令
ALTER TABLE BEGIN CREATE INDEX
DROP DATABASE DROP TABLE RENAME TABLE
TRUNCATE
你可以使用 SET TRANSACTION ISOLATION LEVEL ... 改變事務的隔離級。查看章節 6.7.3 SET TRANSACTION 句法。
6.7.2 LOCK TABLES/UNLOCK TABLES 句法
LOCK TABLES tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}
[, tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE} ...]
...
UNLOCK TABLES
LOCK TABLES 為當前線程鎖定表。UNLOCK TABLES 釋放當前線程擁有的所有鎖定。當線程發出另一個 LOCK TABLES,或當與服務器的連接被關閉時,被當前線程鎖定的所有表將被自動地解鎖。
為了在 MySQL 4.0.2 使用 LOCK TABLES ,你必須擁有一個全局的 LOCK TABLES 權限和一個在相關表上的
SELECT 權限。在 MySQL 3.23 中,你對該表需要有 SELECT、insert、DELETE 和 UPDATE 權限。
使用 LOCK TABLES 的主要原因是,仿效事務處理或在更新表時得到更快的速度。此后會有更詳細的描述。
如果一個線程在一個表上得到一個 READ 鎖,該線程 (和所有其它線程) 只能從表中讀取。如果一個線程在一個表上得到一個 WRITE 鎖,那么只有擁有這個鎖的線程可以從表中讀取和寫表。其它的線程被阻塞。
READ LOCAL 和 READ 之間的不同就在于,當鎖被加載時,READ LOCAL 允許非沖突(non-conflicting) INSERT 語句執行。如果當你加載著鎖時從 MySQL 外部操作數據庫文件,這將仍不能被使用。
當你使用 LOCK TABLES 是地,你必須鎖定所有你將使用的表,并且必須使用與你的查詢中將使用的別名相同!如果你在一個查詢中多次使用一個表(用別名),你必須為每一個別名獲得一個鎖。
WRITE 鎖通過比 READ 鎖有更高的權限,以確保更新被盡快地處理。這就意味著,如果一個線程獲得一個 READ
鎖,而同時另外一個線程請求一個 WRITE 鎖,并發的 READ 鎖請求將等待直到 WRITE 線程得到了鎖并釋放了它。你可以使用
LOW_PRIORITY WRITE 鎖,當該線程在等待 WRITE 鎖時,它將允許其它的線程獲得 READ 鎖。 你應該只使用
LOW_PRIORITY WRITE 鎖,如果你確信這將是最后一次,當沒有線程將擁有 READ 鎖。
LOCK TABLES 工作如下:
以內部定義的次序排序所有被鎖定的表 (從用戶立場說,該次序是不明確的)。
如果一個表被以一個讀鎖和一個寫鎖鎖定,將寫鎖放在讀鎖之前。
一次只鎖定一個表,只到線程得到所有的鎖定。
這個方案是為了確保,表鎖定死鎖釋放。 對于這個模式你仍然有些其它事情需要知道:
如果你對一個表使用一個 LOW_PRIORITY WRITE 鎖定,這就意味著,MySQL 將等待這個鎖,直到沒有線程請求一個 READ
鎖。當線程得到了 WRITE 鎖,并等待獲得鎖定表列表中的下一個表的鎖定時,其它所有的線程將等待 WRITE
鎖被釋放。如果這在你的應用程序中會引起一個嚴重的問題,你應該考慮將你的某些表轉換為事務安全表。
你可以使用 KILL 安全地殺死一個正在表鎖定的線程。查看章節 4.5.5 KILL 句法。
注意,你不應該 鎖定你正在對其使用 INSERT DELAYED 的表。這是因為,在這種情況下,INSERT 是通過單獨的線程完成的。
通常,你不需要鎖定任何表,因為所有單 UPDATE 語句都是原子的;其它的線程無法干擾當前執行的 SQL 語句。當你無論如何希望鎖定表時,這里有一些情況:
如果你在一束表上運行許多操作,鎖定你將要使用的表,這會更快一些。當然有不利的方面,其它線程將不能更新一個 READ
鎖的表,并且沒有其它線程要以讀取一個 WRITE 鎖的表。 在 LOCK TABLES 下,某些事運行得更快一些的原因是,MySQL
將不會轉儲清除被鎖定表鍵高速緩沖,直到 UNLOCK TABLES 被調用 (通常鍵高速緩沖在每個 SQL 語句后都會被轉儲清除)。這將加速在
MyISAM 表上的插入、更新、刪除。
如果你在 MySQL 中正在使用一個不支持事務的存儲引擎,如果你希望能確保沒有其它的線程會出現在一個 SELECT 和 一個 UPDATE 之間,你必須使用 LOCK TABLES 。下面的示例顯示為了安全地執行,這里需要LOCK TABLES :
mysql LOCK TABLES trans READ, customer WRITE;
mysql SELECT SUM(value) FROM trans WHERE customer_id=some_id;
mysql UPDATE customer SET total_value=sum_from_previous_statement
- WHERE customer_id=some_id;
mysql UNLOCK TABLES;
不使用 LOCK TABLES,將可能發生在 SELECT 和 UPDATE 語句執行期間有另外一個線程可能在 trans 表中插入一行新記錄。
通過使用遞增更新 (UPDATE customer SET value=value+new_value) 或 LAST_INSERT_ID() 函數,你可以在很多情況下避免使用 LOCK TABLES。
你也可以使用用戶級鎖定函數 GET_LOCK() 和 RELEASE_LOCK() 解決一些情況,這些鎖被保存在服務器上的一個哈希表中,并以
pthread_mutex_lock() 和 pthread_mutex_unlock() 實現以獲得高速度。查看章節 6.3.6.2
輔助功能函數。
查看章節 5.3.1 MySQL 如何鎖定表,以獲取關于鎖定方案的更多信息。
你可以使用 FLUSH TABLES WITH READ LOCK 命令以讀鎖鎖定所有數據庫中的所有表。查看章節 4.5.3 FLUSH 句法。如果你有一個可以及時建立文件快照的文件系統,例如 Veritas,這將是得到備份的非常方便方式。
注意:LOCK TABLES 不是事務安全的,在嘗試鎖定一個表之前,將自動地提交所有的活動事務。
6.7.3 SET TRANSACTION 句法
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL
{ READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE }
設置全局的、整個會話或下一個事務的事務隔離級。
缺省行為是設置下一個(未啟動的)事務的隔離級。如果你使用 GLOBAL
關鍵詞,語句為所有在那個點上建立的新連接設置默認的全局事務隔離級。為了這樣做,你需要有 SUPER 權限。使用 SESSION
關鍵詞為當前連接所有將來執行的事務設置默認的事務隔離級。
你可以使用 --transaction-isolation=... 為 mysqld 設置默認的全局隔離級。查看章節 4.1.1 mysqld 命令行選項
/**
?*?啟動事務
?*?@return?bool
?*/
public?function?begin()
{
if?($this-transDepth?==?0)
{
$this-query('START?TRANSACTION');
}
$this-transDepth++;
return?TRUE;
}
/**
?*?事務提交
?*?@return?bool
?*/
public?function?commit()
{
if?($this-transDepth??0)
{
$result?=?$this-query('COMMIT');
$this-transDepth?=?0;
if(!$result)?die("err:trans?commit?".mysql_error());
}
return?TRUE;
}
/**
?*?事務回滾
?*?@return?bool
?*/
public?function?rollback()
{
if?($this-transDepth??0)
{
$result?=?$this-query('ROLLBACK');
$this-transDepth?=?0;
if(!$result)?die("err:trans?commit?".mysql_error());
}
return?TRUE;
}
什么是事務?
事務是邏輯上的一組操作,組成這組操作的各個單元,要不全都成功要不全都失敗,這個特性就是事務
注意:mysql數據支持事務,但是要求必須是innoDB存儲引擎
解決這個問題:
mysql的事務解決這個問題,因為mysql的事務特性,要求這組操作,要不全都成功,要不全都失敗,這樣就避免了某個操作成功某個操作失敗。利于數據的安全
如何使用:
(1)在執行sql語句之前,我們要開啟事務 start transaction;
(2)正常執行我們的sql語句
(3)當sql語句執行完畢,存在兩種情況:
1,全都成功,我們要將sql語句對數據庫造成的影響提交到數據庫中,committ
2,某些sql語句失敗,我們執行rollback(回滾),將對數據庫操作趕緊撤銷
(注意:mysql數據支持事務,但是要求必須是innoDB存儲引擎)
mysql create table bank(name varchar(20),money decimal(5,1))engine=innodb defau
lt charset=utf8;
mysql inset into bank values('shaotuo',1000),('laohu',5000);
mysql select*from bank;
+---------+--------+
| name | money |
+---------+--------+
| shaotuo | 1000.0 |
| laohu | 5000.0 |
+---------+--------+
------沒有成功“回滾”執行rollback
mysql start transaction; //開啟事務
Query OK, 0 rows affected (0.00 sec)
mysql update bank set money=money+500 where name='shaotuo';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql update bank set moey=money-500 where name='laohu';
ERROR 1054 (42S22): Unknown column 'moey' in 'field list'
mysql rollback; //只要有一個不成功,執行rollback操作
Query OK, 0 rows affected (0.01 sec)
mysql select*from bank;
+---------+--------+
| name | money |
+---------+--------+
| shaotuo | 1000.0 |
| laohu | 5000.0 |
+---------+--------+
------成功之后 進行commit操作
mysql start transaction; //開啟事務
Query OK, 0 rows affected (0.00 sec)
mysql update bank set money=money+500 where name='shaotuo';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql update bank set money=money-500 where name='laohu';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql commit; //兩個都成功后執行commit(只要不執行commit,sql語句不會對真實的數據庫造成影響)
Query OK, 0 rows affected (0.05 sec)
mysql select*from bank;
+---------+--------+
| name | money |
+---------+--------+
| shaotuo | 1500.0 |
| laohu | 4500.0 |
+---------+--------+