Бінарний журнал може записувати зміни трьома способами, що задається binlog_format.
STATEMENT - записується текст запиту:
UPDATE products SET price = price * 1.1 WHERE category_id = 5;
-- у журналі - один цей рядок, хоч змінилося 100 000 рядків
- компактно для масових змін;
- небезпечно для недетермінованих запитів:
UUID(),NOW()в певних контекстах,RAND(),LIMITбезORDER BY,UPDATE ... ORDER BYз різним порядком на репліці, тригери й функції. Репліка може отримати інші дані, і це ніхто не помітить одразу; - несумісний з READ COMMITTED для InnoDB.
ROW (за замовчуванням з MySQL 5.7.7) - записуються самі змінені рядки: значення до і після:
- детерміновано: репліка отримує рівно ті самі дані, незалежно від того, як їх обчислено;
- безпечно з будь-яким рівнем ізоляції й будь-якими функціями;
- великий обсяг для масових змін:
UPDATE100 000 рядків - 100 000 записів у журналі; - зручний для захоплення змін (CDC): Debezium та подібні інструменти вимагають саме
ROW.
MIXED - за замовчуванням STATEMENT, але для небезпечних запитів автоматично перемикається на ROW. Компроміс, що втрачає популярність: складно передбачити, що саме потрапить у журнал.
Налаштування ROW-формату:
binlog_row_image:FULL(за замовчуванням) - усі колонки до і після;MINIMAL- лише колонки, що змінилися, плюс ті, що ідентифікують рядок. Помітно зменшує журнал для широких таблиць ізTEXT/JSON, але дехто зі споживачів CDC вимагаєFULL;NOBLOB- усі, крім незміненихBLOB/TEXT.
binlog_row_value_options = PARTIAL_JSON- дляJSON_SET/JSON_REPLACEписати лише змінену частину документа.
Важливо для реплікації в ROW: на репліці рядки знаходяться за первинним ключем. Таблиця без первинного ключа змушує репліку для кожного зміненого рядка сканувати всю таблицю - класична причина багатогодинного відставання після одного масового UPDATE.
Як подивитися ROW-події:
mysqlbinlog --base64-output=DECODE-ROWS --verbose binlog.000042
# ### UPDATE `app`.`products`
# ### WHERE
# ### @1=17 ...
# ### SET
# ### @1=17 ...
Практична відповідь на співбесіді: використовувати ROW (типове значення), за потреби з binlog_row_image = MINIMAL, і мати первинні ключі в усіх таблицях.