1. 程式人生 > 實用技巧 >docker 搭建Mycat環境實現Mysql資料庫的讀寫分離

docker 搭建Mycat環境實現Mysql資料庫的讀寫分離

MySQL的事務

本文為《MySQL技術內幕: InnoDB儲存引擎》的閱讀筆記。

前言

事務就是一組原子性的SQL查詢,或者說一個獨立的工作單元。
事務內的語句,要麼全部執行成功,要麼全部執行失敗。

    一個執行良好的事務處理系統,具備以下特徵。

  • 原子性 (atomicity)
  • 一致性 (consistency)
  • 隔離性 (isolation)
  • 永續性 (durability)

    其中,ISOANIS SQL 標準制定了四種事務隔離級別的標準,但是很少有資料庫廠商遵循這些標準。比如 Oracle 資料庫就不支援 READ_UNCOMMITTEDREPEATABLE_READ

    事務的實現


    redoundo 的作用都可以視為一種恢復操作,redo 恢復提交事務修改的操作,而 undo 回滾行記錄到某個特定的版本。redo 通常是物理日誌,記錄的是頁的物理修改操作,undo 是邏輯日誌,根據每行記錄進行記錄。

redo

    重做日誌有兩部分組成:一是記憶體中的重做日誌緩衝 (redo log buffer),其是易失的;二是重做日誌檔案 (redo log file),其是持久的。

    InnoDB 是事務的儲存引擎,當事務提交時,必須先將該事務的所有日誌寫入到重做日誌檔案進行持久化,待事務的提交操作完成才算完成。在 InnoDB

中,redo log 用來保證事務的永續性。

undo

    undo log 用來幫助事務回滾以及 MVCC 的功能。redo 存放在重做日誌檔案中,與 redo 不同,undo 存放在資料庫內部的一個特殊段(segment)中,這個段稱為 undo 段。undo 段位於共享表空間內。

    undo 是邏輯日誌,因此只是將資料庫邏輯地恢復到原來的樣子。當 InnoDB 儲存引擎回滾時,它實際做的是與先前相反的工作。對於每個 INSERTInnoDB 儲存引擎會完成一個 DELETE;對於每個 DELETEInnoDB 儲存引擎會執行一個 INSERT

    在SQL標準中定義了四種隔離級別

。較低級別的隔離通常可以執行更高的併發,系統的開銷也更低。

   undo 的另一個作用是 MVCC,即在 InnoDB 儲存引擎中的 MVCC 的實現是通過 undo 來完成。當用戶讀取一行記錄時,若該記錄已經被其他事務佔用,當前事務可以通過 undo 讀取之前的版本資訊,以此實現非鎖定讀取。

   undo log 會產生 redo log

    四種隔離級別分別是:

  1. 未提交讀 (READ_UNCOMMITTED)
  2. 提交讀 (READ_COMMITTED)
  3. 可重複讀 (REPEATABLE_READ)
  4. 可序列化 (SERIALIZABLE)

未提交讀

    READ_UNCOMMITTED 級別,事務中的修改,即使沒有提交,對其他事務也都是可見的。事務可以讀取未提交的資料,也稱為髒讀。從效能上來說,READ_UNCOMMITTED 不會比其他級別好太多,但卻缺乏其他級別的很多好處,一般很少使用。

髒讀

    將兩個會話的隔離級別都修改為未提交讀。(我試了將一個會話設定為未提交讀,另一個隔離級別預設,但是沒有效果

## 初始資料
mysql> SELECT * FROM t_bank;
+----+-------+---------+
| id | name  | balance |
+----+-------+---------+
|  1 | Tom   | 1600.00 |
|  2 | Cindy | 100.00  |
|  3 | Jerry | 100.00  |
+----+-------+---------+
3 rows in set (0.02 sec)

    會話執行例項:下表第5行,兩個事務中,會話B可以讀取到會話A的修改結果。

Time 會話A 會話B
1 SELECT @@tx_isolation; SELECT @@tx_isolation;
2 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
3 START TRANSACTION; START TRANSACTION;
4 UPDATE t_bank SET balance = balance - 100 WHERE id = 1;
5 SELECT * FROM t_bank;
6 ROLLBACK; ROLLBACK;

提交讀

    大多數資料庫系統預設隔離級別都是 READ_COMMITTED (但 MySQL 不是)。一個事務從開始直到提交之前,所做的任何修改對其他事務都是不可見的。這個級別有時候也叫做不可重複讀, 因為兩次執行同樣的查詢,可能會得到不一樣的結果。

復現未提交讀

    會話執行例項:下表第7行,兩個事務中,會話A讀取不到會話B的修改結果。可是一旦會話B提交了事務,會話A就可以讀取到會話B修改的結果了。

Time 會話A 會話B
1 SELECT @@tx_isolation; SELECT @@tx_isolation;
2 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
3 START TRANSACTION; START TRANSACTION;
4 SELECT * FROM t_bank;
5 UPDATE t_bank SET balance = balance - 100 WHERE id = 1;
6 SELECT * FROM t_bank; // 1700
7 SELECT * FROM t_bank; // 1800
8 COMMIT;
9 SELECT * FROM t_bank; // 1700
8 ROLLBACK; ROLLBACK;

可重複讀

    REPEATABLE_READ 解決了髒讀的問題。該級別保證了在同一事務中多次讀取同樣記錄的結果是一致的。但是沒有解決幻讀問題。所謂幻讀,指的是當某個事務在讀取某個範圍內的記錄時,會產生幻行InnoDBXtraDB 儲存引擎通過多版本併發控制解決了幻讀問題。

    可重複讀MySQL預設事務隔離級別

驗證 REPEATABLE_READ 解決了可重複讀

    會話執行例項:下表第6行,說明解決了髒讀。下表第8行,說明解決了不可重複讀。注意第9行,是在會話B提交的結果上修改的。

Time 會話A 會話B
1 SELECT @@tx_isolation; SELECT @@tx_isolation;
2 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
3 START TRANSACTION; START TRANSACTION;
4 SELECT * FROM t_bank; SELECT * FROM t_bank;
5 UPDATE t_bank SET balance = balance + 100 WHERE id = 1;
6 SELECT * FROM t_bank; // 1600 SELECT * FROM t_bank; // 1700
7 COMMIT;
8 SELECT * FROM t_bank; // 1600 SELECT * FROM t_bank; // 1700
9 UPDATE t_bank SET balance = balance + 100 WHERE id = 1;
10 SELECT * FROM t_bank; // 1800 SELECT * FROM t_bank; // 1700
11 COMMIT;
12 SELECT * FROM t_bank; // 1800

    關於幻讀的操作,在一次事務裡面,多次查詢之後,結果集的個數不一致的情況叫做幻讀,而多出來或者少出來的那一行叫做幻行。

    在快照讀的情況下,MySQL 通過 MVCC 來避免幻讀。

    在當前讀的情況下,MySQL 通過 next-key 來避免幻讀。

mysql> select * from t_bank;
+----+-------+---------+
| id | name  | balance |
+----+-------+---------+
|  1 | Tom   | 2100.00 |
|  2 | Cindy | 100.00  |
|  3 | Jerry | 100.00  |
+----+-------+---------+
3 rows in set (0.03 sec)
Time 會話A 會話B
1 SELECT @@tx_isolation; SELECT @@tx_isolation;
2 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
3 START TRANSACTION;
4 # 使用當前讀(加共享鎖)
SELECT * FROM t_bank LOCK IN SHARE MODE;
5 START TRANSACTION;
6 INSERT INTO t_bank VALUES(4, "new_man", 500); // 執行此條命令阻塞
7 ROLLBACK; 強行停止

InnoDB可重複讀隔離級別下如何避免幻讀

RR級別下

  • 表象:快照讀(非阻塞讀) -- 偽MVCC
  • 內在:next-key鎖 (行鎖+gap鎖)

什麼是當前讀
加了鎖(共享鎖和排他鎖)的增刪改查語句

  • 當前讀:select …… lock in share modeselect……for update
  • 當前讀:updatedeleteinsert

什麼是快照讀

  • 快照讀:不加鎖的非阻塞讀,select [非 序列化隔離級別下 ]

RCRR 級別下的 InnoDB 的非阻塞讀如何實現

    資料行裡的DB_TRX_IDDB_ROLL_PTRDB_ROW_ID欄位。