表a中有4條記錄1,3,5,7,開兩個sessionmysql
session1:begin; delete <= 7 ; session2:begin; insert a values(2); commit; session1:commit;
這個模型在rc的狀況下,這是沒問題的,只是加記錄鎖,不會鎖範圍,插入2是能夠的。最後a上就剩2這個記錄sql
那binlog裏面記錄的內容就有講究了,假設是statment格式的binlog數據庫
insert 2; delete <= 7;
這時候數據庫作同步,從庫上就gg了,一個事務所作的修改對另外一個事務不可見,好似串行session
這就是不符合隔離性的要求,並行執行和串行執行的結果不同併發
row格式的binlog是下面這樣記錄的:ide
insert 2; delete 1,delete 3, delete 5, delete 7
這樣2就還在性能
因此,必定要用row,row記錄的是一條一條記錄,而不是簡單的sql.因此說rc的狀況下設置爲row,主從仍是能夠保持一致的測試
5.1版本才支持row,主從複製從3.23就開始支持,中間差了4.0,4.1,5.0三個版本,最先爲何innodb要支持這樣的鎖,也有部分緣由是當時的MySQL的複製不用Next-key Lock,就是根本不可用的,由於innodb能夠並行的,有併發問題,不像其餘存儲引擎有表鎖優化
tips:code
先講兩句題外話,這個判斷的是根據事務id來作的,每條記錄有rowid、txid、rowpointer,後面才接用戶的列
txid是6個字節的,其實每一個事務分配的,並且是全局自增的,在共享表空間的某個位置存放着當前max的txid,因此每開啓一個事務都會分配一個txid
①當前活躍事務列表,裏面記錄着當前正在執行未提交的事務
begin; xxx 隨便什麼語句
這時候產生一個read_view的內存對象,如今在mysql裏面徹底看不到,能看到的就是下面這個,有多少個read_view開着,咱們經過這個read_view來判斷記錄的可見性
-------------- ROW OPERATIONS -------------- 0 queries inside InnoDB, 0 queries in queue 0 read views open inside InnoDB Process ID=23137, Main thread ID=139830599059200, state: sleeping Number of rows inserted 116, updated 27, deleted 0, read 130 0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
這個read_view就是把事務開始的時候事務活躍列表拷貝一份出來,這時候會拿到不少個事務id,只要你的記錄對應的事務id在這個活躍列表中,意味着這個記錄不可見,由於這條記錄在select這個事務開啓的時候還沒提交,因此該記錄的事務id對新產生的事務是不可見的
②read_view在rc和rr中的區別
rr中read_view只產生一次,rc中每執行一條sql語句就會建立一個read_view
緣由:rc能夠讀到已經提交的事務的記錄,第二次執行還要檢查事務活躍列表,若是提交了這條記錄就可見,而rr實現了可重複讀
若是一個事務執行時間很長,它插入了一條記錄,是否可見?就看建立read_view的時候這個tx_id有沒有在活躍列表中,若是不在就意味着不可見,那就不會刪掉了。
rr的好處,read_view只建立一次,rc要建立n次
若是事務活躍列表很長的話,每次拷貝的時候要鎖住全部活躍事務(latch,5.5版本叫kernel_mutex,這個鎖很大),須要時間仍是挺長的,但5.6,5.7都開始作優化了(把大鎖拆分了)
因此,一個事務中有不少條操做,並且全是select操做(隨機主鍵值查詢),這時候rr性能更好,沒有insert因此不會有gap鎖致使的併發插入影響,這種狀況太少了,因此咱們仍是選擇rc
begin的時候仍是執行第一條sql的時候?
測一把便知
mysql> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set (0.00 sec) mysql> desc l; +-------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+---------+------+-----+---------+-------+ | a | int(11) | NO | PRI | NULL | | +-------+---------+------+-----+---------+-------+ 1 row in set (0.00 sec) mysql> select * from l; +---+ | a | +---+ | 1 | | 2 | | 3 | +---+ 3 rows in set (0.00 sec)
測試一:
session1: mysql> begin; Query OK, 0 rows affected (0.00 sec) session2: mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> insert into l values (4); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from l; +---+ | a | +---+ | 1 | | 2 | | 3 | | 4 | +---+ 4 rows in set (0.00 sec) session1: mysql> select * from l; +---+ | a | +---+ | 1 | | 2 | | 3 | | 4 | +---+ 4 rows in set (0.00 sec)
測試二:
session1: mysql> mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from l; +---+ | a | +---+ | 1 | | 2 | | 3 | +---+ 3 rows in set (0.00 sec) session2: mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> insert into l values (4); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from l; +---+ | a | +---+ | 1 | | 2 | | 3 | | 4 | +---+ 4 rows in set (0.00 sec) session1: mysql> select * from l; +---+ | a | +---+ | 1 | | 2 | | 3 | +---+ 3 rows in set (0.00 sec)
按道理若是事務隔離級別爲rr,那一個事務提交了,對另外一個事務不可見,解決不可重複讀,這樣看測試二是合理的,
那爲何,測試二session1一開始select了一把,session2裏面事務提交了,session1就不可見,而測試一session1一開始沒有select,後面再select就可見了?這是重複讀的體現嗎?
緣由:rr隔離級別的時,事務中有select時會建立一個read_view,並且一個事務只建立一次,因此測試一的時候,最後session1 select的時候才建立了read_view,發現session2的事務中相關記錄已經commit了,不在事務活躍列表中,因此讀到了這條記錄,而測試二,session1 開啓事務,第一個select的時候就建立了read_view,這時候session2裏面的事務還沒開啓,第二個select的時候用的仍是原來的rv,這樣就不可見了
tips:
若是但願begin的時候就建立read_view
必須用start transaction with consistent snapshot; 結合rr用,由於rc時候,事務中每執行一個sql就會建立read_view
session1: start transaction with consistent snapshot; 建立了rv session2: begin; insert aaa; commit; session1: select aaa;查不到,建立rv的時候,session2中的事務還不存在 若是rc的話能讀到aaa,由於第三步session1裏執行select又會建立一個rv,會發現aaa這個記錄已經提交了,就能看到了