1. 程式人生 > 資料庫 >Mysql事物原理及鎖

Mysql事物原理及鎖

一、Innodb事物原理

1.什麼是事物

a.事務(Transaction)是資料庫區別於檔案系統的重要特性之一,事務會把資料庫從一種一致性狀態轉換為另一種一致性狀態。

b.在資料庫提交時,可以確保要麼所有修改都已儲存,要麼所有修改都不儲存。

2.事務的特性:(ACID)

a.原子性(Atomicity):事務中的全部操作在資料庫中是不可分割的,要麼全部完成,要麼均不執行。

b.一致性(Consistency):幾個並行執行的事務,其執行結果必須與按某一順序序列執行的結果相一致。

c.隔離性(Isolation):事務的執行不受其他事務的干擾,事務執行的中間結果對其他事務必須是透明的。

d.永續性(Durability):對於任意已提交事務,系統必須保證該事務對資料庫的改變不被丟失,即使資料庫出現故障.

3.事務的分類:

3.1扁平事務(Flat Transactions)

a.扁平事務是事務型別中最簡單但使用最頻繁的事務。

b.在扁平事務中,所有的操作都處於同一層次,由BEGIN/START TRANSACTION開始事務,由COMMIT/ROLLBACK結束,且都是原子的,要麼都執行,要麼都回滾

c.扁平事務是應用程式成為原子操作的基本組成模組。

扁平事務一般有四種不同的結果:

 

1.事務的完成

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into student(name) value('wangwu');
Query OK, 1 row affected (0.00 sec)

mysql> update student set name = '王五' where id = 4;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from student;
+----+--------+
| id | name   |
+----+--------+
|  1 | 張三   |
|  2 | 李四   |
|  3 | wangwu |
|  4 | 王五   |
+----+--------+
4 rows in set (0.00 sec)
事務成功完成

2.應用程式要求停止事務。比如應用程式在捕獲到異常時會回滾事務

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into student(name) value('joke');
Query OK, 1 row affected (0.02 sec)

mysql> select * from student;
+----+--------+
| id | name   |
+----+--------+
|  1 | 張三   |
|  2 | 李四   |
|  3 | wangwu |
|  4 | 王五   |
|  5 | joke   |
+----+--------+
5 rows in set (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.07 sec)

mysql> select * from student;
+----+--------+
| id | name   |
+----+--------+
|  1 | 張三   |
|  2 | 李四   |
|  3 | wangwu |
|  4 | 王五   |
+----+--------+
4 rows in set (0.00 sec)
應用程式要求停止服務

3.外界因素強制終止事務,如連線超時或連線斷開

mysql> begin;
Query OK, 0 rows affected (0.07 sec)

mysql> insert into student(name) value('田七');
Query OK, 1 row affected (0.01 sec)

mysql> # 此時,我將MySQL的服務停止掉了,去執行刪除操作
mysql> delete from student where id=7;
ERROR 2013 (HY000): Lost connection to MySQL server during query

msyql> # 此時,我將MySQL的服務重新啟動,去執行刪除操作
mysql> delete from student where id=7;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    3
Current database: mytest

Query OK, 0 rows affected (0.03 sec)

mysql> select * from student;
+----+--------+
| id | name   |
+----+--------+
|  1 | 張三   |
|  2 | 李四   |
|  3 | wangwu |
|  4 | 王五   |
|  6 | 趙六   |
+----+--------+
5 rows in set (0.00 sec)
外界因素強制終止事務

4.帶有儲存節點的扁平事務

a.帶有儲存節點的扁平事務允許事務在執行過程中回滾到較早的一個狀態,而不是回滾所有的操作。

b.儲存點用來通知系統應該記住事務當前的狀態,以便當之後發生錯誤時,事務能回到儲存點當時的狀態。

c.對於扁平事務來說,在事務開始時隱式地設定了一個儲存點,回滾時只能回滾到事務開始時的狀態。

#帶有儲存節點的扁平事務
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from student;
+----+------+-----+
| id | name | age |
+----+------+-----+
|  1 | 張三 |  18 |
|  2 | 李四 |  19 |
+----+------+-----+
2 rows in set (0.00 sec)

mysql> update student set age=28 where id=1;
Query OK, 1 row affected (0.04 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> savepoint sp1;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into student(name,age) value ('王五',20);
Query OK, 1 row affected (0.00 sec)

mysql> rollback to sp1;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from student;
+----+------+-----+
| id | name | age |
+----+------+-----+
|  1 | 張三 |  28 |
|  2 | 李四 |  19 |
+----+------+-----+
2 rows in set (0.00 sec)
帶有儲存節點的扁平事務

3.2、鏈事務

什麼是鏈事務:

a.鏈事務(Chained Transaction)是指一個事務由多個子事務鏈式組成

b.前一個子事務的提交操作和下一個子事務的開始操作合併成一個原子操作,這意味著下一個事務將看到上一個事務的結果,就好像在一個事務中進行的一樣。

c.在提交子事務時就可以釋放不需要的資料物件,而不必等到整個事務完成後才釋放。

鏈事務工作示意圖:

 

鏈事務與扁平事務的區別:

a.鏈事務中的回滾僅限於當前事務,相當於只能恢復到最近的一個儲存節點

b.帶儲存節點的扁平事務能回滾到任意正確的儲存點

c.帶有儲存節點的扁平事務中的儲存點是易失的,當發生系統崩潰是,所有的儲存點都將消失,這意味著當進行恢復時,事務需要從開始處重新執行。

3.3、巢狀事務

什麼是巢狀事務?

a.巢狀事務(Nested Transaction)是一個層次結構框架。

b.由一個頂層事務(top-level transaction)控制著各個層次的事務。

c.頂層事務之下巢狀的事務成為子事務(subtransaction),其控制著每一個區域性的操作,子事務本身也可以是巢狀事務.

d.巢狀事務的層次結構可以看成是一顆樹.

巢狀事務結構如下圖所示:

4.事務的隔離級別

 SQL標準定義的四個隔離級別:

a.READ UNCOMMITTED: 未提交讀,作用域為(global)全域性 、(session)當前會話

 未提交讀

b.READ COMMITTED: 提交讀

 提交讀

c.REPEATABLE READ: 可重複讀

 可重複讀

d.SERIALIZABLE: 可序列讀

 可序列讀

4.1、Read uncommitted 

讀取未提交內容。在該隔離級別下,所有事務都可以看到其它未提交事務的執行結果。

a.事務2查詢到的資料是事務1中修改但未提交的資料

b.但因為事務1回滾了資料,所以事務2查詢的資料是不正確的

c.因此出現了髒讀的問題

4.2、Read committed

1.讀取提交內容。在該隔離級別下,一個事務從開始到提交之前對資料所做的改變對其它事務是不可見的,這樣就解決在READ-UNCOMMITTED級別下的髒讀問題。

2.但如果一個事務在執行過程中,其它事務的提交對該事物中的資料發生改變,那麼該事務中的一個查詢語句在兩次執行過程中會返回不一樣的結果

 

a.事務2執行update語句但未提交前,事務1的前兩個select操作返回結果是相同的。

b.但事務2執行commit操作後,事務1的第三個select操作就讀取到事務2對資料的改變,導致與前兩次select操作返回不同的資料

c.因此出現了不可重複讀的問題。

4.3、Repeatable read

1.可重複讀。這是MySQL的預設事務隔離級別,能確保事務在併發讀取資料時會看到同樣的資料行,解決了READ-COMMITTED隔離級別下的不可重複讀問題

2.MySQL的InnoDB儲存引擎通過多版本併發控制(Multi_Version Concurrency Control, MVCC)機制來解決該問題

3.在該機制下,事務每開啟一個例項,都會分配一個版本號給它,如果讀取的資料行正在被其它事務執行DELETE或UPDATE操作(即該行上有排他鎖),這時該事物的讀取操作不會等待行上的鎖釋放,而是根據版本號去讀取行的快照資料(記錄在undo log中)

4.這樣,事務中的查詢操作返回的都是同一版本下的資料,解決了不可重複讀問題

a.雖然該隔離級別下解決了不可重複讀問題,但理論上會導致另一個問題:幻讀(Phantom Read)

b.正如上面所講,一個事務在執行過程中,另一個事物對已有資料行的更改

c.MVCC機制可保障該事物讀取到的原有資料行的內容相同,但並不能阻止另一個事務插入新的資料行,這就會導致該事物中憑空多出資料行,像出現了幻讀一樣,這便是幻讀問題

1)事務2對id=1的行內容進行了修改並且執行了commit操作

2)事務1中的第二個select操作在MVCC機制的作用下返回的仍是v=1的資料

3)事務3執行了insert操作

4) 事務1第三次執行select操作時便返回了id=2的資料行,與前兩次的select操作返回的值不一樣

需要說明的是,REPEATABLE-READ隔離級別下的幻讀問題是SQL標準定義下理論上會導致的問題,MySQL的InnoDB儲存引擎在該隔離級別下,採用了Next-Key Locking鎖機制避免了幻讀問題。Next-Key Locking鎖機制將在後面的鎖章節中講到。

 4.4 Serializable

   1.可序列化。這是事務的最高隔離級別

   2.通過強制事務排序,使之不可能相互衝突,就是在每個讀的資料行加上共享鎖來實現

   3.在該隔離級別下,可以解決前面出現的髒讀、不可重複讀和幻讀問題

   4.但也會導致大量的超時和鎖競爭現象,一般不推薦使用。

 

二、Mysql資料庫中的鎖  

1、MyISAM和InnoDB支援的鎖型別

  1. 相對其他資料庫而言,MySQL的鎖機制比較簡單,其最顯著的特點是不同的儲存引擎支援不同的鎖機制。

  2. MyISAM和MEMORY儲存引擎採用的是表級鎖(table-level locking)。

  3. InnoDB儲存引擎既支援行級鎖(row-level locking),也支援表級鎖,但預設情況下是採用行級鎖。

2、MySQL這3種鎖的特性

  1)行級鎖

   1. 行級鎖分為共享鎖和排它鎖,行級鎖是Mysql中鎖定粒度最細的鎖。

   2. InnoDB引擎支援行級鎖和表級鎖,只有在通過索引條件檢索資料的時候,才使用行級鎖,否就使用表級鎖。

   3. 行級鎖開銷大,加鎖慢,鎖定粒度最小,發生鎖衝突概率最低,併發度最高

   舉例: 只根據主鍵進行查詢,並且查詢到資料,主鍵欄位產生行鎖。

 產生行鎖

  2)表級鎖

   1. 表級鎖分為表共享鎖和表獨佔鎖。

   2. 表級鎖開銷小,加鎖快,鎖定粒度大、發生鎖衝突最高,併發度最低

   舉例:根據非主鍵不含索引(name)進行查詢,並且查詢到資料,name欄位產生表鎖。

 產生表鎖

  3)頁級鎖

   1. 頁級鎖是MySQL中鎖定粒度介於行級鎖和表級鎖中間的一種鎖。

   2. 表級鎖速度快,但衝突多,行級衝突少,但速度慢。

   3. 所以取了折衷的頁級,一次鎖定相鄰的一組記錄,BDB支援頁級鎖。

   4. 開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度一般。

   總結:

    1. 表級鎖更適合於以查詢為主,只有少量按索引條件更新資料的應用,如Web應用;

    2. 而行級鎖則更適合於有大量按索引條件併發更新少量不同資料,同時又有併發查詢的應用,如一些線上事務處理(OLTP)系統。

3、鎖分類

  1. 按操作劃分:DML鎖,DDL鎖

  2. 按鎖的粒度劃分:表級鎖、行級鎖、頁級鎖

  3. 按鎖級別劃分:共享鎖、排他鎖

  4. 按加鎖方式劃分:自動鎖、顯示鎖

  5. 按使用方式劃分:樂觀鎖、悲觀鎖

4、樂觀鎖悲觀鎖作用

  1. 在併發訪問情況下,很有可能出現不可重複讀等等讀現象。

  2. 為了更好的應對高併發,封鎖、時間戳、樂觀併發控制(樂觀鎖)、悲觀併發控制(悲觀鎖)都是併發控制採用的主要技術方式。
          

5、悲觀鎖

  1. 悲觀鎖的實現,往往依靠資料庫提供的鎖機制

  2. MySQL會對查詢結果集中每行資料都新增排他鎖,其他執行緒對該記錄的更新與刪除操作都會阻塞,排他鎖包含行鎖、表鎖。

  3. 申請前提:沒有執行緒對該結果集中的任何行資料使用排他鎖或共享鎖,否則申請會阻塞。

  適用場景:悲觀鎖適合寫入頻繁的場景。

  注:

   首先我們需要set autocommit=0,即不允許自動提交
   用法:select * from tablename where id = 1 for update;

6、樂觀鎖

  1. 在更新資料的時候需要比較程式中的庫存量與資料庫中的庫存量是否相等,如果相等則進行更新。

  2. 反之程式重新獲取庫存量,再次進行比較,直到兩個庫存量的數值相等才進行資料更新。

7、舉例:對商品數量-1操作

  1)悲觀鎖實現方法

    1. 每次獲取商品時,對該商品加排他鎖。

    2. 也就是在使用者A獲取獲取 id=1 的商品資訊時對該行記錄加鎖,期間其他使用者阻塞等待訪問該記錄。

 悲觀鎖     python使用悲觀鎖

  2)樂觀鎖實現方法

    1. 每次獲取商品時,不對該商品加鎖。

    2. 在更新資料的時候需要比較程式中的庫存量與資料庫中的庫存量是否相等,如果相等則進行更新

    3. 反之程式重新獲取庫存量,再次進行比較,直到兩個庫存量的數值相等才進行資料更新。

 樂觀鎖  python使用樂觀鎖 

8、python適用樂觀鎖解決事物問題

   使用 django.db.transaction 模組解決MySQL 事物管理 問題   

   1. 在事務當前啟動celery非同步任務, 無法獲取未提交的改動.

   2. 在使用transaction當中, Model.save()都不做commit .

   3. 因此如果在transaction當中設定非同步任務,使用get()查詢資料庫,將看不到物件在事務當中的改變.

   4. 這也是實現”可重複讀”的事務隔離級別,即同一個事務裡面的多次查詢都應該保持結果不變.

 使用transaction模組解決mysql事物問題  transaction使用例項

9、MySQL中 共享鎖 和 排它鎖 

  1)排它鎖

  1. 排它鎖又叫寫鎖,如果事務T對A加上排它鎖,則其它事務都不能對A加任何型別的鎖。獲准排它鎖的事務既能讀資料,又能寫資料。

  2. 用法 :  SELECT … FOR UPDATE

  2)共享鎖(share lock)

    1. 共享鎖又叫讀鎖,如果事務T對A加上共享鎖,則其它事務只能對A再加共享鎖,不能加其它鎖。

    2. 獲准共享鎖的事務只能讀資料,不能寫資料。

    3. 用法: SELECT … LOCK IN SHARE MODE;