數據的事務是指做爲單個邏輯工做單元執行的一系列操做,要麼徹底執行,要麼徹底不執行。html
事務必須具有四個特性:mysql
>set autocommit = 0 禁止自動提交 >start transaction; >update accout set money=money+100 where name="Jason"; >commit;
>set autocommit = 0 禁止自動提交 >start transaction; >update account set money=money-100 where name="justin"; >rollback;
若是沒有隔離會發生這樣幾個問題:算法
一個事務處理過程裏讀取了另外一個未說起的的事務中的數據。sql
對於數據庫中的某個數據,一個事務範圍內屢次查詢卻能夠返回不一樣的數據值,這是因爲在查詢的間隔期間,另一個事務修改並提交了該數據。數據庫
在一個事務中讀取到了別的事務插入的數據,致使先後不同。好比:第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的所有數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,之後就會發生操做第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺同樣。segmentfault
查看當前事務級別session
select @@tx_isolation;
修改事務的隔離級別併發
語法ide
set [global | session] transaction isolation level 隔離級別名稱;
例如:高併發
set global transaction isolation level Repeatable read; # 修改全局的以後要從新創建會話纔有效
隔離級別:Serializable | Repeatable read | Read committed |Read uncommitted
推薦博客:http://blog.chinaunix.net/uid-14010457-id-3956842.html
數據庫爲了保證四個特性,特別是一致性和隔離性,採用了加鎖的方式。因爲數據庫是一個高併發的應用,同一時間有大量的併發訪問。若是加鎖過分的話,會極大地下降併發處理能力,因此對於加鎖的處理,是數據庫對於事務處理的精髓所在。
由於有大量的併發訪問,爲了預防死鎖,通常應用中採用的是一次封鎖的方案:就是在方法的開始階段,已經預先知道須要用到那些數據,而後所有鎖住,在方法執行以後,再所有解鎖。
這種方案能夠有效避免死鎖發生,當時因爲數據庫操做在事務開始階段並不知道具體會用到哪些數據,因此該方案不合適在數據庫中使用。
兩段鎖協議將事務分紅兩個階段:加鎖階段和解鎖階段
解鎖階段:當事務釋放了一個封鎖之後,事務進入解鎖階段,在該階段只能進行解鎖操做不能進行加鎖操做。
注:這個方案沒法避免死鎖,可是能夠保證事務調度的串行化(串行化在數據庫恢復和備份時候很重要)。
對一整張表加鎖,併發能力低下(即便有分讀鎖、寫鎖),通常在DDL處理時使用
下圖時三個鎖的位置關係圖:
只鎖住特定行的數據,併發能力強,MySQL通常都是用行鎖來處理併發事務。
若是用到無索引的字段,那麼MySQL會在存儲引擎層面將全部的記錄加鎖,而後由MySQL Server過濾,若是不知足會調用unlock_row把不知足條件的記錄釋放鎖(這裏違背了二段鎖協議)。# -----------------會話1--------------------------------------------------- mysql> create table t(id int,name varchar(10),key idx_id(id),primary key(name))engine =innodb; Query OK, 0 rows affected (0.03 sec) mysql> insert into t values(1,'a'),(3,'c'),(5,'e'),(8,'g'),(11,'j'); Query OK, 5 rows affected (0.01 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql> select @@global.tx_isolation, @@tx_isolation; +-----------------------+-----------------+ | @@global.tx_isolation | @@tx_isolation | +-----------------------+-----------------+ | REPEATABLE-READ | REPEATABLE-READ | +-----------------------+-----------------+ 1 row in set, 2 warnings (0.01 sec) mysql> select * from t; +------+------+ | id | name | +------+------+ | 1 | a | | 3 | c | | 5 | e | | 8 | g | | 11 | j | +------+------+ 5 rows in set (0.01 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> delete from t where id=8; Query OK, 1 row affected (0.00 sec) """ 能夠看到最後加了一個事務,對id=8的數據進行處理,然而沒有提交 """ # -----------------會話2--------------------------------------------------- mysql> select @@global.tx_isolation, @@tx_isolation; +-----------------------+-----------------+ | @@global.tx_isolation | @@tx_isolation | +-----------------------+-----------------+ | REPEATABLE-READ | REPEATABLE-READ | +-----------------------+-----------------+ 1 row in set, 2 warnings (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into t(id,name) values(6,'f'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(5,'e1'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(7,'h'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(8,'gg'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(9,'k'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(10,'p'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(11,'iz'); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t(id,name) values(5,'cz'); Query OK, 1 row affected (0.00 sec) mysql> insert into t(id,name) values(11,'ja'); Query OK, 1 row affected (0.00 sec) """ 分析:由於會話1已經對id=8的記錄加了一個X鎖,因爲是RR隔離級別,INNODB要防止幻讀須要加GAP鎖:即id=5(8的左邊),id=11(8的右邊)之間須要加間隙鎖(GAP)。 這樣[5,e]和[8,g],[8,g]和[11,j]之間的數據都要被鎖。上面測試已經驗證了這一點,根據索引的有序性,數據按照主鍵(name)排序,後面寫入的[5,cz]([5,e]的左邊) 和[11,ja]([11,j]的右邊)不屬於上面的範圍從而能夠寫入。 另一種狀況,把name主鍵去掉會是怎麼樣的狀況?有興趣的能夠測試一下。 """
上面的例子中,當插入被會話1鎖住的內容的時候,會有一個超時間,我用ctrl+c強制終止了。若是是在一個事務中,插入數據的時候超時了,會怎麼辦呢。
超時時間的參數:innodb_lock_wait_timeout ,默認是50秒。
超時是否回滾參數:innodb_rollback_on_timeout 默認是OFF。
在默認請求下,不會由於超時引起的異常而回滾,當參數innodb_rollback_on_timeout設置爲ON時,則會。
接下來再看一個例子
# -----------------會話1--------------------------------------------------- mysql> create table tt(a int primary key)engine =innodb; Query OK, 0 rows affected (0.06 sec) mysql> insert into tt values(1),(3),(5),(8),(11); Query OK, 5 rows affected (0.01 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql> select * from t; +------+------+ | id | name | +------+------+ | 1 | a | | 3 | c | | 5 | e | | 11 | j | +------+------+ 4 rows in set (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from tt where a = 8 for update; +---+ | a | +---+ | 8 | +---+ 1 row in set (0.00 sec) # -----------------會話2--------------------------------------------------- mysql> start transaction; Query OK, 0 rows affected (0.01 sec) mysql> insert into tt values(6); Query OK, 1 row affected (0.01 sec) mysql> insert into tt values(7); Query OK, 1 row affected (0.00 sec) mysql> insert into tt values(9); Query OK, 1 row affected (0.00 sec)
到這裏應該會有些人有問題了,爲啥這個例2和例1不同。
解釋:
由於InnoDB對於行的查詢都是採用了Next-Key Lock的算法,鎖定的不是單個值,而是一個範圍,按照這個方法是會和第一次測試結果同樣。可是,當查詢的索引含有惟一屬性的時候,Next-Key Lock 會進行優化,將其降級爲Record Lock,即僅鎖住索引自己,不是範圍。
注意:經過主鍵或則惟一索引來鎖定不存在的值,也會產生GAP鎖定。
注
若是不想出現那種阻塞的現象,能夠顯示的關閉GAP鎖
1:把事務隔離級別改爲:Read Committed,提交讀、不可重複讀。SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
2:修改參數:innodb_locks_unsafe_for_binlog 設置爲1。
注:innodb_locks_unsafe_for_binlog最主要的做用就是控制innodb是否對gap加鎖。該參數若是是enable的,則是unsafe的,此時gap不會加鎖;反之,若是disable掉該參數,則gap會加鎖。固然對於一些和數據完整性相關的定義,如外鍵和惟一索引(含主鍵)須要對gap進行加鎖,那麼innodb_locks_unsafe_for_binlog的設置並不會影響gap是否加鎖。
牛人博客推薦:http://hedengcheng.com/?p=771
關於這方面的知識推薦一個思否的文章:http://www.javashuo.com/article/p-nocfhqye-eu.html