TiDB のベスト プラクティス

このドキュメントでは、オンライン分析処理 (OLAP) およびオンライン トランザクション処理 (OLTP) シナリオの SQL の使用と最適化のヒント、特に TiDB に固有の最適化オプションを含む、TiDB の使用に関するベスト プラクティスをまとめています。

このドキュメントを読む前に、TiDB の技術原理を紹介する 3 つのブログ投稿を読むことをお勧めします。

序文

データベースは汎用インフラストラクチャ システムです。開発時にはさまざまなユーザーシナリオを検討し、具体的なビジネスシーンで実際の状況に合わせてデータのパラメーターや使い方を変更することが重要です。

TiDB は、MySQL プロトコルおよび構文と互換性のある分散データベースです。しかし、分散ストレージとトランザクションの内部実装とサポートにより、TiDB の使用方法は MySQL とは異なります。

基本概念

ベスト プラクティスは、その実装原則と密接に関連しています。 Raftコンセンサス アルゴリズム、分散トランザクション、データ シャーディング、負荷分散、SQL から Key-Value (KV) へのマッピング ソリューション、セカンダリ インデックスの実装方法、分散実行など、基本的なメカニズムのいくつかを学習することをお勧めします。エンジン。

このセクションでは、これらの概念について説明します。詳細については、 PingCAP のブログ投稿を参照してください。

Raft

Raftは、強力な一貫性を備えたデータ複製を保証するコンセンサス アルゴリズムです。最レイヤーでは、TiDB はRaftを使用してデータを複製します。 TiDB は、成功の結果を返す前に、大部分のレプリカにデータを書き込みます。このようにして、いくつかのレプリカが失われたとしても、システムには最新のデータが残っています。たとえば、レプリカが 3 つある場合、データが 2 つのレプリカに書き込まれるまで、システムは成功の結果を返しません。レプリカが失われるたびに、残りの 2 つのレプリカの少なくとも 1 つに最新のデータがあります。

3 つのレプリカを格納するには、Source-Replica のレプリケーションと比較してRaftの方が効率的です。 Raftの書き込みレイテンシーは、最も遅いレプリカではなく、2 つの最も速いレプリカに依存します。したがって、 Raftレプリケーションを使用することで、地理的に分散された複数のアクティブなデータ センターの実装が可能になります。 3 つのデータ センターが 2 つのサイトに分散している典型的なシナリオでは、データの整合性を保証するために、TiDB は 3 つのデータ センターすべてに書き込むのではなく、ローカル データ センターと近いデータ センターにデータを正常に書き込むだけで済みます。ただし、これは、クロスデータセンター展開がどのシナリオでも実装できることを意味するものではありません。書き込むデータ量が多い場合、データセンター間の帯域幅とレイテンシーが重要な要素になります。書き込み速度が帯域幅を超えているか、レイテンシーが高すぎる場合、 Raftレプリケーション メカニズムは依然としてうまく機能しません。

分散トランザクション

TiDB は完全な分散トランザクションを提供し、モデルはGoogle パーコレーターに基づいていくつかの最適化を行います。このドキュメントでは、次の機能について説明します。

  • 楽観的な取引モデル

    TiDB の楽観的なトランザクション モデルは、コミット フェーズまで競合を検出しません。競合がある場合は、トランザクションを再試行する必要があります。ただし、再試行前の操作は無効であり、繰り返す必要があるため、競合が深刻な場合、このモデルは非効率的です。

    データベースがカウンターとして使用されているとします。アクセスの同時実行数が多いと、深刻な競合が発生し、複数回の再試行やタイムアウトが発生する可能性があります。したがって、競合が深刻なシナリオでは、ペシミスティック トランザクション モードを使用するか、Redis にカウンターを配置するなど、システムアーキテクチャレベルで問題を解決することをお勧めします。それにもかかわらず、アクセスの競合がそれほど深刻でない場合、楽観的なトランザクション モデルは効率的です。

  • ペシミスティック トランザクション モード

    TiDB では、ペシミスティック トランザクション モードは MySQL とほぼ同じ動作をします。トランザクションは実行フェーズ中にロックを適用します。これにより、競合状況での再試行が回避され、成功率が高くなります。悲観的ロックを適用することで、 SELECT FOR UPDATEを使用して事前にデータをロックすることもできます。

    ただし、アプリケーション シナリオの競合が少ない場合は、楽観的なトランザクション モデルのパフォーマンスが向上します。

  • 取引サイズ制限

    分散トランザクションは 2 フェーズ コミットを実行する必要があり、最レイヤーはRaftレプリケーションを実行するため、トランザクションが非常に大きい場合、コミット プロセスが非常に遅くなり、後続のRaftレプリケーション プロセスがスタックします。この問題を回避するために、トランザクション サイズが制限されています。

    • トランザクションは 5,000 SQL ステートメントに制限されます (デフォルト)
    • 各 Key-Value エントリは 6 MB 以下です (デフォルト)
    • Key-Value エントリの合計サイズは 10 GB 以下です。

    Google クラウド スパナにも同様の制限があります。

データシャーディング

TiKV は、キーの範囲に応じて最下層のデータを自動的に分割します。各リージョンはキーの範囲であり、左が閉じて右が開いている間隔[StartKey, EndKey)です。リージョンは自動的に 2 つに分割されます。

負荷分散

Placement Driver (PD) は、TiKV クラスター全体のステータスに従って、クラスターの負荷を分散します。スケジューリングの単位はリージョンであり、ロジックは PD によって構成された戦略です。

KV の SQL

TiDB は、SQL 構造を Key-Value 構造に自動的にマップします。詳細については、 TiDB 内部 (II) - コンピューティングを参照してください。

簡単に言えば、TiDB は次の操作を実行します。

  • データの行は、キーと値のペアにマップされます。キーにはTableIDがプレフィックスとして付けられ、行 ID がサフィックスとして付けられます。
  • インデックスは、キーと値のペアとしてマップされます。キーにはTableID+IndexIDがプレフィックスとして付けられ、インデックス値がサフィックスとして付けられます。

同じテーブル内のデータまたはインデックスには、同じプレフィックスがあります。これらの Key-Value は、TiKV のキー スペース内の隣接する位置にあります。そのため、書き込むデータ量が多く、すべてが 1 つのテーブルに書き込まれる場合、書き込みホットスポットが作成されます。連続して書き込まれるデータの一部のインデックス値も連続している場合 (たとえば、 update timeのように時間とともに増加するフィールド)、状況はさらに悪化し、いくつかの書き込みホットスポットが作成され、システム全体のボトルネックになります。

同様に、集中した狭い範囲 (たとえば、連続する数万行または数十万行のデータ) からすべてのデータを読み取る場合、データのアクセス ホットスポットが発生する可能性があります。

二次索引

TiDB は、グローバル インデックスでもある完全なセカンダリ インデックスをサポートしています。多くのクエリは、インデックスによって最適化できます。したがって、アプリケーションが副次索引をうまく利用することが重要です。

MySQL の豊富な経験は、TiDB にも当てはまります。 TiDB には独自の機能があることに注意してください。 TiDB でセカンダリ インデックスを使用する場合の注意事項を次に示します。

  • 副次索引が多いほど良いですか?

    セカンダリ インデックスはクエリを高速化できますが、インデックスの追加には副作用があります。前のセクションでは、インデックスのストレージ モデルを紹介しました。追加のインデックスごとに、行を挿入するときに Key-Value が 1 つ増えます。したがって、インデックスが多いほど、書き込み速度が遅くなり、より多くのスペースが必要になります。

    さらに、インデックスが多すぎるとオプティマイザーの実行時間に影響し、不適切なインデックスはオプティマイザーを誤解させます。したがって、セカンダリ インデックスを増やしてもパフォーマンスが向上するわけではありません。

  • インデックスを作成する列はどれですか?

    前述のように、インデックスは重要ですが、インデックスの数は適切でなければなりません。アプリケーションの特性に応じて、適切なインデックスを作成する必要があります。原則として、パフォーマンスを向上させるには、クエリに関係する列にインデックスを作成する必要があります。インデックスを作成する必要がある状況は次のとおりです。

    • 高度な差別化を持つ列の場合、フィルター処理された行はインデックスによって大幅に削減されます。
    • 複数のクエリ基準がある場合は、複合インデックスを選択できます。複合インデックスの前に、同等の条件を持つ列を配置することに注意してください。

    たとえば、一般的に使用されるクエリがselect * from t where c1 = 10 and c2 = 100 and c3 > 10の場合、複合インデックスIndex cidx (c1, c2, c3)を作成できます。このように、クエリ条件を使用してインデックス プレフィックスを作成し、スキャンすることができます。

  • インデックスによるクエリとテーブルの直接スキャンの違い

    TiDB はグローバル インデックスを実装しているため、テーブルのインデックスとデータは必ずしも同じデータ シャーディング上にあるとは限りません。インデックスを介してクエリを実行する場合、まずインデックスをスキャンして対応する行 ID を取得し、次にその行 ID を使用してデータを取得する必要があります。したがって、この方法には 2 つのネットワーク要求が含まれ、一定のパフォーマンス オーバーヘッドがあります。

    クエリに多数の行が含まれる場合、インデックスのスキャンは同時に進行します。結果の最初のバッチが返されると、テーブルのデータの取得を続行できます。したがって、これは並列 + パイプライン モデルです。 2 つのアクセスによってオーバーヘッドが発生しますが、レイテンシーは高くありません。

    次の 2 つの条件では、2 回アクセスの問題は発生しません。

    • インデックスの列は、クエリの要件を既に満たしています。 tテーブルのc列にインデックスがあり、クエリがselect c from t where c > 10;であるとします。この時点で、インデックスにアクセスすると、必要なすべてのデータを取得できます。この状況をCovering Indexと呼びます。ただし、クエリのパフォーマンスを重視する場合は、フィルター処理する必要はないがクエリ結果で返す必要がある列の一部をインデックスに入れ、複合インデックスを作成できます。例としてselect c1, c2 from t where c1 > 10;を取り上げます。このクエリを最適化するには、複合インデックスIndex c12 (c1, c2)を作成します。

    • テーブルの主キーは整数です。この場合、TiDB は主キーの値を行 ID として使用します。したがって、クエリ条件が主キーにある場合、行 ID の範囲を直接構築し、テーブル データをスキャンして、結果を取得できます。

  • クエリの同時実行

    データは多くのリージョンに分散されているため、クエリは TiDB で同時に実行されます。ただし、多くのシステム リソースを消費する場合、デフォルトの同時実行性は高くありません。さらに、OLTP クエリは通常、大量のデータを必要とせず、同時実行性が低くても十分です。ただし、OLAP クエリの場合、同時実行性は高く、TiDB は次のシステム変数を使用してクエリの同時実行性を変更します。

    • tidb_distsql_scan_concurrency :

      テーブルとインデックス データのスキャンを含む、データのスキャンの同時実行性。

    • tidb_index_lookup_size :

      テーブル データにアクセスする前にインデックスにアクセスして行 ID を取得する必要がある場合は、行 ID のバッチを単一の要求として使用してテーブル データにアクセスします。このパラメーターは、バッチのサイズを設定します。バッチが大きいとレイテンシーが増加しますが、小さいバッチはより多くのクエリにつながる可能性があります。このパラメーターの適切なサイズは、クエリに含まれるデータの量に関連しています。通常、変更は必要ありません。

    • tidb_index_lookup_concurrency :

      テーブル データにアクセスする前に行 ID を取得するためにインデックスにアクセスする必要がある場合、行 ID を介して毎回データを取得する同時実行性は、このパラメーターによって変更されます。

  • インデックスを使用して結果の順序を確認する

    インデックスを使用して、データをフィルター処理または並べ替えることができます。まず、インデックス順に従って行 ID を取得します。次に、行 ID の返された順序に従って、行の内容を返します。このように、返された結果はインデックス列に従って並べ替えられます。インデックスをスキャンして行を取得するモデルは、並列 + パイプラインであることは前述しました。インデックスの順序に従って行が返される場合、2 つのクエリ間の同時実行性が高くても、レイテンシーは短縮されません。したがって、同時実行性はデフォルトでは低くなりますが、 tidb_index_serial_scan_concurrency変数を使用して変更できます。

  • 逆索引スキャン

    TiDB は、通常のスキャンよりも 20% 遅い速度で、昇順のインデックスを逆順にスキャンすることをサポートしています。データが頻繁に変更され、バージョンが多すぎると、パフォーマンスのオーバーヘッドが高くなる可能性があります。リバース インデックス スキャンをできるだけ避けることをお勧めします。

シナリオとプラクティス

最後のセクションでは、TiDB のいくつかの基本的な実装メカニズムと、それらが使用に与える影響について説明しました。このセクションでは、デプロイからアプリケーションの使用まで、具体的な使用シナリオと運用方法を紹介します。

展開

導入前にソフトウェアとハードウェアの要件をお読みください。

TiUPを使用して TiDB クラスターをデプロイすることをお勧めします。このツールは、クラスター全体をデプロイ、停止、破棄、およびアップグレードできるため、非常に便利です。 TiDB クラスターを手動で展開することはお勧めしません。これは、後で保守およびアップグレードするのが面倒になる可能性があります。

データのインポート

インポート プロセス中の書き込みパフォーマンスを向上させるために、 TiKV メモリ パラメータのパフォーマンスの調整で説明されているように TiKV のパラメーターを調整できます。

書く

前述のように、TiDB は Key-Valueレイヤーで 1 つのトランザクションのサイズを制限します。 SQLレイヤーに関しては、データの行が Key-Value エントリにマップされます。追加のインデックスごとに、もう 1 つの Key-Value エントリが追加されます。

ノート:

トランザクションのサイズ制限を設定するときは、TiDB エンコーディングのオーバーヘッドと余分なトランザクション キーを考慮する必要があります。各トランザクションの行数は 200 未満、1 行のデータ サイズは 100 KB 未満にすることをお勧めします。そうしないと、パフォーマンスが低下します。

ステートメントをバッチに分割するか、ステートメントがINSERTUPDATE 、またはDELETEかどうかにかかわらず、ステートメントに制限を追加することをお勧めします。

大量のデータを削除する場合は、 Delete from t where xx limit 5000;を使用することをお勧めします。ループを介して削除し、ループを終了する条件としてAffected Rows == 0を使用します。

一度に削除する必要があるデータの量が多い場合、各削除が逆方向にトラバースするため、このループ メソッドはますます遅くなります。以前のデータを削除した後、多くの削除済みフラグが短期間残り (その後、ガベージ コレクションによってすべてクリアされます)、次のDELETEのステートメントに影響します。可能であれば、 WHERE条件を絞り込むことをお勧めします。 2017-05-26のすべてのデータを削除する必要があると仮定すると、次のステートメントを使用できます。

for i from 0 to 23: while affected_rows > 0: delete from t where insert_time >= i:00:00 and insert_time < (i+1):00:00 limit 5000; affected_rows = select affected_rows()

この疑似コードは、データの巨大なチャンクを小さなものに分割してから削除することを意味し、前のステートメントが後のステートメントに影響を与えないようにしDelete

クエリ

クエリの要件と特定のステートメントについては、 システム変数を参照してください。

SETステートメントとヒントによるJoin演算子の選択により、SQL 実行の同時実行性を制御できます。

さらに、MySQL の標準インデックス選択、ヒント構文を使用するか、オプティマイザを制御してUse Indexまでインデックスを選択することもできIgnore Index hint

アプリケーション シナリオに OLTP と OLAP の両方のワークロードがある場合、OLTP 要求と OLAP 要求を異なる TiDB サーバーに送信して、OLTP に対する OLAP の影響を軽減できます。 OLAP ワークロードを処理する TiDBサーバーには、高性能ハードウェア (たとえば、より多くのプロセッサ コアとより大きなメモリ) を備えたマシンを使用することをお勧めします。

OLTP と OLAP のワークロードを完全に分離するには、TiFlash で OLAP アプリケーションを実行することをお勧めします。 TiFlash は、OLAP ワークロードで優れたパフォーマンスを発揮するコラム型ストレージ エンジンです。 TiFlash は、ストレージレイヤーで物理的な分離を実現し、読み取りの一貫性を保証します。

モニタリングとログ

モニタリング メトリックは、システムのステータスを知るための最良の方法です。 TiDB クラスターと共に監視システムをデプロイすることをお勧めします。

TiDB はグラファナ + プロメテウスを使用してシステム ステータスを監視します。 TiUP を使用して TiDB をデプロイすると、監視システムが自動的にデプロイおよび構成されます。

監視システムには多くの項目があり、その大部分は TiDB 開発者向けです。ソース コードに関する深い知識がなくても、これらの項目を理解する必要はありません。アプリケーションまたはシステム キー コンポーネントの状態に関連するいくつかの項目が選択され、ユーザー用に別のoverviewパネルに配置されます。

監視に加えて、システム ログを表示することもできます。 TiDB の 3 つのコンポーネントである tidb-server、tikv-server、および pd-server には、それぞれ--log-fileつのパラメーターがあります。クラスタの起動時にこのパラメータが設定されている場合、ログはパラメータで設定されたファイルに保存され、ログ ファイルは毎日自動的にアーカイブされます。 --log-fileパラメータが設定されていない場合、ログはstderrに出力されます。

TiDB 4.0 以降、TiDB は使いやすさを向上させるためにTiDB ダッシュボードの UI を提供します。ブラウザでhttp://PDIP:{PD_IP}:{PD_PORT}/ダッシュボードにアクセスすると、TiDB ダッシュボードにアクセスできます。 TiDB ダッシュボードは、クラスター ステータスの表示、パフォーマンス分析、トラフィックの視覚化、クラスター診断、ログ検索などの機能を提供します。

ドキュメンテーション

システムについて学び、問題を解決する最善の方法は、そのドキュメントを読み、実装の原則を理解することです。

TiDB には、中国語と英語の両方で多数の公式ドキュメントがあります。問題が発生した場合は、 FAQTiDBクラスタトラブルシューティング ガイドから開始できます。課題リストを検索したり、 GitHub の TiDB リポジトリで課題を作成したりすることもできます。

TiDB には、便利な移行ツールも多数あります。詳細は移行ツールの概要を参照してください。

TiDB の技術的な詳細に関するその他の記事については、 PingCAP公式ブログサイトを参照してください。

TiDB の最適なシナリオ

TiDB は、次のシナリオに適しています。

  • スタンドアロン データベースのデータ ボリュームが大きすぎる
  • シャーディングをしたくない
  • アクセス モードには明確なホットスポットがありません
  • トランザクション、強整合性、災害復旧が必要
  • リアルタイムのハイブリッド トランザクション/分析処理 (HTAP) 分析を行い、ストレージ リンクを削減したいと考えています。