運用に必要な知識
MySQLのプロセスアーキテクチャ
- MySQLはシングルプロセス・マルチスレッドモデルのデータベース。
- psコマンドのauxや-elfなどのオプションではmysqldのプロセスは1つしか見えない。
- 内部的には複数のスレッドを使い分けている
- スレッドは、フォアグランドスレッドとバックグランドスレッドに分けられる
- フォアグランドスレッドは外部のクライアントと1対1で紐づく
- 1つの接続が1つのスレッドになり、接続が切断されるまでの同一のスレッドが処理をする
- 現在のコネクション(= スレッド)の情報はperformance_schemaのthreadsテーブルまたはSHOW PROCESSLISTで確認できる
- 実行時のオーバーヘッドや情報の粒度などからperformance_schema.threadsテーブルの方が推奨されている
- フォアグランドスレッドは外部のクライアントと1対1で紐づく
MySQLのパラメータ
- MySQLにはたくさんのパラメータ(設定可能項目)があり、分類ごとにいくつかの特性を持っている。
- オンラインで変更が可能なパラメータと設定変更にMySQLの再起動が必要なパラメータがある。
- my.cnfの読み込み順序
- オンラインで変更したパラメータを永続化するにはSET PERSISTステートメントが使える
MySQLのバージョン情報
MySQLのバージョンは、SELECT VERSION(); や SHOW VARIABLES LIKE 'version'; などで調べることができる。
運用設計
バージョン選定
MySQL 8.0系列は同一リリース系列内でも非互換性があるため、バージョンの選定は慎重にされなくてはならない。 クライアントやライブラリのバージョンはMySQLサーバと同じかそれ以上であることが必須。MySQL 8.0系列ではバージョン番号の信仰に伴って新機能が追加されてしまうため、 その機能やその周辺に新規にバグが内在する可能性がある。
現時点で確実に進められることは次の通り。
- 導入時点での最新バージョンを選び動作確認をする
- 導入以後のアップグレードに備えてSQLレベルでの非互換性を検出できるように回帰テストを作成する
- アップグレードをする際にはクライアントのバージョンを先にあげる
バックアップ設計
- バックアップの間隔
- バックアップにはフルバックアップと増分バックアップがあり、両方揃って初めて任意の時点のデータを復元できる
- フルバックアップの頻度
- 増分バックアップの頻度
- リストアにかかる時間の見積もり
- バックアップの運用計画に関するパラメータ
- 増分バックアップの計画に大切なパラメータとしてbinlog_expire_logs_secondsという値がある。
- これは、現在のバイナリログがスイッチした時点で過去の古いバイナリログを削除するためのパラメータ。
- バイナリログがスイッチするタイミングは、max_binlog_sizeというパラメータで決定される。
- 追記されていったバイナリログのサイズがこのパラメータで設定されて値よりも大きくなると、バイナリログのスイッチが発生する。
- log_slave_updates
- デフォルトはONで、スレーブはマスターからレプリケーションで受け取った更新差分を自身(= スレーブ)のバイナリログに出力する。これによりマスターとスレーブで二重にバイナリログが保管され、バイナリログの喪失への耐性が上がる。
- 増分バックアップの計画に大切なパラメータとしてbinlog_expire_logs_secondsという値がある。
モニタリング計画
MySQLが機能を提供しているかどうかを確認する「死活監視」と、中長期的にリソースの利用状況がどうなっていくかを確認する「リソース監視」の2つが必要。
- 死活監視
- リソース監視
- 直接的なエラー以外の、レスポンス速度低下の原因究明や性能限界の計測に役立つ
- 「問題発生以前」、「問題発生中」、「問題終息後」の3時点のリソースを比較するためにも継続的なデータの蓄積は必須
- MySQLの各種ログ
実際の運用作業
パラメータチューニング
すこしくらいパラメータを変更したところでパフォーマンスが劇的によくなることはない。事前に綿密な計算のもと、パラメータを算出するよりも運用中に効果を測定しながら継続的にパラメータの最適値に近づけていくことが大切。
- 事前に計算しておくべきパラメータ
- innodb_buffer_pool_sizeは重要なオプション
- InnoDBのすべてのテーブルで利用されるバッファプール(メモリ上に配置)のサイズを指定する
- SELECTのときにキャッシュとして使われるのみならず、InnoDBのすべての操作を一度バッファプールを経由して処理されるため、あらゆるDMLやDDLの速度に影響を及ぼす。
- 物理メモリが小さい(例えば4GB以下)ときは50%程度、十分大きい(例えば12GB以上)ときは80%などと単純な割合ではなく、InnoDBバッファプール以外にどれくらいメモリを残しておくべきかから算出するのがよい
- innodb_buffer_pool_sizeはオンラインで変更できる(MySQL 5.6とそれ以前は再起動が必要だった)
- innodb_buffer_pool_sizeは重要なオプション
クエリチューニング
MySQLのパフォーマンス問題の多くはクエリ単位に分解して考えることがベター。単一のクエリが遅い場合はもちろん、複数のクエリがロックやリソースの競合を起こしている場合でも、個々のクエリのスピードをあげてやることで全体としてのパフォーマンを改善できる パラメータのチューニングがせいぜい数倍程度にしかパフォーマンスの問題を改善できないのに対し、正しくチューニングされたクエリは数百倍から数千倍のパフォーマンスが出る。
- インデックスの仕組み
- 最もコストパフォーマンスが高いのは「インデックスを使わせること」
- INSERT/UPDATE/DELETEといった更新処理はインデックスも同時にメンテナンスしなければならないため、更新の負荷はインデックスがない場合よりも大きくなる
- 特定のクエリに対してベストなインデックスであっても、クエリの形が変わると同じインデックスがベストとは限らない
- 複数カラムからなるインデックスはカラムの順番で効率が変わることがある、インデックスの大部分を操作しなければならないアクセスパターンは効率がよくない
- インデックスによるWHERE句のチューニング
- カードをめくって目視が1番コストの高い動作になるため、可能な限りこれを避けるようにインデックスを追加していく
- どのくらいカードをめくって目視が発生する見積もりであるかは、実行したいクエリの先頭にEXPLAINキーワードをつけることで確認できる
- rowsがカードをめくって目視する見積もりを表しており、filteredがカードをめくってチェックしたうち、何%程度が実際にマッチする行である見積もりを表している
- ExtraのUsing whereがインデックスだけではWHERE句を解決しきれないので目視のフィルタリングが必要であることを表している
- 不要なインデックスを作ってしまうことを重要視するよりも、不要になったインデックスを安全に検出して削除することを継続指定実践していいくのがよい
- WHERE句のチューニングによる改善すべきクエリの特定
- インデックスによる ORDER BY ... LIMIT のチューニング
- MySQLのインデックスはWHERE句による絞り込み以外に、ORDER BY句に対する効率化にも利用可能
- ORDER BY ... LIMIT のための「優先度付きキュー」によるソート
- ORDER BY ... LIMIT のチューニングによる改善すべきクエリの特定
- ORDER BY句をインデックスで解決できているか、いなかを判断するためにもスロークエリログまたはperformance_schemaを使う
- クエリの書き換えによるチューニング
- アプリケーション側の発行するクエリを変えることによって高速化する
- ループ構造による繰り返しクエリ
- 大量データフェッチへの対処
- 大量データのフェッチによってI/Oを枯渇させる
- アプリケーション側でフェッチする範囲を狭めてループで複数回実行することが負荷の軽減につながる
- LIMIT句とOFFSET句
- ORDER BY句とLIMIT句を併用することで、ソートに優先度付きキューを使った最適化やORDER BY狙いのキーを使った追加ソートを不要とする方法がある。
- OFFSET句がカーソル操作をする対象は生成済みの結果セットに対してのみ
- 巨大な1つのクエリを分割するときにLIMIT句とOFFSET句を併用した場合、個々のクエリで削減できるのはネットワーク転送量だけ
- LIMIT句とORDER BY句の組み合わせは、パフォーマンス上の問題を引き起こしやすく、ORDER BY句の考慮漏れなどで正しい結果セットを得られなくなる可能性もあるため、注意が必要
- ALTER TABLE実行時のTIPS
- MySQLは以前のバージョンからオンラインALTER TABLEと呼ばれる機能を備えている
- オンラインの意味はALTER TABLE中に読み書きが可能という意味
- オンラインALTER TABLEがサポートされているかどうかを確認する
- ALTER TABLEとレプリケーション遅延
- マスターで1時間かかったクエリはスレーブでも1時間かかること、マルチスレッドスレープを有効にしてもALTER TABLEは追い越せない
- マスターでALTER TABLEに要した時間と同じだけスレーブでへの反映が遅延する
- レプリケーションの遅延が許容できない場合、オンラインALTER TABLEであってもアプリケーションを停止してからメンテナンスとしてALTER TABLEを実行する必要がある
- マスターで1時間かかったクエリはスレーブでも1時間かかること、マルチスレッドスレープを有効にしてもALTER TABLEは追い越せない
- 安全なインデックス削除のための不可視インデックス
- MySQLは以前のバージョンからオンラインALTER TABLEと呼ばれる機能を備えている
ユーザ管理
- グローバル権限はNG
MySQLのさまざまな状態をみる
STATUSコマンド
- 動作中のMySQLサーバーや、接続に関する基本的な情報を表示
- Current user
- 接続ユーザ
- Threads
- 現在MySQLサーバーに接続中のスレッド数
- Question
- MySQLサーバーが起動してから発生したクエリーの数
- Slow queries
- MySQLサーバーが起動してから発生したスロークエリー数
- Opens
- MySQLサーバーが起動してからオープンされたクテーブルの数
- Open tables
- 現在オープンされているテーブルの数
SHOWコマンド
- データベースオブジェクトの情報やさまざまな状態を表示する
- SHOW DATABASES
- 現在接続中のユーザーがアクセス可能なデータベースの一覧を表示
- SHOW TABLES
- カレントデータベース内に存在するテーブル及びビューの一覧を表示
- SHOW CREATE TABLE table_name
- 指定したテーブルのCREATE文を返す
- SHOW COLUMNS FROM table_name
- 指定したテーブルまたはビューを構成するカラムの情報を表示
- DESC table_name でも同じ結果を表示
サーバーや接続の動作オプションを見る
SHOW GLOBAL VARIABLES, SHOW SESSION VARIABLES(SHOW VARIABLES)
MySQLシステム変数の値を確認したいときには、SHOW VARIABLESを使用する。 セッション変数は@@SESSION、グローバル変数は@@GLOBALそれぞれの要素として確認できる。
動作中の状態を見る
- SHOW STATUSコマンド
- MySQLサーバーの実行状態に関する値を表示。メモリやディスクの使用状態、接続数に関する情報などが含まれている。
- SHOW PROCESSLIST
- その時点で稼働中のプロセスを一覧表示
発生したエラーや警告を見る
最後に実行したクエリーで発生したエラーや警告の内容を確認するには、SHOW ERRORS, SHOW WARNINGS命令が便利
レプリケーション
レプリケーションは「複数のMySQLサーバのデータを同期する」ための仕組みで、3.23 以前から20年以上にわかって提供し続けている枯れた機能
レプリケーションの仕組み
MySQLのレプリケーションの構成では「マスターとスレーブに同一のデータを用意する」「」マスターに更新SQLを実行する」「マスターからスレーブに更新SQLを転送する」「スレーブが転送された更新SQLの内容をリプレイする」ことでデータを同期する。 「まったく同じデータを持ったMySQLに対して」「まったく同じSQLを実行すれば」「実行後のデータはまったく同じになる」という「結果整合性モデル」でレプリケーションは動作する。
(非同期)レプリケーションのデータ同期
古くからあるこの方式では、「クライアントから更新SQLを受け杖ける(唯一の)サーバー」を「マスター」、「マスターから間接的に更新SQLの内容を受け取るサーバー」を「スレーブ」と呼び、その関係は非対称。 マスターにはBinlog Dumpと呼ばれるスレッドが待機しており、このスレッドがバイナリログの更新を検知してスレーブにバイナリログイベントを送信する。Binlog Dumpスレッドはマスターと接続しているスレーブに対応して1対1で常駐する。 Binlog Dumpスレッドに対応する形でスレーブには「(スレーブ)I/O」が起動しており、この2つのスレッドがTCP通信でコネクションを開いている。コネクションからバイナリログイベントが送信されてくると、それをI/Oスレッドが受信してリレーログと呼ばれるファイルに記録する。 スレーブにはさらに「(スレーブ)SQL」スレッドが常駐しており、リレーログの更新を待ち受けている。このSQLスレッドがリレーログからバイナリログイベントを取り出し、スレーブ上のデータに対してリプレイすることで、マスターとスレーブのデータが同一となる。 このリプレイ時にスレーブにはスレーブサーバー常に新たにバイナリログを記録する。
- 非同期レプリケーションと準同期レプリケーション
- マルチソースレプリケーション
- 多段レプリケーション構成
- グループレプリケーションのデータ同期
レプリケーションはデータの複製のための機能。マスターへの更新はバイナリログから始まる形でスレーブに非同期に差分更新される。