Description:
If a table has a trigger, Table_map events are written for all tables referenced in the trigger, even when those tables are not actually involved in the transaction.
Splitting the trigger into multiple triggers (one table per trigger) does not change this behavior.
Impact :
Average event size: 618 bytes
Overhead per event: 109 bytes (~17%)
~95% of transactions affected
Resulting overhead:
Binlog size: >15%
Master network traffic: ~15%
Replica network traffic: ~15% (40+ replicas)
Additional parsing overhead on replicas
How to repeat:
1. Enable the binlog if it is not enabled.
2. SET GLOBAL general_log = 'ON';
3. Table Creation
DROP TABLE IF EXISTS binlog_test;
CREATE TABLE IF NOT EXISTS binlog_test (
id int UNSIGNED AUTO_INCREMENT PRIMARY KEY,
val double,
name_1 varchar(50),
name_2 varchar(50),
name_3 varchar(50)
);
DROP TABLE IF EXISTS binlog_test_val_change_log;
CREATE TABLE IF NOT EXISTS binlog_test_val_change_log (
id_ai int UNSIGNED AUTO_INCREMENT PRIMARY KEY,
id int UNSIGNED,
dt datetime,
dx double,
is_trigger tinyint UNSIGNED
);
DROP TABLE IF EXISTS binlog_test_name_change_log;
CREATE TABLE IF NOT EXISTS binlog_test_name_change_log (
id_ai int UNSIGNED AUTO_INCREMENT PRIMARY KEY,
id int UNSIGNED,
name_1 varchar(50),
name_2 varchar(50),
name_3 varchar(50)
);
DELIMITER $$
CREATE TRIGGER binlog_test AFTER UPDATE ON binlog_test
FOR EACH ROW
BEGIN
IF old.val != new.val THEN
INSERT INTO binlog_test_val_change_log ( id, dt, dx, is_trigger )
VALUES (old.id, NOW(), (new.val - old.val), 1);
END IF;
IF (COALESCE(old.name_1, '') != COALESCE(new.name_1, '') OR COALESCE(old.name_2, '') != COALESCE(new.name_2, '') OR COALESCE(old.name_2, '') != COALESCE(new.name_2, '') ) THEN
INSERT IGNORE INTO binlog_test_name_change_log (id, name_1, name_2, name_3)
VALUES (old.id, old.name_1, old.name_2, old.name_3);
END IF;
END
$$
DELIMITER ;
INSERT INTO binlog_test (val, name_1, name_2, name_3)
VALUES (0.0, 'a', 'a', 'a');
INSERT INTO binlog_test (val, name_1, name_2, name_3)
VALUES (0.0, 'b', 'b', 'b');
SELECT * FROM binlog_test;
UPDATE binlog_test bt SET
bt.val = bt.val + 0.5
WHERE bt.id = 1
LIMIT 1;
UPDATE binlog_test bt SET
bt.val = bt.val + 0.7
WHERE bt.id = 1
LIMIT 1;
UPDATE binlog_test bt SET
bt.val = bt.val + 0.12
WHERE bt.id = 2
LIMIT 1;
select * from binlog_test_val_change_log;
From General Log :
/home/[REDACTED]/opt/mysql/8.4.7/bin/mysqld, Version: 8.4.7-7 ([REDACTED] Server (GPL), Release 7, Revision 9a19f1fd), Time: 2026-07-03T10:27:35.240029Z. started with:
Tcp port: 8409 Unix socket: /tmp/mysql_sandbox8409.sock
Time Id Command Argument
2026-07-03T10:27:48.399067Z 44 Query UPDATE binlog_test bt SET
bt.val = bt.val + 0.7
WHERE bt.id = 1
LIMIT 1
2026-07-03T10:27:48.399380Z 44 Query INSERT INTO binlog_test_val_change_log ( id, dt, dx, is_trigger )
VALUES (old.id, NOW(), (new.val - old.val), 1)
2026-07-03T10:27:53.677505Z 44 Query select * from binlog_test_val_change_log
2026-07-03T10:27:57.768474Z 44 Query select * from binlog_test_name_change_log
select * from binlog_test_name_change_log;
From Binary Log File :
BEGIN
/*!*/;
# at 3954
#260703 10:27:48 server id 1 end_log_pos 4027 CRC32 0xc3a866c4 Table_map: `test`.`binlog_test` mapped to number 104
# has_generated_invisible_primary_key=0
# at 4027
#260703 10:27:48 server id 1 end_log_pos 4115 CRC32 0x58710394 Table_map: `test`.`binlog_test_name_change_log` mapped to number 105
# has_generated_invisible_primary_key=0
# at 4115
#260703 10:27:48 server id 1 end_log_pos 4193 CRC32 0x94ec3630 Table_map: `test`.`binlog_test_val_change_log` mapped to number 106
# has_generated_invisible_primary_key=0
# at 4193
#260703 10:27:48 server id 1 end_log_pos 4267 CRC32 0x7611a665 Update_rows: table id 104
# at 4267
#260703 10:27:48 server id 1 end_log_pos 4325 CRC32 0xf8488805 Write_rows: table id 106 flags: STMT_END_F
### UPDATE `test`.`binlog_test`
### WHERE
### @1=1 /* INT meta=0 nullable=0 is_null=0 */
### @2=0.5 /* DOUBLE meta=8 nullable=1 is_null=0 */
### @3='a' /* VARSTRING(200) meta=200 nullable=1 is_null=0 */
### @4='a' /* VARSTRING(200) meta=200 nullable=1 is_null=0 */
### @5='a' /* VARSTRING(200) meta=200 nullable=1 is_null=0 */
### SET
### @1=1 /* INT meta=0 nullable=0 is_null=0 */
### @2=1.1999999999999999556 /* DOUBLE meta=8 nullable=1 is_null=0 */
### @3='a' /* VARSTRING(200) meta=200 nullable=1 is_null=0 */
### @4='a' /* VARSTRING(200) meta=200 nullable=1 is_null=0 */
### @5='a' /* VARSTRING(200) meta=200 nullable=1 is_null=0 */
### INSERT INTO `test`.`binlog_test_val_change_log`
### SET
### @1=2 /* INT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### @3='2026-07-03 10:27:48' /* DATETIME(0) meta=0 nullable=1 is_null=0 */
### @4=0.69999999999999995559 /* DOUBLE meta=8 nullable=1 is_null=0 */
### @5=1 /* TINYINT meta=0 nullable=1 is_null=0 */
# at 4325
#260703 10:27:48 server id 1 end_log_pos 4356 CRC32 0xd57cef3a Xid = 112
COMMIT/*!*/;
As shown above, even though there is no INSERT operation for the binlog_test_name_change_log table, a Table_map event for this table is still written to the binary log, as shown below:
#260703 10:34:07 server id 1 end_log_pos 4680 CRC32 0x51475007 Table_map: `test`.`binlog_test_name_change_log` mapped to number 105
The same behavior can be observed in the opposite scenario. Although records are inserted only into the binlog_test_name_change_log table and no records are inserted into the binlog_test_val_change_log table, a Table_map event is still written for the binlog_test_val_change_log table.
However, this behavior is expected and is by design. The purpose of this feature request is to introduce a new option or configuration parameter, or any improvement, that would suppress Table_map events for tables referenced by a trigger but not actually modified during trigger execution, thereby reducing unnecessary binary log entries.
More Info : https://github.com/xxxx/xxxx-server/blob/8.0/sql/binlog.cc#L9961
i.e. Before writing row changes for this table, write a table-map event so replicas know what table_id means.