1. 程式人生 > >再談事務

再談事務

lock 1.3 eve cti 會有 chang ssi strong eap

上一篇咱們簡單了解了事務,這一篇我們再深入一下吧

Ⅰ、事務隔離級別

事務一共有四種隔離級別

簡稱 全稱 -
ru read-uncommited 未提交讀
rc read-commited 提交讀
rr repeatable-read 可重復讀
srz serializable 可串行

從真正意義上來看只有srz達到真正隔離性的要求

oracel、sqlserver默認rc,mysql默認rr(99.99%達到隔離性要求)

事務隔離級別越低,事務請求的鎖越少或者保持鎖的時間就越短

1.1 四種隔離級別解讀

事務隔離級別解決了三個問題,臟讀,不可重復讀,幻讀

①read-uncommitted

可以讀到其他線程未提交的數據,沒人用,這就是臟讀

②read-committed

解決了臟讀,不會讀到其他線程未提交的數據

但是a線程事務開始沒提交,b線程讀不到對應的數據,a線程事務提交後,b讀到了

這就是不可重復讀,兩次讀到不一樣,破壞了隔離性,一個線程的事務所做的修改被另一個線程可見了

③repeatable-read

解決不可重復讀,一邊提交了,另一邊還是看不到,兩次讀結果一樣

重復讀還可以解決一個問題,幻讀(讀到之前不存在的記錄,講鎖的時候再演示)

例外情況:

  • for update鎖定讀就可以讀到第一個線程中事務提交的數據,可以說是幻讀,也可以說是一個不可重復讀,這就是99.999%,哈哈

  • 這裏也是一種不符合隔離性的,讀到了之前不存在的記錄,只要是支持mvcc就很難做到完全隔離性

④serializable

兩階段加鎖可串行化保證隔離性:加鎖階段,只加鎖不放鎖,解鎖階段,只放鎖,不解鎖

對於所有的寫,每行上面都有鎖,會有大量的鎖競爭和超時

這樣就失去了MVCC特性(非鎖定一致性讀)

所有操作都要加共享鎖,lock in share mode ,執行selct * from xxx;

其實重寫為select * from xxx lock in share mode; 對這條記錄加共享鎖,

強制事務排序,使得不會互相沖突,這樣就不存在並發問題,都是串行了,讀寫相互阻塞

1.2 好奇心,我們對比下rr和sr

RR隔離級別
sesion1:
mysql> set tx_isolation="REPEATABLE-READ";
Query OK, 0 rows affected (0.00 sec)

mysql> create table t_lock(a int, b int, primary key(a));
Query OK, 0 rows affected (0.11 sec)

mysql> insert into t_lock values(1,1);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t_lock;
+---+------+
| a | b    |
+---+------+
| 1 |    1 |
+---+------+
1 row in set (0.00 sec)

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

mysql> select b from t_lock where a=1;
+------+
| b    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

session2:
mysql> set tx_isolation="REPEATABLE-READ";
Query OK, 0 rows affected (0.00 sec)

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

mysql> update  t_lock set b=2 where a = 1;
Query OK, 1 row affected (0.00 sec) Rows matched: 1  Changed: 1  Warnings: 0

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

session1:
mysql> select b from t_lock where a=1;  -- 再執行一次,得到的結果是1
+------+
| b    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> select b from t_lock where a=1 for update;  -- for update的去讀,得到的結果是2
+------+
| b    |
+------+
|    2 |
+------+ 1 row in set (0.00 sec)
session1中, RR隔離級別下,前兩次讀都是讀取的快照,最後一次讀取的當前更新的值


SR隔離級別
session1:
mysql> select * from t_lock;
+---+------+
| a | b    |
+---+------+
| 1 |    1 |
+---+------+
1 row in set (0.00 sec)

mysql> set tx_isolation=‘SERIALIZABLE‘;
Query OK, 0 rows affected (0.00 sec)

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

mysql> select b from t_lock where a=1;
+------+
| b    |
+------+
|    1 |
+------+ 1
row in set (0.00 sec)

session2:
mysql> show engine innodb status\G
-- ------------省略部分輸出------------
2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 3, OS thread handle 0x7f946bc94700, query id 30 localhost root cleaning up
TABLE LOCK table `burn_test`.`t_lock` trx id 5390 lock mode IS
RECORD LOCKS space id 15 page no 3 n bits 72 index `PRIMARY` of table `burn_test`.`t_lock` trx id 5390 lock mode S locks rec but not gap  -- 有S鎖
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000001; asc     ;;
1: len 6; hex 00000000150c; asc       ;;
2: len 7; hex 8c000000340110; asc     4  ;;
3: len 4; hex 80000001; asc     ;;

mysql> set tx_isolation=‘SERIALIZABLE‘;
Query OK, 0 rows affected (0.00 sec)

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

mysql> update t_lock set b=2 where a=1;  -- 在SR的隔離級別下,直接阻塞,因為a=1上有一個S鎖
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

SERIALIZABLE的隔離級別一般來說是不會去用

1.3 事務隔離級別的選擇

show variables like ‘tx_isolation‘;
set global 後只對之後創建的session生效,之前的session沒用

如何查看當前各個線程的事務隔離級別,sys庫裏面的session表看不了啊?

應該看performance_schema中user_variables_by_thread表

select * from user_variables_by_thread where variable_name = ‘tx_isolation‘;
找到thread_id
select processlist_id from threads where thread_id in(xxx);
再對應到processlist id

重點:

  • 我們生產中一定要用rc,不要用默認的rr,一般場景rc性能會好很多,rc的鎖好理解

  • 為了同步數據一致性binlog_format用row,mixed會遇到各種bug導致主從不一致,5.7官方默認用row了,沒有協商的余地,組復制也必須用row(目前MySQL若啟用rc,不管binlog_format怎麽設置都會強行轉為row)

    transaction_isolation = read-committed
    binlog_format = row
    上面兩個一定要配到my.cnf中,沒有任何理由
  • 有一種情況rr比rc好,條件是,應用偏讀,一個事務裏面有10個select,後面會提到,read_view的原因

再談事務