天天看點

MySQL5.6.27 Release Note解讀(innodb及複制子產品)

問題描述(bug #18871046, bug #72811):

主要為了解決一個比較“古老”的mysql在numa架構下的“swap insanity”問題,其表現為盡管為innodb buffer pool配置設定了足夠多的記憶體,但依然會産生swap。而swap對資料庫系統性能而言是比較緻命的。

當我們配置的buffer pool超過單個node的記憶體時,例如總共64gb記憶體,每個節點32gb,配置設定buffer pool為40gb,預設情況下,會先用滿node 0,再在node1上配置設定8gb記憶體。如果綁定到node 0上的線程需要申請新的記憶體時,不是從node1上申請(還有24gb空餘),而是swap node0的記憶體,再做申請。

解決:

增加了一個隻讀的新參數innodb_numa_interleave選項,當開啟該選項,可以允許在numa架構下采取交叉配置設定記憶體的方式。

在啟動mysql時,采用mpol_interleave方式配置設定記憶體,如上例,在啟動時每個node上配置設定20gb記憶體,保證每個node依然有空閑記憶體可用;在完成innodb buffer pool記憶體配置設定後,再将記憶體配置設定政策設定為mpol_default,之後的線程申請記憶體,都在各自的節點完成。

更新檔:

<a href="https://github.com/mysql/mysql-server/commit/242fa2c92de304637e794e531df1f1b86b8d1dee" target="_blank">https://github.com/mysql/mysql-server/commit/242fa2c92de304637e794e531df1f1b86b8d1dee</a>

<a href="https://github.com/mysql/mysql-server/commit/d2578b57ba7d90a00281ae124a1cd6c83193f62a" target="_blank">https://github.com/mysql/mysql-server/commit/d2578b57ba7d90a00281ae124a1cd6c83193f62a</a>

問題描述:

當一個表被驅逐并重新導入時,如果表的内容是空的(例如先插入大量資料,再一次全删除時,auto_increment不為0),就會導緻auto_increment的值被重置。(bug #21454472, bug #77743)

這是因為每次打開表時,總是通過計算表上記錄最大自增列值的方式來重新設定,當表被打開時,如果沒有資料,auto_increment就會被重置成最小值。

在驅逐表時,将這個表的auto_increment值存儲在記憶體中(dict_sys-&gt;autoinc_map),當表重新讀入時,再恢複其auto_increment值。

這個fix并不算一個完整的修複,當執行個體重新開機時,auto_increment依然會被重置(參閱bug#199), rds mysq已經fix了這個bug,能夠持久化自增列到實體檔案中,在重新開機後不會丢失。

<a href="https://github.com/mysql/mysql-server/commit/d404923aad4693dc152d02461f858d7ef218c336" target="_blank">https://github.com/mysql/mysql-server/commit/d404923aad4693dc152d02461f858d7ef218c336</a>

memcached的一段開啟事務的代碼在assert中調用,而根據assert的文檔定義(

),他的行為是未知的,受ndebug控制,可能assert會被定義成空函數,導緻assert中的函數調用被忽略掉。這個bug在某些平台下可能極易觸發。使用memcached的同學需要注意下。 (bug #21239299, bug #75199)

将函數調用從assert中移出來,隻assert函數傳回值。

<a href="https://github.com/mysql/mysql-server/commit/db5dc6fd3abe855685a554bc3c555b1b63914b60" target="_blank">https://github.com/mysql/mysql-server/commit/db5dc6fd3abe855685a554bc3c555b1b63914b60</a>

在arm64平台上, gcc的内建的tas操作函數__sync_lock_test_and_set

可能不準确,這和平台記憶體模型有關,鎖的行為錯誤,導緻進一步的資料損壞。(bug #21102971, bug #76135)

如果定義了__atomic_test_and_set(ptr, __atomic_acquire)/ __atomic_clear(ptr, __atomic_release)操作,則優先使用該函數進行變量設定,否則如果是x86平台,使用__sync_lock_test_and_set ,其他情況,在編譯階段報錯。

<a href="https://github.com/mysql/mysql-server/commit/f59d68eeae37338d7b25f2571407e763fa897e15" target="_blank">https://github.com/mysql/mysql-server/commit/f59d68eeae37338d7b25f2571407e763fa897e15</a>

innodb shutdown時需要調用三次trx_purge操作,每次處理300個undo log page,最終處理900個undo log page,但是shutdown需要等待purge線程退出,,這可能需要耗費比較長的時間,shutdown會更耗時。(bug #21040050)

1. 在每loop trx_sys_n_rsegs個復原段時,隻調用一次trx_purge,之前的邏輯會調用兩次

2. 在shutdown時一次處理的batch size臨時調整到最多20,

<a href="https://github.com/mysql/mysql-server/commit/3e8202ff443909e93231d453a3f1560b5a5ce3cb" target="_blank">https://github.com/mysql/mysql-server/commit/3e8202ff443909e93231d453a3f1560b5a5ce3cb</a>

調整鎖繼承邏輯,如果是類似replace into這樣的操作,需要進行鎖繼承。之前月報有詳細分析。

<a href="https://github.com/mysql/mysql-server/commit/608efca4c4e4afa1ffea251abda675fcc06efc69" target="_blank">https://github.com/mysql/mysql-server/commit/608efca4c4e4afa1ffea251abda675fcc06efc69</a>

ibuf_bitmap_free的值表示一個page可用的空閑空間資料範圍, 這個值比實際的空閑空間值要大些,當insert操作是通過更新一個已存在但被标記删除的記錄來完成時,innodb沒有更新對應page在ibuf bitmap中的ibuf_bitmap_free值。

解決:更新bitmap

<a href="https://github.com/mysql/mysql-server/commit/641ab6f36813516255738ba25d3c6b721189832e" target="_blank">https://github.com/mysql/mysql-server/commit/641ab6f36813516255738ba25d3c6b721189832e</a>

當innodb_force_recovery大于3時,innodb在redo 被apply之前被設定成read only模式,導緻無法恢複。另外從目前版本開始,當force recovery 大于3時,支援drop table,這可以協助将損壞的表清理掉,避免重複crash。

在代碼中,增加了一個新的變量high_level_read_only,當srv_read_only_mode開啟或者force recovery 大于3時,将該變量設定為true. 在除了drop table之外的ddl和dml執行時,會做檢查。也就是說,force recovery 大于3時,drop table是唯一可以做的操作; innodb_read_only模式下,依然不允許所有變更操作。

<a href="https://github.com/mysql/mysql-server/commit/e5cb1d5adaa603a88277f03fe37cb729d77e8ff6" target="_blank">https://github.com/mysql/mysql-server/commit/e5cb1d5adaa603a88277f03fe37cb729d77e8ff6</a>

本次版本更新修複了大量的複制bug

如果一個事務在備庫apply時需要等待行鎖逾時,并且repositories設定為table模式,重試時可能觸發斷言錯誤。 因為在确定重試時,事務狀态依然是active的,再次調用global_init_info函數會去開啟一個新的事務,觸發斷言(修複bug16533802 引入的regression)

在逾時重試之前調用rli-&gt;cleanup_context(thd, 1),将目前事務完全復原掉,恢複到非active狀态。

更新檔:

當使用statement模式時,單條sql更新多個表時,一個事務可能被錯誤的記錄到binlog中。

如下所示:

create table t1(c1 int) engine=innodb; create table t2(c1 int) engine=innodb;set session binlog_format=’statement';start transaction; update t1,t2 set t1.c1 = 0; savepoint sp1; savepoint sp2; ​commit;

binlog中的記錄為:

| master-bin.000001 |  229 | query       |         1 |         340 | use `test`; create table t1(c1 int) engine=innodb | | master-bin.000001 |  340 | query       |         1 |         451 | use `test`; create table t2(c1 int) engine=innodb | | master-bin.000001 |  451 | query       |         1 |         539 | begin                                             | | master-bin.000001 |  539 | query       |         1 |         648 | use `test`; update t1,t2 set t1.c1 = 0            | | master-bin.000001 |  648 | query       |         1 |         737 | commit                                            | | master-bin.000001 |  737 | query       |         1 |         825 | begin                                             | | master-bin.000001 |  825 | query       |         1 |         934 | use `test`; update t1,t2 set t1.c1 = 0            | | master-bin.000001 |  934 | query       |         1 |        1023 | commit                                            | | master-bin.000001 | 1023 | query       |         1 |        1095 | begin                                             | | master-bin.000001 | 1095 | query       |         1 |        1177 | savepoint `sp1`                                   | | master-bin.000001 | 1177 | query       |         1 |        1259 | savepoint `sp2`                                   | | master-bin.000001 | 1259 | query       |         1 |        1332 | commit                                            | +——————-+——+————-+———–+————-+—————————————————+

這是因為在決定寫binlog cache時,錯誤的寫到stmt_cache中(即時更新的都是事務表),是以在執行兩條update時都單獨寫入到binlog中。并最後寫入了兩個savepoint語句。

(bug #16621582, bug #21349028)

如果是事務表,寫到trx_cache中

https://github.com/mysql/mysql-server/commit/507051d967b134d7f29be759742d132423da601f

當dump線程在dump一個非活躍的binlog時被kill掉時,

本該退出這個日志中的某些事件可能被忽略掉,導緻備庫的資料丢失。這是修複bug#19975697時引入的退化,影響5.6.24之後的版本

在發送binlog時,有兩個循環:1.内循環周遊單個binlog檔案;2.外循環在檔案間進行切換。内循環發現被kill掉後,進入外循環的邏輯,沒有檢查thd的kill标記,而是繼續下面的工作,可能導緻目前檔案中剩餘的log被忽略

(bug #78337, bug #21816399)

在退出内循環後,立刻檢查kill标記,如果線程被kill了,則dump線程退出。

<a href="https://github.com/mysql/mysql-server/commit/0911fe2e28da2778d5385a8c7f93307f64bd807b">https://github.com/mysql/mysql-server/commit/0911fe2e28da2778d5385a8c7f93307f64bd807b</a>

問題描述:

trigger中存在savepoint和rollback to savepoint可能觸發斷言失敗(debug版本),或者錯誤日志中報如下錯誤,複制中斷:

[error] slave sql for channel ”: could not execute write_rows event on table test.t3; unknown error, error_code: 1105; handler error ha_err_generic; the event’s master log master-bin.000001, end_log_pos 1553, error_code: 1105

我們執行bug#76727中提供的case,從binlog記錄的序列如下

begin table_map write_rows xid savepoint `event_logging`

由于這裡savepoint `event_logging`是一個query event,他會去把已有的table map event做清理。導緻後面的write_rows無法解析。

另外一種場景是,即時savepoint後面沒有dmll, 由于在執行savepoint時會調用mysql_bin_log.write_event,将binlog flush 到io cache,并且不帶stmt_end_f标記。如果之後沒有任何dml,沒有做合适的清理。這導緻之後的一次dml不生成自己的tablemap而是使用之前生成的,觸發備庫的解析錯誤。

1. 當在trigger/存儲過程中執行rollback to savepoint時,在函數binlog_savepoint_rollback中會清理table map标記(thd-&gt;clear_binlog_table_maps())。2. 對于trigger/存儲過程中執行的savepoint操作,在調用函數mysql_bin_log::write_event時,加上stmt_end_f标記,并重置table map。

<a href="https://github.com/mysql/mysql-server/commit/69d4e72cb397d3e27a76458e989f440fba049f4f">https://github.com/mysql/mysql-server/commit/69d4e72cb397d3e27a76458e989f440fba049f4f</a>

當執行show binlog events時,會持有lock_log鎖,進行binlog讀取和釋放;而事務送出時,也需要持有lock_log将cache寫到binlog檔案中。這會導緻事務hang住,直到show binlog event操作完成。(bug #76618, bug #20928790)

隻在确認顯示event的結尾位置時才加lock_log保護,讀取和發送都不加鎖;這可能帶來的問題是,例如另外一個線程做一次purge,會産生warning。不過這是可以接受的。

<a href="https://github.com/mysql/mysql-server/commit/739ac25439176f309cd2132ce16fe49d918127c7">https://github.com/mysql/mysql-server/commit/739ac25439176f309cd2132ce16fe49d918127c7</a>

在rotate時若出現由于某些原因導緻生成新檔案名出錯 (需要掃描目錄找到最大binlog序号)或者無法建立新的binlog檔案, binlog_error_action沒有處理,binlog中記錄了incident event,導緻所有slave複制中斷。 (bug #76379, bug #20805298)

出現上述情況時,也讓binlog_error_action進行處理。

<a href="https://github.com/mysql/mysql-server/commit/ffa4a65978901bc6fa9b41692e81a5f7d89342d3">https://github.com/mysql/mysql-server/commit/ffa4a65978901bc6fa9b41692e81a5f7d89342d3</a>

假設有gtid 1-3 已經寫入relay log,我們正準備寫入第4個gtid event,在之前的邏輯中,先寫relay log檔案,再将gtid加入到retrieved_gtid_set中,如果這時在寫完gtid 4 (但還沒加入到retrieved集合)的事件到relay log,發生rotate,這時候previous_gtid的值為(1-3),這時候如果發生重新開機,假設4之後還有别的gtid event,重新開機後的retrieved_gtid_set可能類似1-3:5-6,中間産生gap。

先加入到retrieved_gtid_set,再寫gtid event到relay log中。如果寫入檔案失敗了,則從集合中移除。

<a href="https://github.com/mysql/mysql-server/commit/a2b4259f24b76bc5032d9dbfe6f4452cc9dafc70">https://github.com/mysql/mysql-server/commit/a2b4259f24b76bc5032d9dbfe6f4452cc9dafc70</a>

<a href="https://github.com/mysql/mysql-server/commit/25c85fdfe5e0233c29d9b6014eb4d0b9cf3247ac">https://github.com/mysql/mysql-server/commit/25c85fdfe5e0233c29d9b6014eb4d0b9cf3247ac</a>

主要有兩個問題:1. create view即時發生錯誤,也被記錄到binlog中;2. (1)産生的日志在備庫上,即時被設定了過濾,也會将他的錯誤碼和目前錯誤碼相對比,這屬于設計邏輯錯誤。(bug #76493, bug #20797764)

對于第一個問題,不記錄binlog; 對于第二個問題,隻有在event不被過濾的情況下,才和本地做對比。

<a href="https://github.com/mysql/mysql-server/commit/b127c273f4f66a69761089043db3c1d6a49e2d36">https://github.com/mysql/mysql-server/commit/b127c273f4f66a69761089043db3c1d6a49e2d36</a>

(bug #74950, bug #20074353)

禁止在活躍事務中修改上述兩個參數。

<a href="https://github.com/mysql/mysql-server/commit/5639195ee2988ef80eda3537638dca2d752841be">https://github.com/mysql/mysql-server/commit/5639195ee2988ef80eda3537638dca2d752841be</a>

一個relay log頭通常的序列如下:

format_desc (of slave) previous-gtids (of slave) rotate (of master) format_desc (of master)

在sql線程重新開機時,調用relay_log_info::init_relay_log_pos,需要找到從master上傳過來的fd事件,也就是第四個事件,然而,目前的邏輯認為序列是這樣的:

按照上述序列,由于邏輯上的錯誤,導緻無法找到正确的fd。(bug #73806, bug #20644100, bug #76746, bug #20909880)

需要忽略previous-gtid和rotate,找到正确的fd事件。

<a href="https://github.com/mysql/mysql-server/commit/8a070d30fa66ac5f7099c9972ff7dc92955f979f">https://github.com/mysql/mysql-server/commit/8a070d30fa66ac5f7099c9972ff7dc92955f979f</a>

前提:

1. relay-log-info-repository設定為table模式

2. 打開gtid

3. 一個使用非事務引擎(例如myisam)的事務,被記錄到多個relay log時(在io線程寫relay log時,做個flush log操作)

在執行完上一個relay log時,前一個relay log被purge,調用rli-&gt;flush_info(true),會做一次隐式送出,導緻gtid被消費,切換到下一個relay log,就會因為沒有gtid報錯。(bug #68525, bug #16418100)

當處于一個active的事務組時,不調用rli-&gt;flush_info。

<a href="https://github.com/mysql/mysql-server/commit/9b0e016889c73b8d9db1837f97e03f8ee20fbf19">https://github.com/mysql/mysql-server/commit/9b0e016889c73b8d9db1837f97e03f8ee20fbf19</a>

(bug #68525, bug #16418100)