小編給大家分享一下MySQL中slave端Retrieved_Gtid_Set讀取改進(jìn)的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
創(chuàng)新互聯(lián)是一家專注于成都網(wǎng)站制作、成都網(wǎng)站建設(shè)與策劃設(shè)計(jì),平塘網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:平塘等地區(qū)。平塘做網(wǎng)站價(jià)格咨詢:13518219792
今天朋友問我這樣一個(gè)問題@K.I.S.S,在官方文檔中有這樣一段描述:
When using GTIDs, the slave tells the master which transactions it has already received, executed, or both. To compute this set, it reads the global value of gtid_executed and the value of the Retrieved_gtid_set column from SHOW SLAVE STATUS. The GTID of the last transmitted transaction is included in Retrieved_gtid_set only when the full transaction is received. The slave computes the following set: UNION(@@global.gtid_executed, Retrieved_gtid_set) Prior to MySQL 5.7.5, the GTID of the last transmitted transaction was included in Retrieved_gtid_set even if the transaction was only partially transmitted, and the last received GTID was subtracted from this set. (Bug #17943188) Thus, the slave computed the following set: UNION(@@global.gtid_executed, Retrieved_gtid_set - last_received_GTID)
問為什么要減去last_received_GTID。
對于這個(gè)問題我先查看了一下這個(gè)bug到底修復(fù)了什么問題如下:
BUG#17943188 SHOW SLAVE STATUS/RETRIEVED_GTID_SET MAY HAVE PARTIAL TRX OR MISS COMPLETE TRX Problem: ======= The SHOW SLAVE STATUS command contains the column RETRIEVED_GTID_SET. This is supposed to contain the set of GTIDs that exist in the relay log. However, the field is updated when the slave receiver thread (I/O thread) receives a Gtid_log_event, which happens at the beginning of the transaction. If the I/O thread gets disconnected in the middle of a transaction, RETRIEVED_GTID_SET can contain a GTID for a transaction that is only partially received in the relay log. This transaction will subsequently be rolled back, so it is wrong to pretend that the transaction is there. Typical fail-over algorithms use RETRIEVED_GTID_SET to determine which slave has received the most transactions to promote the slave to a master. This is true for e.g. the mysqlfailover utility. When RETRIEVED_GTID_SET can contain partially transmitted transactions, the fail-over utility can choose the wrong slave to promote. This can lead to data corruption later. This means that even if semi-sync is enabled, transactions that have been acknowledged by one slave can be lost. Fix: === It was implemented a transaction boundaries parser that will give information about transaction boundaries of an event stream based on the event types and their queries (when they are Query_log_event). As events are queued by the I/O thread, it feeds the Master_info transaction boundary parser. The slave I/O recovery also uses the transaction parser to determine if a given GTID can be added to the Retrieved_Gtid_Set or not. When the event parser is in GTID state because a Gtid_log_event was queued, the event's GTID isn't added to the retrieved list yet. It is stored in an auxiliary GTID variable. After flushing an event into the relay log, the IO thread verifies if the transaction parser is not inside a transaction anymore (meaning that the last event of the transaction has been flushed). If transaction parser is outside a transaction, the I/O thread verifies if a GTID was stored in the start of the transaction, adding it to the retrieved list, ensuring that all the transaction has arrived and was flushed to the relay log. Also, before this patch, after the I/O thread flushed a single received event into the relaylog, it was possible to rotate the relaylog if the current relaylog file size exceeded max_binlog_size/max_relaylog_size. After this patch, when GTIDs are enabled we only allow this rotation by size if the transaction parser is not in the middle of a transaction. Note: The current patch removed the changes for BUG#17280176, as it also dealt with similar problem in a different way.
大概就是說對于某些I/O線程并沒有完整傳輸?shù)腉tid事物記錄到了RETRIEVED_GTID_SET中這會(huì)導(dǎo)致比較嚴(yán)重問題,因?yàn)槟承┍O(jiān)控工具根據(jù)這個(gè)來判斷是否切換之類的,因此我們加入了一個(gè)事物邊界分析器來判斷事物是否完整傳輸,如果完整傳輸才記錄到RETRIEVED_GTID_SET中這是5.7.5過后加入的??偟恼f來為了進(jìn)行兩個(gè)問題的修復(fù):
1、最后一個(gè)gtid 事物是否完整。
2、跨越多個(gè)relay log的binlog 得到正確的gtid集合。
其實(shí)修改得非常多,不一一列舉,有興趣可以自己看看commit 9dab9dad975d09b8f37f33bf3c522d36fdf1d0f9,這里列舉幾個(gè)我看了的地方。
1、在 MYSQL_BIN_LOG::init_gtid_sets加入了如下邏輯
/* If we use GTIDs and have partial transactions on the relay log, must check if it ends on next relay log files. We also need to feed the boundary parser with the rest of the relay log to put it in the correct state before receiving new events from the master in the case of GTID auto positioning be disabled. */ if (is_relay_log) { /* Suppose the following relaylog: rl-bin.000001 | rl-bin.000002 | rl-bin.000003 | rl-bin-000004 ---------------+---------------+---------------+--------------- PREV_GTIDS | PREV_GTIDS | PREV_GTIDS | PREV_GTIDS (empty) | (UUID:1) | (UUID:1) | (UUID:1) ---------------+---------------+---------------+--------------- GTID(UUID:1) | QUERY(INSERT) | QUERY(INSERT) | XID ---------------+---------------+---------------+--------------- QUERY(CREATE | TABLE t1 ...) | ---------------+ GTID(UUID:2) | ---------------+ QUERY(BEGIN) | ---------------+ As it is impossible to determine the current Retrieved_Gtid_Set by only looking to the PREVIOUS_GTIDS on the last relay log file, and scanning events on it, we tried to find a relay log file that contains at least one GTID event during the backwards search. In the example, we will find a GTID only in rl-bin.000001, as the UUID:2 transaction was spanned across 4 relay log files. The transaction spanning can be caused by "FLUSH RELAY LOGS" commands on slave while it is queuing the transaction. So, in order to correctly add UUID:2 into Retrieved_Gtid_Set, we need to parse the relay log starting on the file we found the last GTID queued to know if the transaction was fully retrieved or not. */ /* Adjust the reverse iterator to point to the relaylog file we need to start parsing, as it was incremented after generating the relay log file name. */ rit--;//回退一個(gè)文件 因?yàn)榍懊鏋閞it++做操作 exp:1<2<3<{4}<5<6<7 ---> 1<2<3<4<{5}<6<7 /* Reset the transaction parser before feeding it with events */ trx_parser->reset(); gtid_partial_trx->clear(); DBUG_PRINT("info", ("Iterating forwards through relay logs, " "updating the Retrieved_Gtid_Set and updating " "IO thread trx parser before start.")); for (it= find(filename_list.begin(), filename_list.end(), *rit); it != filename_list.end(); it++)//從匹配的位置繼續(xù)向后 { const char *filename= it->c_str(); DBUG_PRINT("info", ("filename='%s'", filename)); if (read_gtids_and_update_trx_parser_from_relaylog(filename, all_gtids, true, trx_parser, gtid_partial_trx)) { error= 1; goto end; } } } }
其實(shí)這一塊也說明了解決的什么問題,我們發(fā)現(xiàn)一個(gè)事物的binlog event 在relay log中是可以跨文件的。而在bin log中是不能跨文件的。僅僅判斷最后一個(gè)gtid priv event 是不正確的。因此需要這樣修改。
2、其次加入了邊界分析器Transaction_boundary_parser類
這個(gè)類是完全新加入的,這里是其中的一些狀態(tài):
num enum_event_boundary_type { EVENT_BOUNDARY_TYPE_ERROR= -1, /* Gtid_log_event */ EVENT_BOUNDARY_TYPE_GTID= 0, /* Query_log_event(BEGIN), Query_log_event(XA START) */ EVENT_BOUNDARY_TYPE_BEGIN_TRX= 1, /* Xid, Query_log_event(COMMIT), Query_log_event(ROLLBACK), XA_Prepare_log_event */ EVENT_BOUNDARY_TYPE_END_TRX= 2, /* Query_log_event(XA ROLLBACK) */ EVENT_BOUNDARY_TYPE_END_XA_TRX= 3, /* User_var, Intvar and Rand */ EVENT_BOUNDARY_TYPE_PRE_STATEMENT= 4, /* All other Query_log_events and all other DML events (Rows, Load_data, etc.) */ EVENT_BOUNDARY_TYPE_STATEMENT= 5, /* All non DDL/DML events: Format_desc, Rotate, Incident, Previous_gtids, Stop, etc. */ EVENT_BOUNDARY_TYPE_IGNORE= 6 }; /* Internal states for parsing a stream of events. DDL has the format: DDL-1: [GTID] DDL-2: [User] [Intvar] [Rand] DDL-3: Query DML has the format: DML-1: [GTID] DML-2: Query(BEGIN) DML-3: Statements DML-4: (Query(COMMIT) | Query([XA] ROLLBACK) | Xid | Xa_prepare) */ enum enum_event_parser_state { /* NONE is set after DDL-3 or DML-4 */ EVENT_PARSER_NONE, /* GTID is set after DDL-1 or DML-1 */ EVENT_PARSER_GTID, /* DDL is set after DDL-2 */ EVENT_PARSER_DDL, /* DML is set after DML-2 */ EVENT_PARSER_DML, /* ERROR is set whenever the above pattern is not followed */ EVENT_PARSER_ERROR };
3、read_gtids_and_update_trx_parser_from_relaylog函數(shù)新增
這個(gè)函數(shù)是完全新加入的就是為了完成所說的功能,在read_gtids_and_update_trx_parser_from_relaylog中我看到對文件所有event進(jìn)行了讀取,并且用switch進(jìn)行了不同event類型的處理,但是具體沒有細(xì)看。但是最后看到對于對于是否加入retrieve gtid的判斷如下:
/* If we reached the end of a transaction after storing it's GTID in gtid_partial_trx variable, it is time to add this GTID to the retrieved_gtids set because the transaction is complete and there is no need for asking this transaction again. */ if (trx_parser->is_not_inside_transaction()) { if (!gtid_partial_trx->is_empty()) { DBUG_PRINT("info", ("Adding Gtid to Retrieved_Gtid_Set as the " "transaction was completed at " "relaylog file '%s': Gtid(%d, %lld).", filename, gtid_partial_trx->sidno, gtid_partial_trx->gno)); retrieved_gtids->_add_gtid(gtid_partial_trx->sidno, gtid_partial_trx->gno); gtid_partial_trx->clear(); } }
實(shí)際上就在現(xiàn)在看來應(yīng)該就是讀取 relay_log的最后一個(gè)gtid事物(gtid event或者gtid priv event)同時(shí)需要判斷此gtid事物是否完整。對于官方文檔給出的UNION(@@global.gtid_executed, Retrieved_gtid_set - last_received_GTID),個(gè)人覺得這里的 last_received_GTID應(yīng)該是經(jīng)過判斷的,如果完整則不減,如果不完整則減去。
以上是“MySQL中slave端Retrieved_Gtid_Set讀取改進(jìn)的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!