目的
TDSQL-C for MySQLの管理とメンテナンスを規範化し、操作ミスによるTDSQL-C for MySQLの利用不可などの影響を引き起こす可能性を回避します。
データベース開発者にSQLを合理的に記述するように指導することで、TDSQL-C for MySQLの最適なパフォーマンスを発揮します。
権限管理の規範
TDSQL-C for MySQLの安定性とセキュリティを考慮し、TDSQL-C for MySQLはsuper、shutdown、file権限を制限しています。そのため、TDSQL-C for MySQLでsetステートメントを実行する場合、以下のようなエラーが発生することがあります。
解決方法:setを使用して関連パラメータを変更する必要がある場合、コンソールのクラスタ管理ページにあるパラメータ設定機能を使用して行うことができます。
必要最小限の権限を付与することを原則とし、一般的なアプリケーションにはDML(SELECT、UPDATE、INSERT、DELETE)権限のみを付与すれば十分です。
権限付与対象の最小化原則に基づき、一般的なアプリケーションアクセスユーザーにはデータベースレベルで権限を付与します。
アクセス権限を付与する際は特定のIPまたはIP範囲のみを許可し、コンソールでセキュリティグループを設定して制限できます。セキュリティグループの設定は必ずコンソールの指示に従って操作してください。公衆ネットワーク経由のアクセスでセキュリティグループを設定する場合、関連する全てのエグレスIPを必ず許可してください。
管理アカウントと開発アカウントを分離します。
日常操作の規範
注意事項
脆弱なパスワードの使用を禁止し、データベースインスタンスのセキュリティを向上させます。
同一アカウント、同じリージョン、かつ同じVPC内のマシンである必要があります。
コンソールからダウンロードしたbinlogログをローカルで解析する場合、クライアントのMySQLバージョンがTDSQL-C for MySQLクラスタのバージョンと一致している必要があります。バージョンが異なる場合、文字化けが発生する可能性があるため、mysqlbinlogの3.4以上のバージョンの使用を推奨します。
コンソール上で内部ネットワーク経由でCVMにコールドバックアップファイルをダウンロードする場合、urlを引用符で囲んでください。そうしないと404エラーが発生します。
推奨事項
業務ピーク時のオンラインddl操作は避けてください。使用可能なツールについては、pt-online-schema-changeを参照してください。
業務ピーク時のデータ一括操作はできるだけ避け、業務閑散期に分割して実施することをお勧めします。
一つのインスタンスで複数の業務を実行することはできるだけ避けてください。結合度が高すぎると、業務間で相互に影響を与えるリスクが存在します。
トランザクションの自動コミットを無効にし、オンライン操作ではbegin;を先に実行する習慣を身につけることをお勧めします。これにより誤操作によるデータ損失リスクを低減できます。万が一の誤操作時にはTDSQL-C for MySQLのリストア機能が利用可能です。関連テーブルが複数データベースやテーブルを跨ぐロジックを含まない場合、高速リストアまたは超高速リストアを使用することで迅速なデータ復旧が可能です。リストアで新しく生成されるデータベース/テーブル名はデフォルトで元の名称_bakとなります。
業務でプロモーション活動などを行う場合は、事前にリソースを見積もり、インスタンスの最適化を実施してください。需要が大きい場合には、担当のサービスマネージャーに速やかにご連絡ください。
データベースとテーブルの設計基準
注意事項
TDSQL-C for MySQL バージョン5.7以降ではMyISAMエンジンとMemoryエンジンをサポートしていません。Memoryエンジンが必要な場合、クラウドデータベースRedisまたはMemcachedの使用を推奨します。自社構築データベースをDTSを通じてTDSQL-C for MySQLに移行する場合、MyISAMエンジンは自動的にInnoDBエンジンに変換されます。
オートインクリメント列が存在するテーブルでは、当該列に少なくとも1つの独立したインデックス、またはオートインクリメント列を先頭とする複合インデックスを設定するべきです。
row_formatは非fixedであることを保証する必要があります。
各テーブルには主キーが必須です。適切な列が主キーとして選べない場合でも、無意味な列を追加して主キーとする必要があります。MySQLの第一正規形基準では、InnoDBのセカンダリインデックスのリーフノードは主キー値のコピーを保存します。ディスクスペースを節約し効率を向上させるため、自動増加する短い列を主キーとして使用することをお勧めします。binlog_formatがrowに設定されている環境で、主キーなしにデータを一括削除すると、深刻なマスタースレーブ遅延を引き起こす可能性があります。
フィールドはできるだけNOT NULLでデフォルト値を設定してください。NULLはSQL開発に多くの問題を引き起こし、インデックスが使用されなくなる可能性があります。NULL値の計算を行う場合、IS NULLとIS NOT NULLでのみ判定できます。
推奨事項
業務シナリオ分析とデータアクセス(データベースの読み書きQPS、TPS、ストレージスペースなどを含む)の予測を通じて、データベースの使用リソースを合理的に計画できます。または、コンソールのTCOPインターフェースでTDSQL-C for MySQLインスタンスの各種監視を設定することも可能です。
同一カテゴリの業務に属するテーブルは同一データベースに配置し、異なる業務のテーブルが同一データベースを共有することをできるだけ避けるべきです。プログラム内で複数データベースにまたがる結合操作を実行することも極力回避してください。この操作は後続のクイックリストアにも影響を及ぼす可能性があります。
文字セットは統一してutf8mb4を使用し、文字化けリスクを低減してください。一部の複雑な漢字や絵文字はutf8mb4でなければ正常に表示されません。文字セットの変更は変更後に作成されるテーブルにのみ適用されるため、TDSQL-C for MySQLを新規購入する際はutf8mb4を選択することをお勧めします。
小数フィールドにはdecimal型の使用を推奨します。floatやdoubleでは精度が不十分です。特に金銭関連の業務では、必ずdecimalを使用する必要があります。
データベースでtext/blobを使用して長文、バイナリデータ、画像、ファイルなどのコンテンツを保存することはできるだけ避け、これらのデータはローカルディスクファイルとして保存し、データベースにはその索引情報のみを保存してください。
外部キーの使用はできるだけ避け、アプリケーション層で外部キーのロジックを実装することを推奨します。外部キーとカスケード更新は高同時実行環境には適さず、挿入性能を低下させ、大規模同時実行下ではデッドロックを引き起こしやすいためです。
業務ロジックとデータストレージの結合度を低減します。データベースはデータ保存を主目的とし、業務ロジックはできるだけアプリケーション層で実装してください。ストアドプロシージャ、トリガー、関数、イベント、ビューなどの高度な機能の使用は可能な限り控えることを推奨します。これらの機能は移植性や拡張性に劣ります。インスタンスにこのようなオブジェクトが存在する場合、デフォルトでdefinerを設定しないことをお勧めします。移行アカウントとdefinerの不一致による移行失敗を回避するためです。
短期間で業務が大規模なレベルに達しない場合は、パーティションテーブルの使用を禁止することを推奨します。パーティションテーブルは主にアーカイブ管理に使用され、物流業界やEC業界の注文テーブルで多く採用されています。ただし、パーティションテーブルには性能向上効果はなく、業務クエリの80%以上がパーティションフィールドを使用する場合に限り有効です。
読み取り負荷が大きく、かつ整合性の要求が低い(データの秒単位の遅延が許容される)ビジネスシナリオでは、読み取り専用インスタンスを購入して読み書き分離戦略を実現することをお勧めします。
インデックス設計指針
注意事項
更新頻度が非常に高く識別度が低いカラムにインデックスを作成することは禁止します。レコード更新はB+ツリーを変更するため、更新頻度の高いフィールドにインデックスを作成するとデータベースのパフォーマンスが大幅に低下します。
複合インデックスを作成する際は、識別度が最も高い列をインデックスの左端に配置します。例:select xxx where a = x and b = x;の場合、aとbで複合インデックスを作成するとき、aの識別度が高いならidx_ab(a,b)を構築します。非等号と等号の混合条件が存在する場合、等号条件の列を前に配置する必要があります。例:where a xxx and b = xxxの場合、aの識別度が高くても、インデックスが適用されないためbをインデックスの先頭に配置しなければなりません。
推奨事項
単一テーブルのインデックス数は5個以内にし、単一インデックス内のフィールド数は5個以内にすることを推奨します。多すぎるとフィルタリング効果が得られず、インデックスもストレージを占有し、管理にもリソースを消費します。
ビジネスでSQLフィルタリングが最も頻繁に使用され、かつカーディナリティ値が比較的高い列を選択してインデックスを作成してください。ビジネスSQLで使用されない列へのインデックス作成は無意味です。フィールドの一意性が高いほどカーディナリティ値が高くなり、インデックスのフィルタリング効果も向上します。一般的に、インデックス列のカーディナリティが全レコードの10%未満の場合、これは非効率なインデックスと見なされます(例:性別フィールド)。
varcharフィールドにインデックスを作成する際は、インデックス長を指定することを推奨します。列全体に直接インデックスを作成する必要はありません。通常varchar列は長いため、一定の長さを指定すれば十分な識別度が得られます。列全体にインデックスを作成すると重くなり、インデックス保守のコストが増大します。インデックスの識別度はcount(distinct left(列名, インデックス長))/count(*)で確認できます。
冗長なインデックスを避けてください。2つのインデックス(a,b)と(a)が同時に存在する場合、(a)は冗長インデックス(redundant index)に該当します。クエリのフィルタ条件がa列の場合、(a,b)インデックスで十分であり、単独で(a)インデックスを作成する必要はありません。
IOオーバーヘッドを削減するためにカバリングインデックスを適切に活用してください。InnoDBでは、セカンダリインデックスのリーフノードには自身のキー値と主キー値のみが保存されます。SQLクエリがインデックス列でも主キーでもない列を取得する場合、そのインデックスを使用すると対応する主キーを先に見つけ、その後主キーに基づいて必要な列を検索します。これをテーブル戻り(リターンアクセス)と呼び、追加のIOオーバーヘッドが発生します。この問題を解決するためにカバリングインデックスを利用できます。例:select a,b from xxx where a = xxxの場合、aが主キーでなければ、aとbの2列で複合インデックスを作成すればテーブル戻りが発生しません。
SQLコーディング規約
注意事項
UPDATEおよびDELETE操作ではLIMITを使用せず、必ずWHEREによる正確なマッチングを行ってください。LIMITはランダムに作用するため、このような操作はデータ不整合を引き起こす可能性があります。
禁止使用INSERT INTO t_xxx VALUES(xxx)。挿入する列属性を明示的に指定する必要があり、テーブル構造の変更によるデータ不整合を防止します。
SQL文で最も一般的なインデックス無効化を引き起こす状況には注意が必要です:
暗黙の型変換。例えば、インデックスaの型がvarcharである場合、SQL文が where a = 1; と記述されると、varcharがintに変換されます。
インデックス列に対して数学計算や関数などの操作を行うこと。例えば、関数を使用して日付列の書式設定処理を行う場合などです。
join列の文字セットが統一されていません。
複数列のソート順序が一致しない問題。例えば、インデックスが(a,b)である場合、SQL文が order by a b desclike のようになっている場合です。
あいまい検索を使用する場合、文字列型のxxx%パターンでは一部のインデックスが使用されますが、それ以外のケースではインデックスが使用されません。
否定クエリ(not、!=、not in など)を使用しています。
推奨事項
必要なものだけを取得し、select *の使用を避けることで、以下の問題を回避します:
カバリングインデックスが使用できず、テーブルアクセスが発生し、I/Oが増加します。
追加のメモリ負荷が発生し、大量のコールドデータがinnodb_buffer_pool_sizeに流入することで、クエリのヒット率が低下します。
追加のネットワーク転送オーバーヘッド。
大規模トランザクションの使用は可能な限り避け、大規模トランザクションを小規模トランザクションに分割することが推奨されます。これによりマスター/スレーブ間の遅延を回避できます。
ビジネスコード内のトランザクションはタイムリーにコミットし、不要なロック待ちを避けること。
複数テーブルの結合を控え、大規模テーブルの結合は禁止します。2つのテーブルを結合する場合は、小さいテーブルを駆動テーブルにし、結合列の文字セットを統一し、かつすべての列にインデックスが作成されている必要があります。
LIMIT句を用いたページングの最適化について。LIMIT 80000,10のような操作では80010件のレコードを取得した後、最後の10件を返すため、データベースへの負荷が高くなります。最初のレコード位置を特定してからページングすることを推奨します。例:SELECT * FROM test WHERE id >= ( SELECT sql_no_cache id FROM test order by id LIMIT 80000,1 ) LIMIT 10 ;
SQL文の多層サブクエリのネストは避けてください。MySQL 5.5以前のクエリオプティマイザはinをexistsに変更するため、インデックスが無効になります。外部テーブルが大きい場合、パフォーマンスが大幅に低下します。
説明:
上記の状況は完全に避けるのは難しいため、推奨されるソリューションは、このような条件を主要なフィルタ条件として使用せず、インデックスが効く主要なフィルタ条件の後に配置することです。そうすれば問題ありません。
監視上でフルテーブルスキャンの量が比較的多いことが確認された場合、コンソールのパラメータ設定でlog_queries_not_using_indexesを有効にし、後ほどスローログファイルをダウンロードして分析できます。ただし、スローログの急増を防ぐため、長時間有効にしないでください。
業務リリース前には必要なSQL監査を行い、日常の運用保守では定期的にスロークエリログをダウンロードして対象を絞った最適化を行う必要があります。