Mysql數據庫事務及隔離級別學習測試


參考了這篇文章的一些內容:

http://xm-king.iteye.com/blog/770721

 

記住以下這張表:

 

我在springdemo庫里面建了一個表:

CREATE TABLE `tx` (
`id` bigint(11) NOT NULL auto_increment,
`num` bigint(11) default 0 COMMENT '用戶名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='事務隔離級別測試表';

 

Mysql通過以下語句可以查詢

SELECT @@tx_isolation;
SELECT @@global.tx_isolation; 
SELECT @@session.tx_isolation; 

默認的是 REPEATABLE-READ

可以通過以下方式修改隔離級別:

set tx_isolation='read-committed';

 

mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

mysql> set tx_isolation='read-committed';
Query OK, 0 rows affected (0.00 sec)

mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set (0.00 sec)

 

但是,注意以上的改動,只能針對當前會話。

另起一個客戶端,輸入 

select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+

 

————————————————————————————————

以下是各種情況的測試:

READ-UNCOMMITTED

update tx set num=11 where id=1;
ERROR 1665 (HY000): Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging. InnoDB is limited to row-logging when transaction isolation level is READ COMMITTED or READ UNCOMMITTED.

 

READ-COMMITTED

update tx set num=11 where id=1;
ERROR 1665 (HY000): Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging. InnoDB is limited to row-logging when transaction isolation level is READ COMMITTED or READ UNCOMMITTED.

 

REPEATABLE-READ

A:

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

mysql> select * from tx;  (B更新前)
+----+------+
| id | num  |
+----+------+
|  1 |    1 |
|  2 |    2 |
|  3 |    3 |
+----+------+
3 rows in set (0.00 sec)

mysql> select * from tx; (B更新后)
+----+------+
| id | num  |
+----+------+
|  1 |    1 |
|  2 |    2 |
|  3 |    3 |
+----+------+
3 rows in set (0.00 sec)

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

mysql> select * from tx;
+----+------+
| id | num |
+----+------+
| 1 | 10 |
| 2 | 2 |
| 3 | 3 |
+----+------+

可以看出,A的事務中,看不到B對數據的更新。

 

同樣是 REPEATABLE-READ

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

mysql> select * from tx;
+----+------+
| id | num  |
+----+------+
|  1 |   10 |
|  2 |    2 |
|  3 |    3 |
+----+------+
3 rows in set (0.00 sec)

mysql> select * from tx;
+----+------+
| id | num  |
+----+------+
|  1 |   10 |
|  2 |    2 |
|  3 |    3 |
+----+------+
3 rows in set (0.00 sec)

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

mysql> select * from tx;
+----+------+
| id | num |
+----+------+
| 1 | 10 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
+----+------+
4 rows in set (0.00 sec)

另一個客戶端,insert 了一條記錄.

 

對於正常的repeatable-read級別,是有可能出現幻讀的情況,也就是說,第二遍的時候,A能夠讀到新插入的數據。

但是,InnoDB和Falcon存儲引擎通過多版本並發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題。

所以我上面用InnoDB引擎創建的數據庫,就沒有出現幻讀的情況。

 

SERIALIZABLE 

這里注意,只需要對使用事務Transaction的客戶端設置SERIALIZABLE ,其他客戶端的級別是什么都行,比如REPEATABLE-READ .

A

mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+
1 row in set (0.00 sec)

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

mysql> select * from tx;
+----+------+
| id | num  |
+----+------+
|  1 |   10 |
|  2 |    2 |
|  3 |    3 |
|  4 |    4 |
|  5 |    5 |
+----+------+
5 rows in set (0.00 sec)

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

 

B

mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

mysql> insert into tx values(5,5);
Query OK, 1 row affected (0.09 sec)

mysql> insert into tx values(6,6);

Query OK, 1 row affected (31.60 sec)

 

這里,有2點需要注意:

1. A沒有使用(select)表tx的時候,B仍然能夠向tx表中插入數據;

2. A使用(select)表tx之后,B再進行insert操作,就會hang住,直到A表transaction結束。所以可以看到B的insert操作耗時31秒。當然了,也可能超時失敗。

比如:

mysql> insert into tx values(10,10);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

 

以上這條語句,看起來是鎖了整個表。但是其實深層次的目的,是為了保證事務的完整性,以及讓A兩次操作的REPEATABLE_READ;

比如 使用  select * from tx limit 1; 后面就仍然可以insert。因為不改變之前select的結果。

如果 select * from tx limit 10; 而最終只檢出8條。這時候就不能insert,因為insert了的話,下次同樣select得到的結果就不一樣。

 

同理,如果select了某條記錄,那么update同一條記錄就不行,update其他的記錄就可以。而且在事務中能夠讀到新更新的數據。

 

所以記住,事務的隔離級別的要求,只對事務過程中已經獲取過的數據有關。跟沒獲取過、其他不可見的數據,無關。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM