この余⽩はタイトルを書くには狭すぎ る 余⽩じゃなくてタイトル欄なんだけどな 2016/02/20 yoku0825 第2回 MySQL・PostgreSQLユーザーグループ合同DB勉強会 in 東京
この余⽩はタイトルを書くには狭すぎる
余⽩じゃなくてタイトル欄なんだけどな2016/02/20
yoku0825第2回 MySQL・PostgreSQLユーザーグループ合同DB勉強会 in 東京
おしながき
「明⽇使えない⾖知識」その1 (予告)「明⽇使えない⾖知識」その2「明⽇使えない⾖知識」その3「明⽇使えない⾖知識」その4「明⽇使えない⾖知識」その1 (本編)
1/133
「明⽇使えない⾖知識」その1(予告)
2/133
NOW関数は現在時刻を返さない
3/133
真相は番組の後半、チャンネルはそのまま︕
4/133
「明⽇使えない⾖知識」そ
の25/133
MySQL 5.7
2013/04 MySQL 5.7.1 DMR112015/03 MySQL 5.7.6 DMR162015/04 MySQL 5.7.7 RC2015/08 MySQL 5.7.8 RC2015/10 MySQL 5.7.9 GA2015/12 MySQL 5.7.10 GA2016/02 MySQL 5.7.11 GA
6/133
MySQL 5.7
2015/04 MySQL 5.7.7 RCここまではまあいい
2015/08 MySQL 5.7.8 RCJSON型, virtual generated columnの拡張, InnoDB Page Compression
2015/10 MySQL 5.7.9 GAinnodb_default_row_format, JSON -> operator, innodb_numa_interleave
2015/12 MySQL 5.7.10 GABug Fix
2016/02 MySQL 5.7.11 GAInnoDB Tablespace Encryption 7/133
MySQL 5.7
2015/04 MySQL 5.7.7 RCここまではまあいい
2015/08 MySQL 5.7.8 RC新機能!!
2015/10 MySQL 5.7.9 GA新機能!!
2015/12 MySQL 5.7.10 GABug Fix
2016/02 MySQL 5.7.11 GA新機能!!
8/133
General Available #とは
9/133
「明⽇使えない⾖知識」そ
の310/133
講演のタイトル
「イルカ㌠からゾウ㌠に伝えたいMySQLのレプリケーションの仕組み」とか話したかったんですけどかじやまさんから5.7にしるって⾔われてるので残念ながら5.7の何かを話します(仮) と思ったんですけどやっぱりイルカ㌠からゾウ㌠に伝えたいMySQLレプリケーションの話をします
11/133
意訳12/133
MySQL 5.7の話はもう飽きたんでレプリケーションの話をします
13/133
ことのあらまし
14/133
ことのあらまし
15/133
ことのあらまし
16/133
ことのあらまし
17/133
ことのあらまし
18/133
ことのあらまし
19/133
ことのあらまし
20/133
ことのあらまし
21/133
ことのあらまし
22/133
ことのあらまし
23/133
これはレプリケーション でしこたま痛い目を⾒た の先⼈ イルカ㌠から
24/133
これからレプリケーション で痛い目を⾒る でできることが増えていくゾウ㌠へ
25/133
語らねばなるまい(使命感)
26/133
というわけで「明⽇使えない⾖知識」その4
27/133
MySQLのレプリケーションについて
28/133
\こんにちは/
yoku0825@とある企業のDBAオラクれない-ポスグれない-マイエスキューエる-
家に帰ると妻の夫-せがれの⽗-ムスメの⽗-
Twitter: @yoku0825Blog: ⽇々の覚書MyNA ML: ⽇本MySQLユーザ会
29/133
MyNAステッカー
デザインは堤井さん今⽇持ってきてます。声かけてください。
30/133
MySQL年表
< 5.0知らない⼦ですね
5.02005/10〜2012/03
5.12008/11〜2013/12
5.52010/12〜2015/12
5.62013/02〜
5.72015/10〜
31/133
レプリケーション年表
< 5.03.23の頃から非同期レプリケーションがあった
5.1⾏ベースレプリケーションフォーマット追加
5.5準同期レプリケーションの追加
5.6GTID、レプリケーション情報のInnoDBテーブル導⼊、マルチスレッドスレーブ(MTS)の追加、binlogのグループコミット
5.7MTSの機能強化、グループコミットの機能強化、ロスレス準同期レプリケーション 32/133
レプリケーションの動作原理
同じデータを持ったサーバーがあって同じSQLを同じ順番で適⽤すれば最終的に同じデータに戻るよね︖という期待に基づいた、結果整合性モデル
33/133
レプリケーションのご法度
スレーブに更新をかけると「同じデータを持ったサーバー」の前提が崩れる循環レプリケーション…だと…︖-
非決定性関数を使うと「同じSQLを同じ順番で適⽤しても」同じデータに戻らくなるよね︖有名どころでSYSDATE, ユニークキーでORDER BYしてないLIMIT
-
34/133
MySQLのレプリケーションアーキテクチャー
clientA
connection executor storage_engine
binlog binlog_dump
io_thread relaylog
slave_executorsql_thread binlog
storage_engine slave_connection clientB
master
slave
35/133
MySQLレプリケーション⽤語
バイナリーログ, binlogリレーログBinlog Dump ThreadI/Oスレッド, Receiver ThreadSQLスレッド, Applier Thread
36/133
MySQLのレプリケーションアーキテクチャー
Executorがストレージエンジンに書く1. Executorがバイナリーログに書く2. Binlog Dump Threadがバイナリーログを読む3. I/O ThreadがBinlog Dumpからイベントを受け取ってリレーログに書く
4.
SQL Threadがリレーログからイベントを読み取って⾃⾝のExecutorを叩く
5.
37/133
MySQLのレプリケーションアーキテクチャー
Executorがストレージエンジンに書く1. Executorがバイナリーログに書く2. Binlog Dump Threadがバイナリーログを読む3. I/O ThreadがBinlog Dumpからイベントを受け取ってリレーログに書く
4.
SQL Threadがリレーログからイベントを読み取って⾃⾝のExecutorを叩く
5.
38/133
Executorがストレージエンジンに書く
もちろんトランザクション対応はストレージエンジン依存5.6まではコミットの時点でストレージエンジンのコミットをしてしまっていた5.7からはこの時点ではまだコミット されない
39/133
MySQLのレプリケーションアーキテクチャー
Executorがストレージエンジンに書く1. Executorがバイナリーログに書く2. Binlog Dump Threadがバイナリーログを読む3. I/O ThreadがBinlog Dumpからイベントを受け取ってリレーログに書く
4.
SQL Threadがリレーログからイベントを読み取って⾃⾝のExecutorを叩く
5.
40/133
Executorがバイナリーログに書く
Executorから⾒たバイナリーログは binlogストレージエンジンMySQLにもともと実装されているXAトランザクションを使って⼆相コミット
binlog̲cacheにXA PREPAREしておいてXA COMMIT-クラッシュしてもXA RECOVERはできない><-
41/133
ストレージエンジンとして⾒たbinlog
Abinlog_cacheとsync_binlogによりアトミックっぽく振る舞えるけど厳密にAtomicじゃない
Cストレージエンジン層がコミットをシリアライズしてくれることに依存
I常にSERIALIZABLE
Dsync_binlog= 1なら⼀応Durable
42/133
バイナリーログのその他の使い道
PITRシリアライズされた更新情報なのでPITRに使う-
SQLをパースして⾊々するテーブルごとのUPDATE回数を後追いで集計するとか-中⾝を取り出して別のデータストアに⼊れるとか-
43/133
MySQLのレプリケーションアーキテクチャー
Executorがストレージエンジンに書く1. Executorがバイナリーログに書く2. Binlog Dump Threadがバイナリーログを読む3. I/O ThreadがBinlog Dumpからイベントを受け取ってリレーログに書く
4.
SQL Threadがリレーログからイベントを読み取って⾃⾝のExecutorを叩く
5.
44/133
Binlog Dump Threadがバイナリーログを読む
fsyncしててもしてなくてもwriteされた時点から読めるマスターのmysqldではクライアントからのコネクション扱いスレーブでSTART SLAVE => マスターで認証 => ESTABLISH後はマスター側からsendtoできる
-
45/133
MySQLのレプリケーションアーキテクチャー
Executorがストレージエンジンに書く1. Executorがバイナリーログに書く2. Binlog Dump Threadがバイナリーログを読む3. I/O ThreadがBinlog Dumpからイベントを受け取ってリレーログに書く
4.
SQL Threadがリレーログからイベントを読み取って⾃⾝のExecutorを叩く
5.
46/133
I/O ThreadがBinlog Dumpからイベントを受け取ってリレーログに書く
ここからスレーブのmysqldBinlog Dumpから送り付けられたイベントをリレーログとして書き込むちなみに、mysqlbinlog -Rも(マスターから⾒ると)I/Oスレッドと同じ様に振る舞う
47/133
MySQLのレプリケーションアーキテクチャー
Executorがストレージエンジンに書く1. Executorがバイナリーログに書く2. Binlog Dump Threadがバイナリーログを読む3. I/O ThreadがBinlog Dumpからイベントを受け取ってリレーログに書く
4.
SQL Threadがリレーログからイベントを読み取って⾃⾝のExecutorを叩く
5.
48/133
SQL Threadがリレーログからイベントを読み取って⾃⾝のExecutorを叩く
これによりマスターのExecutorがまるでスレーブのExecutorも叩いたかの様にデータが更新されるExecutorはまたバイナリーログを吐くこともできる
49/133
binlogフォーマット
SBRStatement Based Replication
RBRRow Based Replication
MBR(あんまり⾔わない)Mixed Based Replication
50/133
SBR
いわゆる MySQLのレプリケーション っぽいやつbinlogに記録されるのはエンコードされた SQLステートメントイベントはステートメント単位-
MySQL 5.6とそれ以前のデフォルト
51/133
SBR
$ mysqlbinlog -R -h 127.0.0.1 -P 64057 -uroot --stop-never bin.000010
mysql57> SET SESSION binlog_format= 'STATEMENT';Query OK, 0 rows affected (0.00 sec)
mysql57> INSERT INTO t1 VALUES (1, 'one');Query OK, 1 row affected (0.01 sec)
# at 2339#160201 19:14:40 server id 1057 end_log_pos 2441 CRC32 0xb0e9add8 Query thread_id=12 exec_time=0 error_code=0SET TIMESTAMP=1454321680/*!*/;INSERT INTO t1 VALUES (1, 'one')/*!*/;
52/133
SBR
SQLステートメントが記録されるため、そのステートメントが何百万⾏更新しようと1ステートメントぶんのスペースしか取らない。容量的なワーストケースはINSERTや1⾏UPDATE。-
SQLとしてパースされるため、「マスターには存在したけどスレーブには存在しない⾏」へのクエリーはナチュラルに無視される(WHERE句で空振りしたクエリーはエラーじゃなくて空の結果セットを返す)
53/133
SBR
SQLとしてそれぞれのサーバーで実⾏されるため、非決定性(実⾏するたびに結果が違う)ステートメントを使うと死ぬ。
http://dev.mysql.com/doc/refman/5.6/ja/replication-rbr-safe-unsafe.html
-
トリガーはマスターとスレーブで個々に実⾏される(記録されるイベントはトリガー元テーブルに対するもののみ)READ-COMMITTEDだとマスター/スレーブでデータ不整合を起こすのでエラーになる。
54/133
READ-COMMITTEDがSBRを壊す︖
REPEATABLE-READならネクストキーロックでtrx2がブロックされる。
+-----+------+| num | val |+-----+------+| 1 | one |+-----+------+
trx1> BEGIN;trx1> UPDATE t1 SET val= 'updated' WHERE num= 2; -- No row updated.
trx2> BEGIN;trx2> UPDATE t1 SET num= 2 WHERE num = 1;trx2> COMMIT;
trx1> COMMIT;
55/133
RBR
5.1から導⼊binlogに記録されるのは⾏の変更前の値と変更後の値をバイナリーエンコードしたものイベントは⾏単位-
ただしDDLは設定に関わらずSBRで記録されるMySQL 5.7からのデフォルト
56/133
RBR
$ mysqlbinlog -R -h 127.0.0.1 -P 64057 -uroot --stop-never bin.000010
mysql57> SET SESSION binlog_format= 'ROW';Query OK, 0 rows affected (0.00 sec)
mysql57> INSERT INTO t1 VALUES (2, 'two');Query OK, 1 row affected (0.00 sec)
# at 2607#160201 19:23:58 server id 1057 end_log_pos 2653 CRC32 0x424bf805 Table_map: `d1`.`t1` mapped to number 108# at 2653#160201 19:23:58 server id 1057 end_log_pos 2701 CRC32 0xf946a876 Write_rows: table id 108 flags: STMT_END_F
BINLOG 'PjKvVhMhBAAALgAAAF0KAAAAAGwAAAAAAAEAAmQxAAJ0MQACCA8CgAACBfhLQg==PjKvVh4hBAAAMAAAAI0KAAAAAGwAAAAAAAEAAgAC//wCAAAAAAAAAAN0d292qEb5'/*!*/;
57/133
RBR
1⾏1イベント。100万⾏更新すると死ぬ。INSERTや1⾏UPDATEならSBRとそんなに容量変わらない。
-
スレーブでも⾏探索と更新は⾏われるので、 プライマリーキーが無いとカジュアルに死ぬ
-
「マスターには存在したけどスレーブには存在しない⾏」へのクエリーはエラーで⽌まる(更新前の値を持つ⾏が⾒付からないとER̲KEY̲NOT̲FOUND)
58/133
RBR
非決定性関数だろうと「実⾏結果が記録」されるため、マスターとスレーブでも値はズレない。マスターでのみトリガーが解釈され、トリガー元テーブルとトリガー先テーブルの両⽅のイベントがbinlogに記録される。マスターとスレーブでトリガーが違うと地獄が⾒える。-
バイナリーパックで型が決まっているため、マスターとスレーブでデータ型が違うと死ぬ。⽂字コードが違ってもバイト数が異なって死ぬ。-
59/133
MBR
基本はSBR、非決定性ステートメントだけRBRで使い分ける。MBRと呼ぶのは稀。いいとこ尽くしに聞こえなくもないけど、実際は思わぬところでRBRにフォールバックして割とカジュアルに死ぬ。マスタースレーブでトリガーをズラして調整している時にRBRに落ちて泣いたり。
-
何故かWHERE pkey = ?なクエリーにLIMIT 1とか付けられてRBRに落ちて Bug#74118 の洗礼を受けたり。
-
MySQL 5.1の⼀部のリビジョンだけこれがデフォルト。弊社はこれです。。
60/133
初⼼者は黙ってRBR(5.7ʼs default)
初⼼者は黙ってRBRサイレントにデータがズレるよりはエラって⽌まった⽅がマシデータのズレを検出&リカバリーするにはpt-table-
checksumとpt-table-syncが有効pt-slave-restartは使えるけどオススメしない
61/133
pt-table-checksum
SBRとRBRでそれぞれチェックサムを計算してテーブルに突っ込むクエリーを発⾏マスター上では当然SBRでもRBRでも同じチェックサムになる
-
スレーブではSBRで来たイベントは スレーブでチェックサムを計算 、RBRで来たイベントは マスターで計算されたチェックサムがそのままカラムに⼊るこれを⽐較することができる
62/133
pt-online-schema-change
変更元テーブルをコピーして先にALTER TABLEを適⽤する変更元テーブルにトリガーを仕掛けて、変更元テーブルに対する更新がコピーしたテーブルにも反映されるようにするREAD-COMMITTEDでゆっくり変更元テーブルからコピーしたテーブルに⾏をコピーするコピーが終わったらロックを取ってRENAME TABLE
63/133
pt-online-schema-change
InnoDBオンラインDDLが導⼊された5.6以降でも有効何故なら、SQLスレッドは相変わらず1本(あるいは、同じテーブルだとMTS使ってても追い越せない)ので、ALTER
TABLEに時間がかかるとその分レプリケーションが遅れるpt-oscは細切れに⾏をコピーして、スレーブの遅れ具合をチェックしながらスリープを⼊れるので、レプリケーション遅延に優しい
64/133
バイナリーログの⼀⽣
マスター上でコミットされた順にbinlogにエンコードbinlogからrelaylogにシングルスレッドで転送(順序は保証される)relaylogはシングルスレッドまたはマルチスレッドで適⽤
MySQL 5.5とそれ以前はシングルスレッド固定-MySQL 5.6とそれ以降はslave_parallel_workers > 1でMTS(Multi Threaded Slave)
-
65/133
MTSのアーキテクチャー
clientA
connection executor storage_engine
binlog binlog_dump
io_thread relaylog
slave_executor1
slave_executor2
slave_executor3
coordinator slave_worker1
slave_worker2
slave_worker3
storage_engine
binlog
slave_connection clientB
master
slave
66/133
MTSのアーキテクチャー
マスターのバイナリーログがリレーログに記録されるところまでは⼀緒
1.
SQL Thread(coordinator)がリレーログからイベントを取り出す
2.
coordinatorがslave̲worker̲threadにイベントを渡す3. slave̲worker̲threadはそれぞれExecutorを叩いてストレージとバイナリーログを更新する
4.
67/133
MTSのアーキテクチャー
マスターのバイナリーログがリレーログに記録されるところまでは⼀緒
1.
SQL Thread(coordinator)がリレーログからイベントを取り出す
2.
coordinatorがslave̲worker̲threadにイベントを渡す3. slave̲worker̲threadはそれぞれExecutorを叩いてストレージとバイナリーログを更新する
4.
68/133
MTSコーディネーター
前後のイベントが互いに⼲渉しない(順序を⼊れ替えても問題ない)かどうかの責任を持つただし順序が⼊れ替わる(線形でない)ことが⼤前提のため、シングルスレッドレプリケーションよりも結果整合性の⾊が強い
-
69/133
MTSコーディネーター
MySQL 5.6ではスキーマが分かれていることで⼲渉しないことを担保(5.7のslave_parallel_type= DATABASE相当)5.7ではマスターでbinlogに記録された時の直前のコミットのタイムスタンプをベースに⼲渉しないことを担保するモードが登場(slave_parallel_type= LOGICAL_CLOCK)マスター上でロックが競合せずにグループコミットの中に⼊れたってことはスレーブでも競合しないってことだよね︖ という理屈
-
70/133
MTSのアーキテクチャー
マスターのバイナリーログがリレーログに記録されるところまでは⼀緒
1.
SQL Thread(coordinator)がリレーログからイベントを取り出す
2.
coordinatorがslave̲worker̲threadにイベントを渡す3. slave̲worker̲threadはそれぞれExecutorを叩いてストレージとバイナリーログを更新する
4.
71/133
slave̲worker̲thread
独⽴して動作するため、RR(Repeatable-Read)でもマスターと同⼀のデータベース全体のスナップショットを保証できない5.6ではスレーブ側のバイナリーログの中⾝は順序を保証できない(5.7ではslave_preserve_commit_orderでLOGICAL̲CLOCKベースの順序を保証できるようになった)
72/133
イベントの識別
たとえばInnoDBログならLSNバイナリーログはファイル名とファイルの先頭からのオフセットバイト数これらはマスター上でのみ⼀意に識別されるものなのでカスケードすると変わる
-
MySQL 5.6とそれ以降のGTIDもこれらをマッピングするだけで、本質的には相変わらずこれで⾒ている
-
73/133
CHANGE MASTER TO
mysql> CHANGE MASTER TO master_log_file= '..', master_log_pos= ..;
このバイナリーログのこのポジションから先を寄越せ、というクライアントからのリクエスト。ポジションの指定はイベントの先頭でないといけない(イベントの先頭でないポジションを指定するとSTART SLAVEでI/Oスレッドがエラる)バイナリーログの固定ヘッダ⻑はバージョンによって違うけど、どうせ読み⾶ばされるので「先頭から読ませたい」時はmaster_log_pos= 1でどのバージョンでもイケる。
-
74/133
master.info
マスターの情報を保持するための構造。コード追う時はmi。CHANGE MASTER TOで与えられた情報 + αが⼊ってる。relay_log_recovery != 1の時はここからファイルとポジションの情報を復元してレプリケーションを再開する5.6とそれ以降ではInnoDBなテーブルに記録することが可能(master_info_repository= {TABLE|FILE}
75/133
master.info
slave1 [localhost] {msandbox} ((none)) > SELECT * FROM mysql.slave_master_info\G*************************** 1. row *************************** Number_of_lines: 23 Master_log_name: mysql-bin.000001 Master_log_pos: 3089 Host: 127.0.0.1 User_name: rsandbox User_password: rsandbox Port: 22494 Connect_retry: 60 Enabled_ssl: 0 Ssl_ca: Ssl_capath: Ssl_cert: Ssl_cipher: Ssl_key:Ssl_verify_server_cert: 0 Heartbeat: 1800 Bind: Ignored_server_ids: 0 Uuid: 00022494-1111-1111-1111-111111111111 Retry_count: 86400 Ssl_crl: Ssl_crlpath: Enabled_auto_position: 01 row in set (0.00 sec)
76/133
relay̲log.info
リレーログ(SQLスレッド, slave̲worker̲thread)の情報を保持するための構造。コード追う時はrli。Relay̲log̲fileとかRelay̲log̲posとかRelay̲Master̲Log̲FileとかExec̲master̲log̲posとか⼊ってる(SQLスレッド側の使う情報)5.6とそれ以降ではInnoDBなテーブルに記録することが可能(relay_log_info_repository= {TABLE|FILE})
77/133
relay̲log.info
slave1 [localhost] {msandbox} ((none)) > SELECT * FROM mysql.slave_relay_log_info\G*************************** 1. row *************************** Number_of_lines: 7 Relay_log_name: ./mysql-relay.000002 Relay_log_pos: 3061 Master_log_name: mysql-bin.000001 Master_log_pos: 2898 Sql_delay: 0Number_of_workers: 0 Id: 11 row in set (0.00 sec)
78/133
レプリケーションとマスタークラッシュ
バイナリーログ マスターストレージ リレーログ 影響
残ってない 未更新 受信してない なし(︖)
残ってない 未更新 受信した データ不整合(スレーブに
あってマスターにない)
しかもエラー(ER̲MASTER̲FATAL̲ERROR̲READING̲BINLOG)
残ってない 更新済み 受信してない データ不整合(マスターに
あってスレーブにない)
残ってない 更新済み 受信した なしだがエラー(ER̲MASTER̲FATAL̲ERROR̲READING̲BINLOG)
残ってる 未更新 受信してない トランザクションロスト
残ってる 未更新 受信した データ不整合(スレーブに
あってマスターにない)
残ってる 更新済み 受信してない なし(リトライで救われる)
残ってる 更新済み 受信した なし
バイナリーログが残っていない場合、その期間のPITR不可79/133
レプリケーションとマスタークラッシュ
sync_binlog= 1だと起こり得る範囲がぐっと狭くバイナリーログ マスターストレージ リレーログ 影響
残ってる 未更新 受信してない トランザクションロスト
残ってる 未更新 受信した データ不整合(スレーブに
あってマスターにない)
残ってる 更新済み 受信してない なし(リトライで救われる)
残ってる 更新済み 受信した なし
80/133
レプリケーションとマスタークラッシュ
またはsemisync + log_slave_updatesでマスター切り替えバイナリーログ マスターストレージ リレーログ 影響
残ってない 未更新 受信した データ不整合(スレーブに
あってマスターにない)
しかもエラー(ER̲MASTER̲FATAL̲ERROR̲READING̲BINLOG)
残ってない 更新済み 受信した なしだがエラー(ER̲MASTER̲FATAL̲ERROR̲READING̲BINLOG)
残ってる 未更新 受信した データ不整合(スレーブに
あってマスターにない)
残ってる 更新済み 受信した なし
81/133
レプリケーションとスレーブクラッシュ
リレーログ master.info SQLスレッド relay̲log.info 影響
受け取ってない 記録してない 適⽤してない 記録してない なし
受け取ってる 記録してない 適⽤してない 記録してない リレーログ⼆重受信
受け取ってる 記録してない 適⽤した 記録してない リレーログ⼆重受信 + ⼆重適⽤ = 三重コミット
受け取ってる 記録してない 適⽤した 記録した リレーログ⼆重受信
受け取ってる 記録した 適⽤してない 記録してない なし
受け取ってる 記録した 適⽤した 記録してない リレーログ⼆重適⽤
受け取ってる 記録した 適⽤した 記録した なし
82/133
レプリケーションとスレーブクラッシュ
relay_log_info_repository= TABLE
ただしInnoDB前提(innodb_flush_log_at_trx_commit
= 1で次の表)-
SQLスレッドのイベント適⽤とmysql.slave̲relay̲log̲infoへのUPDATEを1つのコミットにまとめる
-
83/133
relay_log_info_repository= TABLE && innodb_flush_log_at_trx_commit = 1
リレーログ master.info SQLスレッド relay̲log.info 影響
受け取ってない 記録してない 適⽤してない 記録してない なし
受け取ってる 記録してない 適⽤してない 記録してない リレーログ⼆重受信
受け取ってる 記録してない 適⽤した 記録した リレーログ⼆重受信
受け取ってる 記録した 適⽤してない 記録してない なし
受け取ってる 記録した 適⽤した 記録した なし
84/133
レプリケーションとスレーブクラッシュ
relay_log_recovery
レプリケーションの再開時にmaster.infoのポジション情報をアテにせず、relaylog.infoの情報を元にレプリケーションを復元する
-
受信済みだけど未適⽤の(relaylog.infoに適⽤済みと記録されてない)リレーログは捨てる
-
85/133
relay_log_recovery
リレーログ master.info SQLスレッド relay̲log.info 影響
受け取ってない 記録してない 適⽤してない 記録してない なし
受け取ってる 記録してない 適⽤した 記録してない リレーログ⼆重受信 + リレーログ⼆重適⽤ = 三重コミット
受け取ってる 記録した 適⽤してない 記録してない なし
受け取ってる 記録した 適⽤した 記録した なし
86/133
レプリケーションとスレーブクラッシュ
男らしくバックアップからリストアする
割と真⾯目に。毎回リストアすると割り切れば、ACIDのDは無茶な⽅向に振れて性能が稼げるお( ^ω^)
-
87/133
レプリケーションとスレーブクラッシュ
sync̲relay̲log?syncされていようとsyncされていまいと、リレーログからレプリケーション再開位置を特定するわけではない
sync̲master̲info?relay_log_recoveryならrelay̲log.infoから再開位置を特定するのであまり重要じゃない
sync̲relay̲log̲info?relay_log_info_repository != TABLEやトランザクション非対応テーブルの場合にのみ効いてくる。ややこしいので注意。
http://dev.mysql.com/doc/refman/5.6/ja/replication-options-slave.html#sysvar̲sync̲relay̲log̲info 88/133
マスター側のフィルターに関するパラメーター
server_id, replicate_same_server_idフィルター関連っぽくないけど。-⾃分と同じサーバーIDのイベントは読み⾶ばすカスケードすると同じサーバーIDのmysqldがいても⼀⾒問題なさげに⾒えてしまう
-
binlog_do_db, binlog_ignore_dbそもそもバイナリーログに記録されなくなる。-カレントデータベース基準。-個⼈的に推奨しない。-
89/133
スレーブ側のフィルターに関するパラメーター
SQLスレッドでフィルターなので、I/Oスレッドはフツーに受信してリレーログまで吐く
-
replicate_do_db, replicate_ignore_db-replicate_do_table, replicate_ignore_table-replicate_wild_do_table, replicate_wild_ignore_table-
replicate_rewrite_db
90/133
レプリケーションフィルターの評価順
フローチャートになってるので⾒やすいMySQL :: MySQL 5.6 リファレンスマニュアル :: 17.2.3 サーバーがレプリケーションフィルタリングルールをどのように評価するかマスターとスレーブで意図的に不整合を起こさせよう っていうんだから業が深い
91/133
レプリケーションで垂直シャード
table1 table2 table3
binlog
replicate_do_table= table1
table1 replicate_do_table= table2, table3
table2
table3
master
slave slave
92/133
マスターとスレーブで
バージョンが違ってもいい(レプリケーションがサポートされるのは原則リリース系列1世代)スレーブを先にバージョンアップするパターンのみ全部のスレーブを先にバージョンアップしてマスター切り替えして、旧マスターをバージョンアップして新マスターにぶら下げるとローリングアップグレードとか
93/133
マスターとスレーブで
ストレージエンジンが違ってもいい(だがマスターをBLACKHOLEはやめておけ)マスターはInnoDBでトランザクション保護、集計⽤スレーブはMyISAMでスキャンしまくる遊び(あるいは、MyISAMは容量効率がいいし)マスターはInnoDBで保護、参照⽤スレーブにMroonga(参照ロックフリー、全⽂検索、カラムナー)ローリングアップグレードと同じ要領で、MyISAMからInnoDBに変更したり
94/133
マスターとスレーブで
インデックスがあったりなかったりしていい(カラムはやめておいた⽅が無難)
RSU(Rolling Schema Update)って呼ぶこともある。呼ばないこともある。
-
5.6のInnoDBオンラインDDLでRSUがかなりやりやすく-ただしあんまり差があると管理が⾯倒
95/133
これまでのレプリケーションの制約
I/Oスレッド - リレーログ - SQLスレッド で1組5.6からSQLスレッドは並列化可能に-I/Oスレッドが1本しかないということは、「スレーブから⾒たマスターは1つしか存在できない」マスターから⾒たスレーブはいくつあってもいい(ただのクライアントなので、(性能を考えなければ)max_connections未満ならいくらでも)
-
96/133
複数のマスターを持ちたい理由
集計⽤スレーブあっちのサーバーのデータとこっちのサーバーのデータをJOINしたい
-
集計⽤と割り切ればHDDのRAID5で容量を稼ぐという選択肢もありだし
-
シャードの集約複数のシャードをまとめてサーバー削減したい-
97/133
N対1レプリケーション
通称えぬいちレプリケーションまたはどあきレプリケーション
「1つのマスターしか設定できないなら、定期的にマスターを切り替えればいいじゃない」do-aki/N1Repl: master n : slave 1 replication for mysql
98/133
N対1レプリケーション
master1 slave master2
START SLAVE
Binlog Dump
Applying
CHANGE MASTER TO
Binlog Dump
Applying
CHANGE MASTER TO
Binlog Dump
99/133
N対1レプリケーション
実はMySQL UtilitiesにもmysqlrplmsというどあきレプリケーションスクリプトがあるただしこちらはGTID必須mysqlrplms ̶ Set Up and Start Replication Among a Slave and Multiple Masters
100/133
(Native) Multi Source Replication
MySQLでは5.7、MariaDBでは10.0から「1つのI/Oスレッドが1つのマスターしか設定できないなら、I/Oスレッドごと横に増やせばいいじゃない」I/Oスレッド, リレーログ, SQLスレッド(MTS可)を「チャンネル」(MariaDBは「コネクション」)という単位で複数起動するもちろんチャンネル間のデータ競合や順序の保証はユーザーがしなければいけない
-
101/133
(Native) Multi Source Replication
master1 slave_channel_1 slave_channel_2 master2
START SLAVE FOR CHANNEL 1
Binlog Dump
Applying
START SLAVE FOR CHANNEL 2
Binlog Dump
Applying
102/133
お⼿軽シャード集約
スキーマが分かれている場合インポートだけ間違えなければ⼤丈夫スキーマは同じだけどテーブルが分かれている場合インポートだけ間違えなければ⼀応⼤丈夫同じ名前空間のテーブルがあるユニークキーは本当にユニーク︖ サロゲートキーは︖
MySQLやSSDとかの話 前編
103/133
GTID
Global Transaction IDentifierサーバー識別⼦(マスターの@@server_uuid)、ソースを追っかけるならSIDNO(Source ID NO)
datadir/auto.cnfに保管されるので、リストア時に注意
-
トランザクション識別⼦(マスター上でトランザクションがコミットされた連番)、ソースを追っかけるならGNO(Group NO)
-
トランザクションを⼀意に識別するだけで、バイナリーログのイベントを⼀意に識別するわけではないこれ意外と⾒落としがち-
104/133
GTIDとマスター
バイナリーログにGTIDを埋め込むようになる…くらいしか違いはないとはいえgtid_mode= ONにはenforce_gtid_consistency= ON
が必要なので、マスターの振る舞いとしては違いは少なくともサーバーとしての振る舞いはちょっと違う明らかにトランザクションアンセーフなステートメントの実⾏を拒否するようになる
-
105/133
GTIDとI/Oスレッド
master_auto_position= 1の場合、「このファイルのこのポジションから寄越せ︕」の代わりに、「この(=スレーブが既に適⽤済みの)GTID 以外 のバイナリーログを頼む」になるサーバー側が動的にファイル名とポジションに変換して送信してくれる。
-
Commandが”Binlog Dump”から”Binlog Dump GTID”になる。⾖。
-
マスターに伝える「適⽤済みのGTID」はgtid_executed変数に⼊っている。
106/133
gtid̲executedの保管
バージョン log̲bin 保存先 再起動時のgtid̲executedの復元
5.6 OFF N/A N/A(GTIDが有効化できない)
5.6 ON バイナリーログ 最新のバイナリーログのPrevious̲gtids̲log̲eventと最新のバイナリーログのGtid̲log̲eventを全部⾜したもの
107/133
gtid̲executedの保管
バージョン log̲bin 保存先 再起動時のgtid̲executedの復元
5.7 OFF mysql.gtid̲excuted
テーブルから(COMMITのたびにmysql.gtid̲executedに保存)
5.7 ON バイナリーログ&テーブル
バイナリーログから。テーブルへの記録はバイナリーログのフラッシュの時だけ
108/133
GTIDとSQLスレッド
GTIDの連番部分(GNO)は気にしないこれから実⾏するトランザクションのGTID(gtid_next)が実⾏済み(gtid_executedに含まれる)の場合、実⾏しない(スキップする)これによってスレーブクラッシュ時の「リレーログの⼆重適⽤」が完全に回避できる
-
sql/sql̲parse.cc:mysql̲execute̲commandの中で分岐するので、エグゼキューターがそもそも(ほぼ)動かない。
-
エグゼキューターなので、SQLスレッドに限らずフツーのスレッドでも同じ動作。
-
109/133
GTIDの有効化
masterslave OFF OFF̲PERMISSIVE ON̲PERMISSIVE ON
OFF ○ ○ ○ ×
OFF̲PERMISSIVE
○ ○ ○ ×
ON̲PERMISSIVE
× ○ ○ ○
ON × ○ ○ ○
110/133
GTIDの有効化
OFFGTIDを振らない。GTIDの振られたイベントが来るとエラる
OFF̲PERMISSIVE (5.7から)GTIDを振らないけど、GTIDの振られたイベントが来ても⽂句を⾔わない
ON̲PERMISSIVE (5.7から)GTIDを振るけど、GTIDの振られてないイベントが来ても⽂句を⾔わない
ONGTIDを振るし、GTIDの振られてないイベントが来るとエラる
111/133
準同期レプリケーション
通称semisync
ここを同期にするExecutorがストレージエンジンに書くa. Executorがバイナリーログに書くb. Binlog Dump Threadがバイナリーログを読むc. I/O ThreadがBinlog Dumpからイベントを受け取ってリレーログに書く
d.
I/O ThreadからBinlog DumpにACKを返すe. SQL Threadがリレーログからイベントを読み取って⾃⾝のExecutorを叩く
f.
112/133
準同期レプリケーションのパラメーター
rpl_semi_sync_master_timeout
デフォルトが10000(ミリ秒、つまり10秒)と⼤きい-rpl_semi_sync_master_wait_no_slave
OFFにすると、スレーブが全滅すると⾃動でAsyncレプリケーションにフォールバックする
-
113/133
準同期レプリケーションの拡張 at 5.7
rpl_semi_sync_master_wait_for_slave_count
いくつのI/O ThreadがACKを返したらマスター上でOKとされるかを指定可能に
-
rpl_semi_sync_master_wait_point= {AFTER_SYNC|AFTER_COMMIT}
マスター上で、ACKを待ってからストレージエンジンに書き込むか、ストレージエンジンに書き込んでからACKを待つか
-
「ロスレス準同期」と⾔ってるやつ-
114/133
その他のレプリケーション︖
115/133
仮想完全同期レプリケーション
通称Galera Cluster
本家codershipのGalera Cluster for MySQL, Percona実装のPercona XtraDB Cluster, MariaDB実装のMariaDB Galera Clusterがある
-
バイナリーログモジュールを使ってるけどバイナリーログとは別のファイルを吐き出すInnoDBストレージエンジンのみサポートbinlog̲format= ROW固定Deadlock detectionじゃなくてLocal certification failureなんだっ⼿⽻先
116/133
Galera Cluster
ローカルノードにトランザクションを展開1. COMMIT前にGalera Cacheに書き込み2. Galera Cacheのプロパゲート3. 他ノードからACK4. ローカルノードでCOMMIT完了5. 他ノードでCOMMIT6.
117/133
Percona XtraBackup
レプリケーション︖グローバルロックをかけてLSNを取り出すa. テーブルスペースファイルのコピーb. コピー中、InnoDBログをスキャンして、開始時以降のLSNのログを全て記録する
c.
コピーしたテーブルスペースにコピーしたログファイルを適⽤する(クラッシュリカバリー相当)
d.
これができるってことは頑張ればInnoDBログのストリームによるレプリケーションもできるんじゃ…︖俺はがんばらない-
118/133
mysqlbinlog -R –stop-never –raw
マスターから⾒るとI/Oスレッドに⾒えるmysqlbinlog -Rにバイナリーログの末尾まで⾏っても終了しない--stop-never
オプションを付けbinlogデコードをかけずにそのままファイルに落とす--raw
オプションを付けるとバイナリーログのストリームバックアップができます
119/133
Facebook MySQLのmysqlbinlog
semisyncスレーブになれるようなオプションが拡張されてるマスターとスレーブは複数の違うDCに配置されてるらしくてマスターと同じDCにバイナリーログ保管⽤のmysqlbinlogさんがいるDC間はAsync, DC内(mysqlbinlog)はSemisyncにすることで、レイテンシーとバイナリーログの保全を両⽅担保Yoshinori Matsunobuʼs blog: Semi-Synchronous Replication at Facebook
120/133
Binlog Events
I/Oスレッド相当の実装とbinlogデコーダーの実装ライブラリー地雷が埋まってるらしい。-MySQL Binlog Events でストリーム処理してみた #MySQLUC15
-
Labs にある”Hadoop Applier”はこれを使った実装に書き換えるとか書き換えないとか
121/133
InnoDB Memcached Plugin
122/133
InnoDB Memcached Plugin
innodb_api_enable_binlogを有効にするとbinlog吐けるイベントは全てRBRがんばれば「トランザクション対応永続化分散memcached」が出来るかも知れない
innodb_api_trx_levelのデフォルトはREAD-UNCOMMITTEDなので要変更
-
innodb_api_bk_commit_intervalのデフォルト5秒も変えた⽅が良さそう
-
123/133
さあみなさんお待ちかね
124/133
「NOW関数は現在時刻を返さない」の謎に迫る
125/133
NOW関数
NOW() は、ステートメントが実⾏を開始する時刻を⽰
す定数時間を返します。
さらに、SET TIMESTAMP ステートメントによっ
て、 ..snip.. タイムスタンプをゼロ以外の値に設定する
と、後続の NOW() が起動されるたびに、その値が返さ
れます。
http://dev.mysql.com/doc/refman/5.6/ja/date-and-time-functions.html#function̲now
126/133
NOW関数
mysql56> SELECT NOW();2016-02-18 19:37:18
mysql56> SELECT NOW();2016-02-18 19:37:19
mysql56> SET timestamp= 1455791802.172832;
mysql56> SELECT NOW();2016-02-18 19:36:42
mysql56> SELECT NOW();2016-02-18 19:36:42
127/133
NOW関数とバイナリーログ
安全でないと⾒なされない非決定的関数。 これらの関
数は決定的ではありませんが、ロギングおよびレプリケ
ー シ ョ ン目 的の場 合は安 全と し て処 理さ れ ま す:
CONNECTION̲ID()、CURDATE()、CURRENT̲DATE
()、CURRENT̲TIME()、CURRENT̲TIMESTAMP()、
CURTIME()、LAST̲INSERT̲ID()、LOCALTIME()、
LOCALTIMESTAMP()、NOW()、UNIX̲TIMESTAMP
( )、U T C ̲ D A T E ( )、U T C ̲ T I M E ( )、および
UTC̲TIMESTAMP()。
http://dev.mysql.com/doc/refman/5.6/ja/replication-rbr-safe-unsafe.html
128/133
NOW関数とバイナリーログ
# at 4381#160212 17:23:20 server id 1056 end_log_pos 4466 Query thread_id=43 exec_time=0 error_code=0SET TIMESTAMP=1455265400/*!*/;BEGIN/*!*/;# at 4466#160212 17:23:20 server id 1056 end_log_pos 4710 Query thread_id=43 exec_time=0 error_code=0SET TIMESTAMP=1455265400/*!*/;INSERT INTO t1 VALUES (1691643253,'JBbKOfG7O3qp66Aa5yY8QelD4WL77eKIpneDJnCbq2Oo0QM6Pm6jOrxLDASCpgXWS1cnooMXjeJj5ahMCnxTXuqkxIxmyK8QLk52PJZ8ykjvAP9N46sbtiKq09SqTc')/*!*/;# at 4710#160212 17:23:20 server id 1056 end_log_pos 4737 Xid = 943518COMMIT/*!*/;
129/133
レプリケーション でしこたま痛い目を⾒た の先⼈ イル
カ㌠から130/133
これからレプリケーション で痛い目を⾒る でできることが増えていくゾウ㌠へ
131/133
楽しいよ︕︕1
132/133
Questions and/or
Suggestions?133/133