チョットワカル Row-Based Replication・その3
こんにちわ。せじまです。
引き続き replication の話をします。
はじめに
第三回です。
前回は THD::decide_logging_format() を通じて、 binlog_format=ROW についてぼちぼち学びました。今回は binlog_row_image に関する話を踏まえつつ、ちょっと応用編っぽい話もします。
解説
binlog_format=ROW についてさらに学ぶべく、まずはMySQL Internals Manualを読んでみましょう。
ただ、
MySQL Internals Manual / The Binary Log / Event Data for Specific Event Types をみると
Write_rows_log_event/WRITE_ROWS_EVENT には次のように書いてあります。
[TODO: following needs verification; it's guesswork]
なるほど、binlog_format=ROW について込み入ったことが知りたいなら、最終的には自分でソースコード読んだ方が早そうですね。
で、 Rows_log_event::print_verbose() や Rows_log_event::print_verbose_one_row() を読むと、「なるほど binlog_format=ROW の中身はこんなものか」と想像はつきます。
Rows_log_event::print_verbose_one_row() を呼び出す mysqlbinlog -vv を実行すると、update takanori set val2=val2+2000 where id=3 のような SQLは、次のように表示されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
$ ./mysqlbinlog /var/lib/mysql/binlog.000010 -vv /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; # at 4 #191030 12:22:01 server id 1 end_log_pos 124 CRC32 0x4ca752a3 Start: binlog v 4, server v 8.0.18 created 191030 12:22:01 # Warning: this binlog is either in use or was not closed properly. BINLOG ' aYC5XQ8BAAAAeAAAAHwAAAABAAQAOC4wLjE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAEwANAAgAAAAABAAEAAAAYAAEGggAAAAICAgCAAAACgoKKioAEjQA CgGjUqdM '/*!*/; # at 124 #191030 12:22:01 server id 1 end_log_pos 155 CRC32 0xa2f9fb41 Previous-GTIDs # [empty] # at 155 #191030 12:22:10 server id 1 end_log_pos 234 CRC32 0xad194110 Anonymous_GTID last_committed=0 sequence_number=1 rbr_only=yes original_committed_timestamp=1572438130895076 immediate_commit_timestamp=1572438130895076 transaction_length=381 /*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/; # original_commit_timestamp=1572438130895076 (2019-10-30 12:22:10.895076 UTC) # immediate_commit_timestamp=1572438130895076 (2019-10-30 12:22:10.895076 UTC) /*!80001 SET @@session.original_commit_timestamp=1572438130895076*//*!*/; /*!80014 SET @@session.original_server_version=80018*//*!*/; /*!80014 SET @@session.immediate_server_version=80018*//*!*/; SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 234 #191030 12:22:10 server id 1 end_log_pos 318 CRC32 0x3ba27e42 Query thread_id=526 exec_time=0 error_code=0 SET TIMESTAMP=1572438130/*!*/; SET @@session.pseudo_thread_id=526/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=1168113696/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; /*!\C latin1 *//*!*/; SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=255/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; /*!80011 SET @@session.default_collation_for_utf8mb4=255*//*!*/; BEGIN /*!*/; # at 318 #191030 12:22:10 server id 1 end_log_pos 387 CRC32 0xfb566c82 Rows_query # update takanori set val2=val2+2000 where id=3 # at 387 #191030 12:22:10 server id 1 end_log_pos 443 CRC32 0x0018856b Table_map: `test`.`takanori` mapped to number 89 # at 443 #191030 12:22:10 server id 1 end_log_pos 505 CRC32 0x2dea17b0 Update_rows: table id 89 flags: STMT_END_F BINLOG ' coC5XR0BAAAARQAAAIMBAACAAC11cGRhdGUgdGFrYW5vcmkgc2V0IHZhbDI9dmFsMisyMDAwIHdo ZXJlIGlkPTOCbFb7 coC5XRMBAAAAOAAAALsBAAAAAFkAAAAAAAEABHRlc3QACHRha2Fub3JpAAMDAwMAAAEBAGuFGAA= coC5XR8BAAAAPgAAAPkBAAAAAFkAAAAAAAEAAgAD//8AAwAAAAEAAADQBwAAAAMAAAABAAAAoA8A ALAX6i0= '/*!*/; ### UPDATE `test`.`takanori` ### WHERE ### @1=3 /* INT meta=0 nullable=0 is_null=0 */ ### @2=1 /* INT meta=0 nullable=0 is_null=0 */ ### @3=2000 /* INT meta=0 nullable=0 is_null=0 */ ### SET ### @1=3 /* INT meta=0 nullable=0 is_null=0 */ ### @2=1 /* INT meta=0 nullable=0 is_null=0 */ ### @3=4000 /* INT meta=0 nullable=0 is_null=0 */ # at 505 #191030 12:22:10 server id 1 end_log_pos 536 CRC32 0x90da88a7 Xid = 1089 COMMIT/*!*/; SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ; # End of log file /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; $ |
binlog_row_image=minimal のときは次のようになります
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
$ ./mysqlbinlog /var/lib/mysql/binlog.000011 -vv /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; # at 4 #191030 12:23:12 server id 1 end_log_pos 124 CRC32 0xf250a38b Start: binlog v 4, server v 8.0.18 created 191030 12:23:12 # Warning: this binlog is either in use or was not closed properly. BINLOG ' sIC5XQ8BAAAAeAAAAHwAAAABAAQAOC4wLjE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAEwANAAgAAAAABAAEAAAAYAAEGggAAAAICAgCAAAACgoKKioAEjQA CgGLo1Dy '/*!*/; # at 124 #191030 12:23:12 server id 1 end_log_pos 155 CRC32 0x6c1666a1 Previous-GTIDs # [empty] # at 155 #191030 12:23:16 server id 1 end_log_pos 234 CRC32 0x9f079791 Anonymous_GTID last_committed=0 sequence_number=1 rbr_only=yes original_committed_timestamp=1572438196622460 immediate_commit_timestamp=1572438196622460 transaction_length=365 /*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/; # original_commit_timestamp=1572438196622460 (2019-10-30 12:23:16.622460 UTC) # immediate_commit_timestamp=1572438196622460 (2019-10-30 12:23:16.622460 UTC) /*!80001 SET @@session.original_commit_timestamp=1572438196622460*//*!*/; /*!80014 SET @@session.original_server_version=80018*//*!*/; /*!80014 SET @@session.immediate_server_version=80018*//*!*/; SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 234 #191030 12:23:16 server id 1 end_log_pos 318 CRC32 0x1ac997c3 Query thread_id=530 exec_time=0 error_code=0 SET TIMESTAMP=1572438196/*!*/; SET @@session.pseudo_thread_id=530/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=1168113696/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; /*!\C latin1 *//*!*/; SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=255/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; /*!80011 SET @@session.default_collation_for_utf8mb4=255*//*!*/; BEGIN /*!*/; # at 318 #191030 12:23:16 server id 1 end_log_pos 387 CRC32 0xb0fa914b Rows_query # update takanori set val2=val2+2000 where id=3 # at 387 #191030 12:23:16 server id 1 end_log_pos 443 CRC32 0x8e8f0fb9 Table_map: `test`.`takanori` mapped to number 89 # at 443 #191030 12:23:16 server id 1 end_log_pos 489 CRC32 0x4325f2a3 Update_rows: table id 89 flags: STMT_END_F BINLOG ' tIC5XR0BAAAARQAAAIMBAACAAC11cGRhdGUgdGFrYW5vcmkgc2V0IHZhbDI9dmFsMisyMDAwIHdo ZXJlIGlkPTNLkfqw tIC5XRMBAAAAOAAAALsBAAAAAFkAAAAAAAEABHRlc3QACHRha2Fub3JpAAMDAwMAAAEBALkPj44= tIC5XR8BAAAALgAAAOkBAAAAAFkAAAAAAAEAAgADAQQAAwAAAABwFwAAo/IlQw== '/*!*/; ### UPDATE `test`.`takanori` ### WHERE ### @1=3 /* INT meta=0 nullable=0 is_null=0 */ ### SET ### @3=6000 /* INT meta=0 nullable=0 is_null=0 */ # at 489 #191030 12:23:16 server id 1 end_log_pos 520 CRC32 0x0a1f2359 Xid = 1105 COMMIT/*!*/; SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ; # End of log file /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; $ |
そして、おそらく、 Row-based replication や binlog_row_image=minimal について最も詳細な資料は、Worklog 5092 であり、上記の binlog の中身を説明するものでもあります。
Worklog 5092
https://dev.mysql.com/worklog/task/?id=5092
だいたい全部重要なんですが抜粋しますと
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
BACKGROUND ========== Before and After Images ----------------------- In row based replication row events may contain two copies for the row that they are changing. These are generally known as images. The first one, called *before image* (BI), contains data that existed on the row before it was actually changed. The second one, called *after image* (AI), contain the actual changes. Each, BI and AI, usage is confined to two different moments in the execution flow. The BI is used while the slave is searching for the row to be updated, while AI is used when replaying the changes in the row. Both have some restrictions: - BI: needs to hold a set of values that can be used by the slave to fetch the correct row. In other words, it should provide a set a values that *uniquely* identify the row to be changed; - AI: needs to hold values that are needed to replay all the changes, that were actually done during the original execution, in an identical set (meaning, same index structures, same engine, ...). Given that BI and AI have different usages, their usefulness can be mapped into the data modification row events: - Write_rows_log_event: *requires only AI*. There is no need for a BI because, we are adding a record, and not changing an existing one. The current implementation logs only the AI. - Delete_rows_log_event: *requires only BI*. There is no need for an AI because, the row ceases to exist, as it is removed. However, before removing it, one needs to find it, thence BI is required. - Update_rows_log_event: *requires both: AI and BI*. Both BI and AI, are required. The row needs to be found (BI comes to play) before being changed (AI comes to play). Summing up, BI must contain values that uniquely identifies rows, acting like a primary key equivalent (PKE), while AI must contain values that make possible changing the row according to the original execution. Primary Key Equivalent ---------------------- Tables contain an index structure - mysql calls indexes keys. This structure holds information on which keys are declared, and these range from plain keys (K), to unique keys (UK), or even a primary key (PK). PK - When it comes to logging row based events, PK plays an important role, as it covers the BI requirement of uniquely identifying a given row just by searching using the PK value. Thence, if replicating the master contents, one should be fine by just logging the PK column. The slave would then use this value to search the correct row. Technically, "A PRIMARY KEY is a unique index where all key columns must be defined as NOT NULL. If they are not explicitly declared as NOT NULL, MySQL declares them so implicitly (and silently). A table can have only one PRIMARY KEY." [1] UK - Unique keys share the same usefulness of the PK, except that they need to be declared without nullable parts. From the manual [1]: "[...] For all engines, a UNIQUE index allows multiple NULL values for columns that can contain NULL." In fact, if there is no PK declared in a table and an application requests one, MySQL "[...] returns the first UNIQUE index that has no NULL columns as the PRIMARY KEY." [1] K - Regular keys or nullable UK are of no particular interest when logging row events. Given that these are stripped from uniqueness property provided by UK and PK, these cannot be used to uniquely identify a row. There can be the case that a table does not declare any index at all, or all indexes are regular keys. In this case the master must ensure that the data provided in BI shall be enough to uniquely identify the row. As such, the alternative is to log the full row. This ensures that when searching (using the BI) and applying (using the AI) the next record to be fetched will be uniquely identified by all the fields in the BI. Should there be indistinguishable rows, searching and updating either one in any given order leads to a correct state. Consequently, a *Primary Key Equivalent* (PKE) is defined as: 1. If a PK exists, the PKE is equal to the PK. 2. Otherwise, if there exists a UK where all columns have the NOT NULL attribute, then that is the PKE (if there are more than one such UKs, then one is chosen arbitrarily). 3. Otherwise, the PKE is equal to the set of all columns. Hereafter, we will be considering to PK to be a subset of PKE and it shall map into items 1. and 2. of PKE definition. Furthermore, if no explicit primary key nor UK NOT NULL exists in the table, it is said that the table has no PK. PROBLEM STATEMENT ================= Given the definition of PKE, one can have a smaller set of columns to be logged (instead of the full row), and still be able to find (BI) and update a record (AI), while replaying the row event. This can be useful to reduce bandwidth (less network traffic) and storage (samller binlogs) usage as well as mysqld memory footprint. It becomes even more important if tables contain large blobs that do not need to be logged as part of the BI. Currently, in MySQL 5.1 GA, PKE is always assumed to be the full row, ie, the index structure is ignored. Consequently, AI and BI are always logged with all their columns. It should be possible for the user to configure this behavior such that he could request that BI and AI always log full rows, or a PK when available - for the BI - and changed columns only - for AI. SOLUTION ======== MySQL shall provide an option and the different configurations should be: - minimal: Means PKE in the before image and changed columns in after image - full: Means all columns in both before and after image - noblob: Works as full but avoids sending blobs when these are not needed. Blobs are still replicated if: 1. In AI, if they have been changed. 2. In BI, if they are part of PK. It shall be named: --binlog-row-image={minimal,noblob,full} DEFAULT VALUE SHALL BE: 'FULL'. |
これを読んでいただくといろいろわかると思うのですが、どうもUPDATE時に評価されているのはPKEのみであり、それ以外のColumnは見てないようです。
binlog_format=ROWのとき、 {Write,Delete,Update}_rows_log_event にはColumnの名前が保存されていません。純粋に「何番目のColumnを更新対象とするのか」としか見てないようです。
よって、RBRではmasterとslaveでカラム名が異なるtableでもreplicationできるんじゃないでしょうか?
では試してみましょう。
以下、本番環境では試さないことを推奨する、実験的な試みをします。
次のようなslaveがあるとします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
mysql> select @@version; +-----------+ | @@version | +-----------+ | 8.0.18 | +-----------+ 1 row in set (0.00 sec) mysql> show create table takanori\G *************************** 1. row *************************** Table: takanori Create Table: CREATE TABLE `takanori` ( `id` int(11) NOT NULL AUTO_INCREMENT, `val10` int(11) NOT NULL, `val20` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec) mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 172.18.0.2 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: binlog.000012 Read_Master_Log_Pos: 155 Relay_Log_File: 8230d2822d2f-relay-bin.000024 Relay_Log_Pos: 363 Relay_Master_Log_File: binlog.000012 Slave_IO_Running: Yes Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 155 Relay_Log_Space: 622 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Master_SSL_Allowed: No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: 0 Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 1 Master_UUID: 385a832a-ef37-11e9-a332-0242ac120002 Master_Info_File: mysql.slave_master_info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 0 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: Master_public_key_path: Get_master_public_key: 0 Network_Namespace: 1 row in set (0.00 sec) mysql> select * from takanori; +----+-------+-------+ | id | val10 | val20 | +----+-------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 8000 | | 4 | 100 | 2000 | +----+-------+-------+ 4 rows in set (0.00 sec) mysql> |
次のようなmasterがあるとします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
mysql> select @@version; +-----------+ | @@version | +-----------+ | 8.0.18 | +-----------+ 1 row in set (0.00 sec) mysql> show create table takanori\G *************************** 1. row *************************** Table: takanori Create Table: CREATE TABLE `takanori` ( `id` int(11) NOT NULL AUTO_INCREMENT, `val1` int(11) NOT NULL, `val2` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec) mysql> select * from takanori; +----+------+------+ | id | val1 | val2 | +----+------+------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 8000 | | 4 | 100 | 2000 | +----+------+------+ 4 rows in set (0.00 sec) mysql> |
slaveで更新かけます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
mysql> update takanori set val20=val20-2000 where id = 3; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from takanori; +----+-------+-------+ | id | val10 | val20 | +----+-------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 6000 | | 4 | 100 | 2000 | +----+-------+-------+ 4 rows in set (0.00 sec) mysql> |
binlog_row_image=FULL で master に更新をかけます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
mysql> update takanori set val2=val2+2000 where id=3; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from takanori; +----+------+-------+ | id | val1 | val2 | +----+------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 10000 | | 4 | 100 | 2000 | +----+------+-------+ 4 rows in set (0.00 sec) mysql> |
slave には次のように更新が反映されます。
1 2 3 4 5 6 7 8 9 10 11 12 |
mysql> select * from takanori; +----+-------+-------+ | id | val10 | val20 | +----+-------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 10000 | | 4 | 100 | 2000 | +----+-------+-------+ 4 rows in set (0.00 sec) mysql> |
master の binlog を mysqlbinlog で見ると、次のとおりです。WHERE に @3=8000 と書いてありますが、これはPKEに含まれないので、実は見ていないようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
$ ./mysqlbinlog /var/lib/mysql/binlog.000012 -vv /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; # at 4 #191030 12:43:18 server id 1 end_log_pos 124 CRC32 0xd094047f Start: binlog v 4, server v 8.0.18 created 191030 12:43:18 # Warning: this binlog is either in use or was not closed properly. BINLOG ' ZoW5XQ8BAAAAeAAAAHwAAAABAAQAOC4wLjE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAEwANAAgAAAAABAAEAAAAYAAEGggAAAAICAgCAAAACgoKKioAEjQA CgF/BJTQ '/*!*/; # at 124 #191030 12:43:18 server id 1 end_log_pos 155 CRC32 0x7f275b04 Previous-GTIDs # [empty] # at 155 #191030 12:45:55 server id 1 end_log_pos 234 CRC32 0xd3d10e90 Anonymous_GTID last_committed=0 sequence_number=1 rbr_only=yes original_committed_timestamp=1572439555300913 immediate_commit_timestamp=1572439555300913 transaction_length=381 /*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/; # original_commit_timestamp=1572439555300913 (2019-10-30 12:45:55.300913 UTC) # immediate_commit_timestamp=1572439555300913 (2019-10-30 12:45:55.300913 UTC) /*!80001 SET @@session.original_commit_timestamp=1572439555300913*//*!*/; /*!80014 SET @@session.original_server_version=80018*//*!*/; /*!80014 SET @@session.immediate_server_version=80018*//*!*/; SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 234 #191030 12:45:55 server id 1 end_log_pos 318 CRC32 0x52116058 Query thread_id=571 exec_time=0 error_code=0 SET TIMESTAMP=1572439555/*!*/; SET @@session.pseudo_thread_id=571/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=1168113696/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; /*!\C latin1 *//*!*/; SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=255/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; /*!80011 SET @@session.default_collation_for_utf8mb4=255*//*!*/; BEGIN /*!*/; # at 318 #191030 12:45:55 server id 1 end_log_pos 387 CRC32 0x1e59d01a Rows_query # update takanori set val2=val2+2000 where id=3 # at 387 #191030 12:45:55 server id 1 end_log_pos 443 CRC32 0x34b2f531 Table_map: `test`.`takanori` mapped to number 90 # at 443 #191030 12:45:55 server id 1 end_log_pos 505 CRC32 0x6b5cc245 Update_rows: table id 90 flags: STMT_END_F BINLOG ' A4a5XR0BAAAARQAAAIMBAACAAC11cGRhdGUgdGFrYW5vcmkgc2V0IHZhbDI9dmFsMisyMDAwIHdo ZXJlIGlkPTMa0Fke A4a5XRMBAAAAOAAAALsBAAAAAFoAAAAAAAEABHRlc3QACHRha2Fub3JpAAMDAwMAAAEBADH1sjQ= A4a5XR8BAAAAPgAAAPkBAAAAAFoAAAAAAAEAAgAD//8AAwAAAAEAAABAHwAAAAMAAAABAAAAECcA AEXCXGs= '/*!*/; ### UPDATE `test`.`takanori` ### WHERE ### @1=3 /* INT meta=0 nullable=0 is_null=0 */ ### @2=1 /* INT meta=0 nullable=0 is_null=0 */ ### @3=8000 /* INT meta=0 nullable=0 is_null=0 */ ### SET ### @1=3 /* INT meta=0 nullable=0 is_null=0 */ ### @2=1 /* INT meta=0 nullable=0 is_null=0 */ ### @3=10000 /* INT meta=0 nullable=0 is_null=0 */ # at 505 #191030 12:45:55 server id 1 end_log_pos 536 CRC32 0xd848947e Xid = 1222 COMMIT/*!*/; SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ; # End of log file /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; $ |
もうちょっと試してみましょう。
slave でこうします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
mysql> update takanori set val10=100, val20=val20-2000 where id = 3; Query OK, 1 row affected (0.02 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from takanori; +----+-------+-------+ | id | val10 | val20 | +----+-------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 100 | 8000 | | 4 | 100 | 2000 | +----+-------+-------+ 4 rows in set (0.00 sec) mysql> |
master でこうします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
mysql> select * from takanori; +----+------+-------+ | id | val1 | val2 | +----+------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 10000 | | 4 | 100 | 2000 | +----+------+-------+ 4 rows in set (0.00 sec) mysql> update takanori set val2=val2+2000 where id=3; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from takanori; +----+------+-------+ | id | val1 | val2 | +----+------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 12000 | | 4 | 100 | 2000 | +----+------+-------+ 4 rows in set (0.00 sec) mysql> |
slave はこうなります。
1 2 3 4 5 6 7 8 9 10 11 12 |
mysql> select * from takanori; +----+-------+-------+ | id | val10 | val20 | +----+-------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 12000 | | 4 | 100 | 2000 | +----+-------+-------+ 4 rows in set (0.00 sec) mysql> |
binlog_row_image=FULL
- 上記の例からわかるように、 BI(before image)はログとして残っているだけで、必ずしも、完全一致であるか評価しているわけではないようです。
- そうであるならば、BIにすべてのデータが保存されている必要性ってあんまりないのでは?という気もします(が、ログとしては意味のあるものです)。
- AI(after image) にすべてのデータが保存されていて、それで強制的に上書きされるのは、整合性を保つ上で望ましいことだと思います。
binlog_row_image=noblob
- 必要がなければBIとAIにblobを含まないというのは、なかなか合理的でバランスが良い気もします。
binlog_row_image=minimal
ある意味、 Statement-Based Replication と一番挙動が近いかもしれません。
例えば、上記の master/slave 構成で試してみますと
master
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
mysql> select @@version;select * from takanori; +-----------+ | @@version | +-----------+ | 8.0.18 | +-----------+ 1 row in set (0.00 sec) +----+------+-------+ | id | val1 | val2 | +----+------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 12000 | | 4 | 100 | 2000 | +----+------+-------+ 4 rows in set (0.00 sec) mysql> |
slave
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
mysql> select @@version;select * from takanori; +-----------+ | @@version | +-----------+ | 8.0.18 | +-----------+ 1 row in set (0.00 sec) +----+-------+-------+ | id | val10 | val20 | +----+-------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 12000 | | 4 | 100 | 2000 | +----+-------+-------+ 4 rows in set (0.00 sec) mysql> |
slaveで更新して
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
mysql> update takanori set val10=100, val20=val20-2000 where id = 3; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select @@version;select * from takanori; +-----------+ | @@version | +-----------+ | 8.0.18 | +-----------+ 1 row in set (0.00 sec) +----+-------+-------+ | id | val10 | val20 | +----+-------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 100 | 10000 | | 4 | 100 | 2000 | +----+-------+-------+ 4 rows in set (0.01 sec) mysql> |
master で set session binlog_row_image=minimal; しつつ更新すると
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
mysql> set session binlog_row_image=minimal; Query OK, 0 rows affected (0.00 sec) mysql> select @@version;select * from takanori; +-----------+ | @@version | +-----------+ | 8.0.18 | +-----------+ 1 row in set (0.00 sec) +----+------+-------+ | id | val1 | val2 | +----+------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 12000 | | 4 | 100 | 2000 | +----+------+-------+ 4 rows in set (0.01 sec) mysql> update takanori set val2=val2+2000 where id=3; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select @@version;select * from takanori; +-----------+ | @@version | +-----------+ | 8.0.18 | +-----------+ 1 row in set (0.00 sec) +----+------+-------+ | id | val1 | val2 | +----+------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 14000 | | 4 | 100 | 2000 | +----+------+-------+ 4 rows in set (0.00 sec) mysql> |
slave は二番目のColumnが更新されません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
mysql> select @@version;select * from takanori; +-----------+ | @@version | +-----------+ | 8.0.18 | +-----------+ 1 row in set (0.00 sec) +----+-------+-------+ | id | val10 | val20 | +----+-------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 100 | 14000 | | 4 | 100 | 2000 | +----+-------+-------+ 4 rows in set (0.00 sec) mysql> |
binlog_row_image=full にすると更新されます
master
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
mysql> set session binlog_row_image=full; Query OK, 0 rows affected (0.01 sec) mysql> update takanori set val2=val2+2000 where id=3; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select @@version;select * from takanori; +-----------+ | @@version | +-----------+ | 8.0.18 | +-----------+ 1 row in set (0.00 sec) +----+------+-------+ | id | val1 | val2 | +----+------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 16000 | | 4 | 100 | 2000 | +----+------+-------+ 4 rows in set (0.00 sec) mysql> |
slave
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
mysql> select @@version;select * from takanori; +-----------+ | @@version | +-----------+ | 8.0.18 | +-----------+ 1 row in set (0.00 sec) +----+-------+-------+ | id | val10 | val20 | +----+-------+-------+ | 1 | 1 | 10 | | 2 | 100 | 100 | | 3 | 1 | 16000 | | 4 | 100 | 2000 | +----+-------+-------+ 4 rows in set (0.01 sec) mysql> |
今日はこれまで
今回は応用編っぽい話をしました。 Worklog 5092 はとても重要なことが書いてあると思います。このあたり関心ある方は、熟読した上で、いろいろ動かしてみたり、ソースコードを読んでいただくと良いでしょう。
次回はもうちょっと踏み込んでいきます。