Description:
There is a difference in binlog_row_image behavior on the replica side, which I believe is not documented and not necessary.
When the source has binlog_row_image=FULL, but the replica has binlog_row_image=MINIMAL, the replica's binary log event contains the full row after-image, even if only one column value changes.
Using binlog_row_image=MINIMAL for the very same update on the source works as expected - only the changed column is in the after-image, and the same is logged on the replica.
I don't see anything about it in the doc:
https://dev.mysql.com/doc/refman/8.4/en/replication-options-binary-log.html#sysvar_binlog_...
How to repeat:
mysql > show create table test1\G
*************************** 1. row ***************************
Table: test1
Create Table: CREATE TABLE `test1` (
`id` int NOT NULL AUTO_INCREMENT,
`a` int DEFAULT NULL,
`b` int DEFAULT NULL,
`c` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
-- source:
mysql > set binlog_row_image=FULL;
Query OK, 0 rows affected (0.00 sec)
mysql > update test1 set c=now() where id=100;
Query OK, 1 row affected (0.08 sec)
Rows matched: 1 Changed: 1 Warnings:
-- binary log event:
BEGIN
/*!*/;
# at 695
#250918 20:07:05 server id 20007 end_log_pos 749 CRC32 0x752e577d Table_map: `db1`.`test1` mapped to number 95
# has_generated_invisible_primary_key=0
# at 749
#250918 20:07:05 server id 20007 end_log_pos 821 CRC32 0x778a9976 Update_rows: table id 95 flags: STMT_END_F
### UPDATE `db1`.`test1`
### WHERE
### @1=100 /* INT meta=0 nullable=0 is_null=0 */
### @2=1824119681 /* INT meta=0 nullable=1 is_null=0 */
### @3=1317478204 /* INT meta=0 nullable=1 is_null=0 */
### @4='2025-09-18 20:06:15' /* DATETIME(0) meta=0 nullable=1 is_null=0 */
### SET
### @1=100 /* INT meta=0 nullable=0 is_null=0 */
### @2=1824119681 /* INT meta=0 nullable=1 is_null=0 */
### @3=1317478204 /* INT meta=0 nullable=1 is_null=0 */
### @4='2025-09-18 20:07:05' /* DATETIME(0) meta=0 nullable=1 is_null=0 */
# at 821
#250918 20:07:05 server id 20007 end_log_pos 852 CRC32 0xa3c5bbc2 Xid = 42
COMMIT/*!*/;
-- replica:
mysql > select @@version,@@binlog_row_image;
+-----------+--------------------+
| @@version | @@binlog_row_image |
+-----------+--------------------+
| 8.4.6 | MINIMAL |
+-----------+--------------------+
1 row in set (0.00 sec)
-- binary log event:
BEGIN
/*!*/;
# at 353
#250918 20:07:05 server id 20007 end_log_pos 407 CRC32 0x88cdafda Table_map: `db1`.`test1` mapped to number 85
# has_generated_invisible_primary_key=0
# at 407
#250918 20:07:05 server id 20007 end_log_pos 466 CRC32 0x0831302c Update_rows: table id 85 flags: STMT_END_F
### UPDATE `db1`.`test1`
### WHERE
### @1=100 /* INT meta=0 nullable=0 is_null=0 */
### SET
### @1=100 /* INT meta=0 nullable=0 is_null=0 */
### @2=1824119681 /* INT meta=0 nullable=1 is_null=0 */
### @3=1317478204 /* INT meta=0 nullable=1 is_null=0 */
### @4='2025-09-18 20:07:05' /* DATETIME(0) meta=0 nullable=1 is_null=0 */
# at 466
#250918 20:07:05 server id 20007 end_log_pos 497 CRC32 0xd18ab8f5 Xid = 17
COMMIT/*!*/;
-- source:
mysql > set binlog_row_image=MINIMAL;
Query OK, 0 rows affected (0.00 sec)
mysql > update test1 set c=now() where id=100;
Query OK, 1 row affected (0.08 sec)
Rows matched: 1 Changed: 1 Warnings: 0
BEGIN
/*!*/;
# at 368
#250918 20:09:27 server id 20007 end_log_pos 422 CRC32 0x145b6e76 Table_map: `db1`.`test1` mapped to number 95
# has_generated_invisible_primary_key=0
# at 422
#250918 20:09:27 server id 20007 end_log_pos 469 CRC32 0x4534a90d Update_rows: table id 95 flags: STMT_END_F
### UPDATE `db1`.`test1`
### WHERE
### @1=100 /* INT meta=0 nullable=0 is_null=0 */
### SET
### @4='2025-09-18 20:09:27' /* DATETIME(0) meta=0 nullable=1 is_null=0 */
# at 469
#250918 20:09:27 server id 20007 end_log_pos 500 CRC32 0x39d2e0d9 Xid = 45
COMMIT/*!*/;
-- replica:
BEGIN
/*!*/;
# at 353
#250918 20:09:27 server id 20007 end_log_pos 407 CRC32 0x581f0b84 Table_map: `db1`.`test1` mapped to number 85
# has_generated_invisible_primary_key=0
# at 407
#250918 20:09:27 server id 20007 end_log_pos 454 CRC32 0x7a4f40a6 Update_rows: table id 85 flags: STMT_END_F
### UPDATE `db1`.`test1`
### WHERE
### @1=100 /* INT meta=0 nullable=0 is_null=0 */
### SET
### @4='2025-09-18 20:09:27' /* DATETIME(0) meta=0 nullable=1 is_null=0 */
# at 454
#250918 20:09:27 server id 20007 end_log_pos 485 CRC32 0xc3e86044 Xid = 25
COMMIT/*!*/;
Suggested fix:
In my opinion, the behavior on the replica is inconsistent, and there is no need to include the full after-row image if only some columns have changed values.
There may be use cases where we want the source to use the full image, but then save the space on replicas at the same time.