はじめに
RDSのリードレプリカやマルチAZを使っていても、その裏側でどういう仕組みが動いているかを説明できますか?
今回は『MySQL運用管理 [実践] 入門』第5章を読んで、レプリケーションの目的・アーキテクチャー・遅延の原因・GTIDについて整理しました。
レプリケーションとは
MySQLサーバーのデータを別のMySQLサーバーへコピーする機能です。コピー元をソース、コピー先をレプリカと呼びます(かつてはマスター・スレーブと呼ばれていました)。
レプリケーションの目的
① 読み取り専用
ソースだけでは処理しきれないトラフィックがあるとき、読み取り専用レプリカを作成してロードバランサーで負荷分散します。RDSのリードレプリカがこれにあたります。
注意点として、レプリケーションは完全同期ではありません。レプリケーション遅延が発生するため、必ず最新データが必要な場合はソースから取得する必要があります。
② バッチ処理/分析
集計や全データ取得のような重いクエリはCPUを専有し、MVCCのためundoログが肥大化します。バッチ処理専用のレプリカを用意することでソースへの影響を防げます。
③ バックアップ
論理バックアップ(mysqldump)は全データ取得のため他セッションに影響します。バックアップ専用レプリカを用意することで回避できます。
④ 複数のMySQLを集約
複数のソースから一つのレプリカにデータを集約するマルチソースレプリケーションがあります。シャーディングされたDBを横断してJOINしたい場合などに使います。
⑤ アップグレード
レプリケーションを使ってダウンタイムを最小化したバージョンアップができます。
⑥ フェイルオーバー
ソースが障害で落ちたときにレプリカをソースに昇格させて高可用性を実現します。MySQLは自動フェイルオーバーを持っていないため、orchestratorやMHAなどのOSSか、MySQL InnoDB Clusterを使います。
RDSとの対応
| RDSの機能 | レプリケーションの目的 |
|---|---|
| リードレプリカ | ①読み取り専用 |
| 自動バックアップ | ③バックアップ |
| マルチAZ | ⑥フェイルオーバー(準同期レプリケーション) |
レプリケーションのアーキテクチャー
レプリケーションはいくつかのスレッドがファイルに更新内容を書き込み、それを伝搬する仕組みです。
ソース レプリカ
┌──────────────────┐ ┌──────────────────────────┐
│ │ │ │
│ コミット │ │ I/Oスレッド │
│ ↓ │ ──────────→ │ ↓ │
│ バイナリログ │ │ リレーログ │
│ ↑ │ │ ↓ │
│ バイナリログ │ │ SQLスレッド │
│ ダンプスレッド │ │ ↓ │
└──────────────────┘ │ レプリカに適用 │
└──────────────────────────┘
各コンポーネントの役割
バイナリログ
MySQLへのINSERT・UPDATE・DDLなどすべての変更内容を記録したファイルです。2つの目的があります。
- レプリケーション
- ポイントインタイムリカバリ(特定時点へのロールバック)
MySQL 8.0以降はデフォルトで有効(プレフィックスは binlog)です。
バイナリログダンプスレッド
ソース側のスレッド。バイナリログを読み取ってレプリカに転送します。チューニングポイントはありません。
I/Oスレッド
レプリカ側のスレッド。ダンプスレッドから受け取ったバイナリログをリレーログに書き込みます。チューニングポイントはありません。
リレーログ
I/Oスレッドが受け取ったバイナリログの内容を保存するファイルです。中身はバイナリログと同じです。
SQLスレッド
レプリカ側のスレッド。リレーログを読み取って更新をレプリカに適用します。レプリケーション遅延が問題になるときのほとんどの原因はここにあります。
シングルスレッド vs MTA方式
MySQL 8.0.27以降はMTA(マルチスレッドアプライヤー)がデフォルトです。
| シングルスレッド | MTA方式 | |
|---|---|---|
| SQLスレッド | 1つ | コーディネータ+複数ワーカー |
| 並列実行 | なし | あり |
| デフォルト | 8.0.26以前 | 8.0.27以降 |
| ワーカー数 | - |
replica_parallel_workers(デフォルト4) |
MTA方式はソースで同時刻にコミットされたトランザクションを並列で処理できるため、レプリケーション遅延が発生しにくくなっています。
バイナリログの形式
binlog_format で3種類から選べます。
| 形式 | 内容 | 特徴 |
|---|---|---|
STATEMENT |
SQL文をそのまま保存 | ディスク使用量が少ない。非決定的SQLで不整合のリスクあり |
ROW |
更新された行データを保存 | データ整合性が高い。ファイルサイズが大きくなりやすい |
MIXED |
通常はSTATEMENT、非決定的SQLはROWにフォールバック | バランス型 |
MySQL 5.7以降のデフォルトは ROW です。MySQL 8.0.34からは binlog_format は非推奨になり、将来のバージョンで削除予定です。
非決定的SQLとは、同じSQL文をレプリカで実行してもソースと同じデータにならない可能性があるSQLのことです。例えば ORDER BY 句のない LIMIT 付きの DELETE や UUID() を含む INSERT などがあります。
ポジションレプリケーション vs GTIDレプリケーション
レプリケーションには2種類の方式があります。
| ポジションレプリケーション | GTIDレプリケーション | |
|---|---|---|
| 仕組み | バイナリログのファイル名+バイト位置を指定 | トランザクションIDで管理 |
| デフォルト | ✅ | 要設定(gtid_mode=ON) |
| 運用 | バイナリログとポジションの管理が必要 | GTID管理のため運用が楽 |
| リスク | 誤ったポジション指定でデータズレが気づかず進む可能性 | 制限事項あり |
GTIDは server_uuid:transaction_id の形で表現されます。
bcbed6d2-a7ed-11eb-a89d-005056862c6b:16
GTIDレプリケーションはポジションの管理が不要なため、レプリカの追加やフェイルオーバー時の作業が楽になります。
レプリケーション遅延の原因と対処
レプリケーション遅延とは、レプリカでの適用がソースよりも遅れることです。読み取り専用レプリカにアクセスしたときにソースにはデータが存在するのにレプリカにはまだ存在しない状態となり、サービスの実装によっては障害を引き起こす可能性があります。
原因と対処法
| 原因 | 対処 |
|---|---|
| 同時実行性の高いトラフィック | シングルスレッド方式をMTA方式に変更してワーカースレッド数を調整 |
| 一度に大量の行を更新するトランザクション | DELETEなどを小さい単位に分けてコミットする |
| プライマリキーがないテーブル | 行ベースレプリケーションでPKがないとフルスキャンになる。必ずすべてのテーブルにPKを作ること |
遅延の確認方法
-- シングルスレッド方式向け(秒単位)
SHOW REPLICA STATUS\G
-- Seconds_Behind_Source の値を確認
-- MTA方式向け(ミリ秒単位で正確)
SELECT
WORKER_ID,
LAST_APPLIED_TRANSACTION,
LAST_APPLIED_TRANSACTION_END_APPLY_TIMESTAMP
- LAST_APPLIED_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP AS delay
FROM performance_schema.replication_applier_status_by_worker;
Seconds_Behind_Source はシングルスレッド方式向けのためMTA方式には適していません。MTA方式では replication_applier_status_by_worker テーブルを使うと各ワーカースレッドごとの遅延をミリ秒単位で確認できます。
まとめ
- レプリケーションはソース→レプリカへのデータコピー。バイナリログ・リレーログ・各スレッドで構成される
- MySQL 8.0.27以降はMTA方式がデフォルト。並列処理でレプリケーション遅延が発生しにくくなった
- バイナリログ形式はMySQL 5.7以降はROWがデフォルト。データ整合性が高い
- ポジションよりGTIDレプリケーションの方が運用が楽
- レプリケーション遅延の原因はSQLスレッドにあることが多い。PKなしテーブルは遅延の原因になるため必ずPKを作ること