Bug #111027 | Multi-column indexes with writest will cause HA_ERR_FOUND_DUPP_KEY | ||
---|---|---|---|
Submitted: | 15 May 2023 10:49 | Modified: | 9 Oct 2023 15:42 |
Reporter: | Shun Yi (OCA) | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: Replication | Severity: | S2 (Serious) |
Version: | 5.7.42, 8.0.33 | OS: | Any |
Assigned to: | CPU Architecture: | Any |
[15 May 2023 10:49]
Shun Yi
[15 May 2023 10:57]
Shun Yi
For example: INSERT INTO test_table values (4, 1, 'aahdde', 'bbbeee', 2); The unique key is "aah-bbb-2-1". However, the value added to writset is: (gdb) p pke $3 = "uniq_idx_t½test½4test_table½10\000A\000A\000H\000D\000D\000E\000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 ½130 \000B\000B\000B\000E\000E\000E\000 "... The values of the columns related to the multi-column key is added to the writeset without truncation.
[16 May 2023 2:17]
Shun Yi
In fact, for the string columns, if an index created that use only the leading part of column values, the last_committed value calculated by the writeset may be problematic, leading to Duplicate entry issues. For example, a primary key on a string column: ================= 1. SQL ================= SET SESSION transaction_write_set_extraction = XXHASH64; SET GLOBAL binlog_transaction_dependency_tracking = WRITESET; CREATE database test; USE test; DROP TABLE IF EXISTS test2; CREATE TABLE `test2` ( `id` varchar(64) NOT NULL, `did` int DEFAULT NULL, PRIMARY KEY (`id`(3)) ) ENGINE=InnoDB; --------- last_committed=3 sequence_number=4 INSERT INTO test2 VALUES ('aaabbb', 1); -- last_committed=4 sequence_number=5 DELETE FROM test2 WHERE did = 1; --------- last_committed=3 sequence_number=6 INSERT INTO test2 VALUES ('aaabbc', 2); -- last_committed=6 sequence_number=7 DELETE FROM test2 WHERE did = 2; --------- last_committed=3 sequence_number=8 INSERT INTO test2 VALUES ('aaabbf', 3); -- last_committed=8 sequence_number=9 DELETE FROM test2 WHERE did = 3; ================= 2. Analysis ================= For all the insert statements above, last_committed is 3, indicating that writeset considers these 3 statements to be non-conflicting and can be executed in parallel on the replica. However, these three insert statements do conflict with each other because they all have the same primary key 'aaa'. Why? Because what is added to the writeset is ('id' varchar(64)), not ('id' varchar(3)). ('id' varchar(3)) is the primary key.
[18 May 2023 7:33]
MySQL Verification Team
Hello Shun Yi, Thank you for the report and test case. regards, Umesh
[18 May 2023 7:35]
MySQL Verification Team
- 8.0.33 ### INSERT INTO `test`.`test_table` ### SET ### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */ ### @2='1' /* VARSTRING(256) meta=256 nullable=0 is_null=0 */ ### @3='aaabbb' /* VARSTRING(256) meta=256 nullable=0 is_null=0 */ ### @4='bbbccc' /* VARSTRING(256) meta=256 nullable=0 is_null=0 */ ### @5='2' /* VARSTRING(7200) meta=7200 nullable=0 is_null=0 */ # at 1333 #230518 9:17:37 server id 1 end_log_pos 1333 CRC32 0x40682a01 Xid = 13 COMMIT/*!*/; # End of compressed events! # at 1333 #230518 9:17:37 server id 1 end_log_pos 1412 CRC32 0xaba16eba GTID last_committed=4 sequence_number=5 rbr_only=yes original_committed_timestamp=1684394257333815 immediate_commit_timestamp=1684394257333815 transaction_length=281 /*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/; # original_commit_timestamp=1684394257333815 (2023-05-18 09:17:37.333815 CEST) # immediate_commit_timestamp=1684394257333815 (2023-05-18 09:17:37.333815 CEST) /*!80001 SET @@session.original_commit_timestamp=1684394257333815*//*!*/; /*!80014 SET @@session.original_server_version=80033*//*!*/; /*!80014 SET @@session.immediate_server_version=80033*//*!*/; SET @@SESSION.GTID_NEXT= 'e31eeae8-f54b-11ed-8927-020017078e24:5'/*!*/; # at 1412 #230518 9:17:37 server id 1 end_log_pos 1614 CRC32 0x1b1b9d75 Transaction_Payload payload_size=169 compression_type=ZSTD uncompressed_size=229 # Start of compressed events! # at 1614 #230518 9:17:37 server id 1 end_log_pos 1614 CRC32 0x4e9d5414 Query thread_id=7 exec_time=0 error_code=0 SET TIMESTAMP=1684394257/*!*/; BEGIN /*!*/; # at 1614 #230518 9:17:37 server id 1 end_log_pos 1614 CRC32 0x54098ef3 Table_map: `test`.`test_table` mapped to number 112 # has_generated_invisible_primary_key=0 # at 1614 #230518 9:17:37 server id 1 end_log_pos 1614 CRC32 0xdd5f8ef2 Delete_rows: table id 112 flags: STMT_END_F BINLOG ' EdFlZBMBAAAASQAAAAAAAAAAAHAAAAAAAAEABHRlc3QACnRlc3RfdGFibGUABQgPDw8PCAABAAEA ASAcAAEBgAID/P8A844JVA== EdFlZCABAAAAQgAAAAAAAAAAAHAAAAAAAAEAAgAF/wABAAAAAAAAAAEAMQYAYWFhYmJiBgBiYmJj Y2MBADLyjl/d '/*!*/; ### DELETE FROM `test`.`test_table` ### WHERE ### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */ ### @2='1' /* VARSTRING(256) meta=256 nullable=0 is_null=0 */ ### @3='aaabbb' /* VARSTRING(256) meta=256 nullable=0 is_null=0 */ ### @4='bbbccc' /* VARSTRING(256) meta=256 nullable=0 is_null=0 */ ### @5='2' /* VARSTRING(7200) meta=7200 nullable=0 is_null=0 */ # at 1614 #230518 9:17:37 server id 1 end_log_pos 1614 CRC32 0xcee72de2 Xid = 14 COMMIT/*!*/; # End of compressed events! # at 1614 #230518 9:17:39 server id 1 end_log_pos 1693 CRC32 0xbd9901b8 GTID last_committed=3 sequence_number=6 rbr_only=yes original_committed_timestamp=1684394259458738 immediate_commit_timestamp=1684394259458738 transaction_length=281 /*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/; # original_commit_timestamp=1684394259458738 (2023-05-18 09:17:39.458738 CEST) # immediate_commit_timestamp=1684394259458738 (2023-05-18 09:17:39.458738 CEST) /*!80001 SET @@session.original_commit_timestamp=1684394259458738*//*!*/; /*!80014 SET @@session.original_server_version=80033*//*!*/; /*!80014 SET @@session.immediate_server_version=80033*//*!*/; SET @@SESSION.GTID_NEXT= 'e31eeae8-f54b-11ed-8927-020017078e24:6'/*!*/; # at 1693 #230518 9:17:39 server id 1 end_log_pos 1895 CRC32 0x41795533 Transaction_Payload payload_size=169 compression_type=ZSTD uncompressed_size=229 # Start of compressed events! # at 1895 #230518 9:17:39 server id 1 end_log_pos 1895 CRC32 0x219fef85 Query thread_id=7 exec_time=0 error_code=0 SET TIMESTAMP=1684394259/*!*/; BEGIN /*!*/; # at 1895 #230518 9:17:39 server id 1 end_log_pos 1895 CRC32 0x2572704a Table_map: `test`.`test_table` mapped to number 112 # has_generated_invisible_primary_key=0 # at 1895 #230518 9:17:39 server id 1 end_log_pos 1895 CRC32 0x2263710b Write_rows: table id 112 flags: STMT_END_F BINLOG ' E9FlZBMBAAAASQAAAAAAAAAAAHAAAAAAAAEABHRlc3QACnRlc3RfdGFibGUABQgPDw8PCAABAAEA ASAcAAEBgAID/P8ASnByJQ== E9FlZB4BAAAAQgAAAAAAAAAAAHAAAAAAAAEAAgAF/wACAAAAAAAAAAEAMQYAYWFhZGRkBgBiYmJl ZWUBADILcWMi '/*!*/; ### INSERT INTO `test`.`test_table` ### SET ### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */ ### @2='1' /* VARSTRING(256) meta=256 nullable=0 is_null=0 */ ### @3='aaaddd' /* VARSTRING(256) meta=256 nullable=0 is_null=0 */ ### @4='bbbeee' /* VARSTRING(256) meta=256 nullable=0 is_null=0 */ ### @5='2' /* VARSTRING(7200) meta=7200 nullable=0 is_null=0 */ # at 1895 #230518 9:17:39 server id 1 end_log_pos 1895 CRC32 0xdf551bfe Xid = 15 COMMIT/*!*/; # End of compressed events! # at 1895 #230518 9:33:18 server id 1 end_log_pos 1954 CRC32 0xe9954ef3 Rotate to support-cluster03-bin.000002 pos: 4 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*/;
[9 Oct 2023 15:42]
Jon Stephens
Documented fix as follows in the MySQL 8.0.35 and 8.2.0 changelogs, as follows: To detect dependencies and conflicts among transactions, MySQL Replication uses writesets which are extracted from keys that are NOT NULL (primary keys by definition, as well as unique and foreign keys which are NOT NULL) whenever binlog_format is ROW and the transaction_write_set_extraction is enabled (the default in both cases). In the case of multi-column unique keys using only the leading parts of column values (see "Column Prefix Key Parts" [https://dev.mysql.com/doc/refman/en/create-index.html#create-index-column-prefixes]), the writesets were not properly generated; this was due to the fact that the whole values of referenced key columns were added to the writeset in such cases, even though the keys themselves referenced only the column prefixes. Closed.
[27 Oct 2023 17:07]
Jean-François Gagné
This is marked as fixed in 8.0.35 and 8.2, but nothing about 5.7. It is advertised that 5.7.44 is the last release of 5.7, so it looks this bug will never get fixed in 5.7. Am I understanding things correctly ?