事務控制和鎖定語句

1、LOCK TABLE 和 UNLOCK TABLEmysql

lock tables 鎖定當前線性表。若是表被其餘線程鎖定,則當前線程會等待,直到能夠獲取全部鎖定爲止。sql

unlock tables 釋放當前線程得到的任何鎖定。當前線程執行另外一個 LOCK TABLES 時, 或當與服務器的鏈接被關閉時,全部由當前線程鎖定的表被隱含地解鎖 數據庫

 

一個得到表鎖和釋放表鎖的簡單例子服務器

session_1 session_2

得到表film_text的Read鎖定session

lock table film_text read分佈式

 

當前session能夠查詢該表紀錄spa

select id,title from film_text where id = 1;線程

+-------+-----------orm

   id.        title事務

+--------+----------

    1          標籤

其餘session也能夠查詢該表的紀錄

select id,title from film_text where id = 1;

+-------+-----------

   id.        title

+--------+----------

    1          標籤

 

 

 其餘session更新鎖定表會等待得到鎖

update film_text set title='lalamei' where id=1;

......等待

釋放鎖:unlock tables

......等待

 

session 得到鎖,更新操做完成

完成

 

 

2、事務控制

默認狀況下,MySQL 是自動提交(Autocommit)的,若是須要經過明確的 Commit 和 Rollback 來提交和回滾事務,那麼須要經過明確的事務控制命令來開始事務 。

* start transaction 或 begin語句能夠開始一項新的事務

* commit 和 rollback 用來提交或者回滾事務

* chain 和 release 字句分別用來定義在事務提交或者回滾以後的操做,chain會當即啓動一個新事物,而且和剛纔的事物具備相同的隔離級別,release則會斷開和客戶端鏈接

* set autocommit 能夠修改當前鏈接的提交方式,若是設置了set autocommit=0,則設置以後的全部事務都須要經過明確的命令進行提交或者回滾

 

演示使用start transaction 開始的事務在提交後自動回到自動提交的方式;若是在提交的時候使用commit and chain,那麼會在提交後當即開啓一個新的事務。

start transaction和commit and chain 的使用例子

 

 

session_1 session_2

從表actor中查詢actor_id=201的記錄,結果爲空:

mysql> select * from actor where actor_id = 201;

Empty set (0.00 sec)

 

從表actor中查詢actor_id=201的記錄,結果爲空:

mysql> select * from actor where actor_id = 201;

Empty set (0.00 sec)

用start transaction 命令啓動一個事務,往表actor中插入一條紀錄,沒有commit:

mysql> start transaction;

Query OK, 0 rows affected (0.00 sec)

 

mysql> insert into actor(actor_id,first_name,last_name) values(201,'lisa','tom');

Query OK, 1 row affected (0.00 sec)

查詢表actor,結果仍然爲空:

mysql> select * from actor where actor_id=201;

Empty set (0.00 sec)

 

 執行提交:

mysql> commit;

Query OK, 0 rows affected (0.00 sec)

 
 

 再次查詢表actor,能夠查詢到結果:

mysql> select * from actor where actor_id=201;

+----------+------------+-----------+

| actor_id | first_name | last_name |

+----------+------------+-----------+

|      201 | lisa       | tom       |

+----------+------------+-----------+

1 row in set (0.00 sec)

 這個事務是按照自動提交執行的:

mysql> insert into actor(actor_id,first_name,last_name) values(202,'lisa','lan');

Query OK, 1 row affected (0.01 sec)

 
 

 能夠從actor表中查詢到session1剛剛插入的數據。

mysql> select * from actor where actor_id in (201,202);

+----------+------------+-----------+

| actor_id | first_name | last_name |

+----------+------------+-----------+

|      201    | lisa           | tom       |

|      202    | lisa           | lan        |

+----------+------------+-----------+

2 rows in set (0.00 sec)

 從新用start transaction 啓動一個事務:

mysql> start transaction;

Query OK, 0 rows affected (0.00 sec)

 

往表actor中插入一條紀錄:

mysql> insert into actor(actor_id,first_name,last_name) values(203,'lisa','tt');

Query OK, 1 row affected (0.00 sec)

 

用commit and chain命令提交:

mysql> commit and chain;

Query OK, 0 rows affected (0.00 sec)

 

此時自動開始一個新的事務:

mysql> insert into actor (actor_id,first_name,last_name) values(204,'Lisa','Mou');

Query OK, 1 row affected (0.00 sec) 

 
 

 session1剛插入的紀錄沒法查看:

mysql> select * from actor where first_name = 'lisa';

+----------+------------+-----------+

| actor_id | first_name | last_name |

+----------+------------+-----------+

|      201   | lisa           | tom       |

|      202   | lisa           | lan         |

|      203   | lisa           | tt           |

+----------+------------+-----------+

3 rows in set (0.00 sec)

 用commit命令提交:

mysql> commit;

Query OK, 0 rows affected (0.01 sec)

 
 

 session1插入的新記錄能夠看到:

mysql> select * from actor where first_name = 'lisa';

+----------+------------+-----------+

| actor_id | first_name | last_name |

+----------+------------+-----------+

|      201    | lisa          | tom       |

|      202    | lisa          | lan        |

|      203    | lisa          | tt          |

|      204    | lisa         | mou       |

+----------+------------+-----------+

4 rows in set (0.00 sec)

 

 

若是在鎖表期間,用 start sransaction 命令開始一個新事務,會形成一個隱含的unlock tables 被執行

start transaction 致使的 unlock tables

 

 

session_1 session_2

從表actor中查詢actor_id=301的記錄,結果爲空:

mysql> select * from actor where actor_id = 301;

Empty set (0.00 sec)

 

從表actor中查詢actor_id=301的記錄,結果爲空:

mysql> select * from actor where actor_id = 301;

Empty set (0.00 sec)

 對錶actor加寫鎖:

mysql> lock table actor write;

Query OK, 0 rows affected (0.00 sec)

 
 

對錶 actor 的讀寫操做被阻塞:

mysql> select * from actor where actor_id=301;

....等待 

 插入一條記錄

mysql> insert into actor(actor_id,first_name,last_name) values(301,'Lisa','Tom');

Query OK, 1 row affected (0.00 sec)

 ....等待 

 回滾剛纔的紀錄:

mysql> rollback;

Query OK, 0 rows affected (0.00 sec)

 ....等待 

 用start transaction 命令從新開始一個事務:

mysql> start transaction;

Query OK, 0 rows affected (0.00 sec)

 ....等待  
 

session1 開始一個事務時,表鎖被釋放,能夠查詢:

mysql> select * from actor where actor_id=301;

+----------+------------+-----------+

| actor_id | first_name | last_name |

+----------+------------+-----------+

|      301   | Lisa          | Tom         |

+----------+------------+-----------+

1 row in set (14 min 58.07 sec)

對lock方式加的表鎖,不能經過rollback進行回滾。

 

 

/*

* 一、commit、rollback只能對事務類型的表進行提交和回滾

* 二、一般對提交的事務紀錄到二進制文件中

* 三、全部的DDL語句是不能回滾的,部分DDL語句會形成隱式提交

*/

在事務中能夠經過定義 SAVEPOINT,指定回滾事務的一個部分,可是不能指定提交事務 的一個部分。對於複雜的應用,能夠定義多個不一樣的 SAVEPOINT,知足不一樣的條件時,回滾 不一樣的 SAVEPOINT。須要注意的是,若是定義了相同名字的 SAVEPOINT,則後面定義的 SAVEPOINT 會覆蓋以前的定義。對於再也不須要使用的 SAVEPOINT,能夠經過 RELEASE SAVEPOINT 命令刪除 SAVEPOINT,刪除後的 SAVEPOINT,不能再執行 ROLLBACK TO SAVEPOINT 命令。 

 

模擬回滾事務

 

session_1 session_2

從表actor中查詢 first_name='Simon' 的紀錄,

結果爲空:

mysql> select * from actor where first_name = 'Simon';

Empty set (0.00 sec)

 

從表actor中查詢 first_name='Simon' 的紀錄,

結果爲空:

mysql> select * from actor where first_name = 'Simon';

Empty set (0.00 sec)

啓動一個事務,往表 actor 中插入一條紀錄;

mysql> start transaction;

Query OK, 0 rows affected (0.00 sec)

 

mysql> insert into actor(actor_id,first_name,last_name) values(501,'Simon','Tom');

Query OK, 1 row affected (0.00 sec)

 

能夠查詢到剛剛插入的紀錄:

mysql> select * from actor where first_name = 'Simon';

+----------+------------+-----------+

| actor_id | first_name | last_name |

+----------+------------+-----------+

|      501   | Simon       | Tom       |

+----------+------------+-----------+

1 row in set (0.00 sec)

 

沒法從actor表中查詢到session1剛插入的紀錄:

mysql> select * from actor where first_name = 'Simon';

Empty set (0.00 sec)

 

 定義 savepoint,名稱爲test;

mysql> savepoint test;

Query OK, 0 rows affected (0.00 sec)

 

繼續插入一條紀錄:

mysql> insert into actor(actor_id,first_name,last_name) values(502,'Simon','Cof');

Query OK, 1 row affected (0.00 sec)

 

能夠查詢到兩條紀錄

mysql> select * from actor where first_name = 'Simon';

+----------+------------+-----------+

| actor_id | first_name | last_name |

+----------+------------+-----------+

|      501   | Simon      | Tom        |

|      502   | Simon      | Cof         |

+----------+------------+-----------+

2 rows in set (0.00 sec)

仍然沒法查詢到結果:

mysql> select * from actor where first_name = 'Simon';

Empty set (0.00 sec)

 

回滾到剛纔定義的savepoint:

mysql> rollback to savepoint test;

Query OK, 0 rows affected (0.00 sec)

 

只能從表 actor 中查詢到第一條記錄,由於第二條記錄已經回滾:

mysql> select * from actor where first_name = 'Simon';

+----------+------------+-----------+

| actor_id | first_name | last_name |

+----------+------------+-----------+

|      501   | Simon       | Tom       |

+----------+------------+-----------+

1 row in set (0.00 sec)

仍然沒法查詢到結果:

mysql> select * from actor where first_name = 'Simon';

Empty set (0.00 sec)

 

用commit命令提交:

mysql> commit;

Query OK, 0 rows affected (0.00 sec)

 
 

只能從表 actor 中查詢到第一條記錄:

mysql> select * from actor where first_name = 'Simon';

+----------+------------+-----------+

| actor_id | first_name | last_name |

+----------+------------+-----------+

|      501   | Simon       | Tom       |

+----------+------------+-----------+

1 row in set (0.00 sec)

只能從表 actor 中查詢到 session1 插入的第一條記錄:

mysql> select * from actor where first_name = 'Simon';

+----------+------------+-----------+

| actor_id | first_name | last_name |

+----------+------------+-----------+

|      501   | Simon       | Tom       |

+----------+------------+-----------+

1 row in set (0.00 sec)

 

 

 

3、分佈式事務的使用

簡介

一、分佈式事務只支持innoDB存儲引擎

二、一個分佈式事務會涉及多個行動,這些行動自己是事務性的。全部行動都必須一塊兒成功完成,或者一塊兒被回滾

 

分佈式事務的原理

 

分佈式事務語法

XA {START|BEGIN} xid [JOIN|RESUMF]

XA START xid 用於啓動一個帶給xid值的XA事務。每一個XA事務必須有一個惟一的xid值,所以該值當前不能被其餘的XA事務使用。

xid是一個XA事務標識符,用來惟一標識一個分佈式事務。xid值由客服端提供,或由mysql服務器生成。

xid值包含1~3個部分

xid : gtrid [, bqual [, formatId ] ]

* gtrid 是一個分佈式事務標識符,相同的分佈式事務應該使用相同的gtrid,這樣能夠明確知道xa事務屬於那個分佈式事務。

* bqual 是一個分支限定符,默認值是空串。對於一個分佈式事務中的每一個分支事務,bqual值必須是惟一的。

* formatID 是一個數字,用於標識由gtrid 和 bqual 值使用的格式,默認值是1。

 

下面其餘 XA 語法中用到的 xid 值,都必須和 START 操做使用的 xid 值相同,也就是表示 對這個啓動的 XA 事務進行操做。

XA END xid [ SUSPEND  [ FOR MIGRATE ] ]

XA PREPARE xid 

使事務進入 PREPARE 狀態,也就是兩階段提交的第一個提交階段 

 

XA COMMIT xid [ ONE PHASE ]
XA ROLLBACK xid 

這兩個命令用來提交或者回滾具體的分支事務。也就是兩階段提交的第二個提交階段, 分支事務被實際的提交或者回滾。 

 

XA RECOVER 

XA RECOVER返回當前數據庫中處於PREPARE 狀態的分支事務的詳細信息。 

 

分佈式的關鍵在於?

如何確保分佈式事務的完整性,

在某個分支出現問題時的故障解決。

 

演示了一個簡單的分佈式事務的執行,事務的內容是在 DB1 中插入一條記錄, 同時在 DB2 中更新一條記錄,兩個操做做爲同一事務提交或者回滾。 

分佈式事務例子

 

session_1 in DB1 session_2 in DB2

在數據庫 DB1 中啓動一個分佈式事務的一個分支事務,xid 的 gtrid 爲test,bqual 爲 db1:

mysql> xa start 'test','db1';

Query OK, 0 rows affected (0.00 sec)

 

分支事務1在表 actor 中插入一條紀錄:

mysql> insert into actor(actor_id,first_name,last_name) values(601,'Simon','Tom');

Query OK, 1 row affected (0.00 sec)

 

對分支事務1進行第一階段提交,進入 prepare 狀態:

mysql> xa end 'test','db1';

Query OK, 0 rows affected (0.00 sec)

 

mysql> xa prepare 'test','db1';

Query OK, 0 rows affected (0.00 sec)

在數據庫DB2中啓動分佈式事務test的另外一個分支事務,xid 的 gtrid 爲 test, bqual 爲 db2: 

mysql> xa start 'test','db2';

Query OK, 0 rows affected (0.00 sec)

 

分支事務 2 在表 film_actor 中更新了21條記錄:

mysql> update film_actor set last_update=now() where actor_id=178;

Query OK, 21 rows affected (0.00 sec)

Rows matched: 21  Changed: 21  Warnings: 0

 

對分支事務 2 進行第一階段提交,進入 prepare 狀態:

mysql> xa end 'test','db2';

Query OK, 0 rows affected (0.00 sec)

 

mysql> xa prepare 'test','db2';

Query OK, 0 rows affected (0.01 sec)

用 xa recover 命令查看當前分支事務狀態:

mysql> xa recover \G;

***************************

***************************

    formatID: 1

gtrid_length: 4

bqual_length: 3

        data: testdb1

1 rows in set (0.00 sec)

用 xa recover 命令查看當前分支事務狀態:

mysql> xa recover \G;

***************************

***************************

    formatID: 1

gtrid_length: 4

bqual_length: 3

        data: testdb2

 1 rows in set (0.00 sec)

                                      兩個事務都進入準備提交階段,若是以前遇到任何錯誤,都應該回滾全部的分支,以確保分佈事務的正確。

 提交分支事務 1:

mysql> xa commit 'test','db1';

Query OK, 0 rows affected (0.00 sec)

 提交分支事務2:

mysql> xa commit 'test','db2';

Query OK, 0 rows affected (0.00 sec)

 

 兩個事務都到達準備提交階段後,一旦開始進行提交操做,就須要確保所有的分支都提交成功。  

 

 

存在的問題1

條件:

分支事務在達到prepare狀態時,數據庫異常從新啓動,重啓之後能夠繼續執行事務進行提交回滾的操做。

問題:

提交事務沒有寫binlog,存在隱患,可能致使使用binlog恢復丟失部分數據。若是存在複製的數據庫,則有可能致使主從數據庫的數據不一致。

 

存在問題二

若是分支事務的客戶端鏈接異常停止,那麼數據庫會自動回滾未完成的分支事務,若是 此時分支事務已經執行到 prepare 狀態,那麼這個分佈式事務的其餘分支可能已經成功提交, 若是這個分支回滾,可能致使分佈式事務的不完整,丟失部分分支事務的內容。 

相關文章
相關標籤/搜索