この記事では、データベースをパーティション分割するときにデータの背後にあるセマンティクスを使用する方法を学習します。これにより、アプリケーションのパフォーマンスを大幅に向上させることができます。そして、最も重要なことは、パーティション化基準を独自のアプリケーションドメインに合わせて調整する必要があることに気付くでしょう。
私はスタートアップと協力して、スポーツの専門家が意思決定を行い、データを調査するためのWebアプリを開発しました。このアプリケーションはあらゆるスポーツをサポートしていますが、私たちはヨーロッパに拠点を置いています。ヨーロッパ人はサッカーが大好きです。世界中で毎日プレイされる何百ものゲームのそれぞれには、何千もの行があります。わずか数か月で、アプリのイベントテーブルは5億行に達しました!
サッカーの専門家がどのようにデータを照会しているかを理解することで、データベースをインテリジェントに分割できました。この新しいテーブルの平均時間の改善は、20倍から40倍速くなりました。すべてのクエリの平均時間の改善は5倍から10倍でした。
ここで、このシナリオを詳しく調べて、データベースをパーティション化するときにデータコンテキストを無視できない理由を学びましょう。
コンテキストの提示
私たちのスポーツアプリケーションは、生データと集約データの両方を提供しますが、それを採用した専門家は後者を好みます。基盤となるデータベースには、複数のプロバイダーからのテラバイト単位の複雑で構造化されていない異種データが含まれています。したがって、最大の課題は、信頼性が高く、高速で、探索しやすいデータベースを設計することでした。
アプリケーションドメイン
この業界では、多くのプロバイダーがクライアントに最も重要なサッカーゲームのイベントへのアクセスを提供しています。具体的には、ゴール、アシスト、イエローカード、パスなど、ゲーム中に起こったことに関連するデータを提供します。このデータを含むテーブルは、私たちが処理しなければならなかったテーブルの中で群を抜いて最大です。
VPSの仕様、テクノロジー、アーキテクチャ
私のチームは、最も重要なデータ探索機能を提供するバックエンドアプリケーションを開発してきました。プログラミング言語としてJVM(Java仮想マシン)上で実行されるKotlin v1.6、フレームワークとしてSpring Boot 2.5.3、ORM(オブジェクトリレーショナルマッピング)としてHibernate5.4.32.Finalを採用しました。このテクノロジースタックを選択した主な理由は、速度が最も重要なビジネス要件の1つであるためです。そのため、大量のマルチスレッド処理を活用できるテクノロジーが必要でしたが、SpringBootは信頼できるソリューションであることがわかりました。
Dokkuが管理するDockerコンテナを介して、バックエンドを16GB8CPUVPSにデプロイしました。最大15GBのRAMを使用できます。これは、1GBのRAMがRedisベースのキャッシングシステム専用であるためです。パフォーマンスを改善し、繰り返しの操作によるバックエンドの過負荷を回避するために追加しました。
データベースとテーブルの構造
データベースに関しては、MySQL 8を選択することにしました。現在、8GBおよび2 CPU VPSが、最大200の同時接続をサポートするデータベースサーバーをホストしています。通信のオーバーヘッドを回避するために、バックエンドアプリケーションとデータベースは同じサーバーファーム内にあります。重複を避け、パフォーマンスを考慮してデータベース構造を設計しました。プロバイダーから受け取ったデータを変換するための一貫した構造が必要だったため、リレーショナルデータベースを採用することにしました。このようにして、スポーツデータを標準化し、探索してエンドユーザーに提示しやすくします。
データベースには、執筆時点で数百のテーブルが含まれていますが、NDAに署名したため、すべてを表示することはできません。幸いなことに、これから表示するデータコンテキストベースのパーティションを採用することになった理由を完全に分析するには、1つのテーブルで十分です。本当の課題は、Eventsテーブルで重いクエリを実行し始めたときに発生しました。しかし、それに飛び込む前に、イベントテーブルがどのように見えるかを見てみましょう:
ご覧のとおり、多くの列は含まれていませんが、機密保持のために一部の列を省略しなければならなかったことを覚えておいてください。しかし、本当に ここで重要なのはparameterId
およびgameId
列。これらの2つの外部キーを使用して、パラメーターのタイプ(ゴール、イエローカード、パス、ペナルティなど)とそれが発生したゲームを選択します。
パフォーマンスの問題
イベントテーブルは、わずか数か月で5億行に達しました。このブログ投稿ですでに詳細に説明したように、主な問題は、低速のINクエリを使用して集計操作を実行する必要があることです。これは、ゲーム中に何が起こるかはそれほど重要ではないためです。代わりに、スポーツの専門家は、集約されたデータを分析して傾向を見つけ、それに基づいて決定を下したいと考えています。
また、一般的にシーズン全体または過去5〜10のゲームを分析しますが、ユーザーは特定のゲームを分析から除外したい場合がよくあります。これは、結果を二極化するために、ゲームのプレイが特に不十分または適切であることを望んでいないためです。可能なすべての組み合わせでこれを行う必要があるため、集計データを事前に生成することはできません。これは実行不可能です。そのため、すべてのデータを保存して、その場で集計する必要があります。
パフォーマンスの問題を理解する
それでは、私たちが直面しなければならなかったパフォーマンスの問題につながった中心的な側面に飛び込みましょう。
百万行のテーブルは遅い
何億もの行を含むテーブルを扱ったことがある場合は、それらが本質的に遅いことをご存知でしょう。このような大きなテーブルでJOINを実行することすら考えられません。それでも、妥当な時間でSELECTクエリを実行できます。これは、これらのクエリに単純なWHERE条件が含まれる場合に特に当てはまります。一方、集計関数やIN句を使用すると、非常に遅くなります。このような場合、最大80秒かかる可能性がありますが、これは単純に長すぎます。
インデックスだけでは不十分です
パフォーマンスを向上させるために、いくつかのインデックスを定義することにしました。これは、パフォーマンスの問題の解決策を見つけるための最初のアプローチでした。しかし、残念ながら、これは別の問題につながりました。インデックスには時間とスペースがかかります。これは一般的に重要ではありませんが、そのような大きなテーブルを扱う場合はそうではありません。最も一般的なクエリに基づいて複雑なインデックスを定義するには、数時間とGBのスペースが必要であることが判明しました。また、インデックスは役に立ちますが、魔法ではありません。
ソリューションとしてのデータコンテキストベースのデータベースパーティショニング
カスタム定義のインデックスではパフォーマンスの問題を解決できなかったため、新しいアプローチを試すことにしました。私たちは他の専門家と話し合い、解決策をオンラインで探し、同様のシナリオに基づいた記事を読み、最終的にデータベースのパーティション分割が従うべき正しいアプローチであると判断しました。
従来のパーティショニングが適切なアプローチではない理由
最大のテーブルをすべて分割する前に、MySQLの公式ドキュメントと興味深い記事の両方でこのトピックを学習しました。これが進むべき道であることに全員が同意しましたが、特定のアプリケーションドメインを考慮せずにパーティショニングを適用することは間違いであることに気づきました。具体的には、データベースを分割するときに適切な基準を見つけることがいかに重要であるかを理解しました。パーティショニングの専門家の中には、従来のアプローチは行数でパーティショニングすることであると教えてくれました。しかし、それよりもインテリジェントで効率的なものを見つけたかったのです。
アプリケーションドメインを調べて、パーティショニング基準を見つけます
アプリケーションドメインを分析し、ユーザーにインタビューすることで、重要な教訓を学びました。スポーツの専門家は、同じ競技会のゲームから集約されたデータを分析する傾向があります。たとえば、サッカーの試合は、リーグ、トーナメント、またはトロフィーを獲得できるシングルマッチの場合があります。何千もの異なる大会があります。ヨーロッパで最も重要なものは、チャンピオンズリーグ、プレミアリーグ、リーガ、セリエA、ブンデスリーガ、エレディビジー、リガ1、プリメイラリガです。
これは、ユーザーがさまざまな競争からのデータを考慮することはめったにないことを意味します。また、季節ごとにデータを調査することを好みます。言い換えれば、彼らは特定の季節に行われるスポーツ競技によって表される文脈を離れることはめったにありません。私たちのデータベース構造は、この概念をSeasonCompetition
というテーブルで表現しました。 、その目標は、競争を特定のシーズンに関連付けることです。そのため、大きなテーブルを特定のSeasonCompetition
に関連するサブテーブルに分割するのが適切なアプローチであることがわかりました。 インスタンス。
具体的には、これらの新しいテーブルに次の名前形式を定義しました:<tableName>_<seasonCompetitionId>
。
したがって、SeasonCompetition
に100行ある場合 テーブルでは、大きなEvents
を分割する必要があります 小さいEvents_1
へのテーブル 、Events_2
、…、Events_100
テーブル。私たちの分析に基づくと、このアプローチは、まれなケースではいくらかのオーバーヘッドを導入しますが、平均的なケースではかなりのパフォーマンスの向上につながります。
最も一般的なクエリと基準を一致させる
この複雑で潜在的にリターンのない操作を実行するためのスクリプトをコーディングして起動する前に、バックエンドアプリケーションによって実行される最も一般的なクエリを調べて、調査を検証しました。しかしそうすることで、クエリの大部分がSeasonCompetition内でプレイされるゲームのみに関係していることがわかりました。これは私たちが正しいことを私たちに確信させました。そのため、データベース内のすべての大きなテーブルを、定義したばかりのアプローチでパーティション化しました。
SELECT AVG('value') as 'value', SUM('minutes') as 'minutes'
FROM 'Events'
WHERE 'parameterId' = 15 AND 'gameId' IN(223,241,245,212,201,299,187,304,187,205)
GROUP BY 'teamId'
それでは、この決定の長所と短所を調べてみましょう。
長所
- 最大50万行を含むテーブルでクエリを実行すると、特に集計クエリに関しては、5億行のテーブルで実行するよりもはるかにパフォーマンスが向上します。
- テーブルが小さいほど、管理と更新が簡単です。列またはインデックスを追加することは、時間とスペースの点で以前と比較することさえできません。さらに、各
SeasonCompetition
は異なり、異なる分析が必要です。その結果、特別な列とインデックスが必要になる場合があり、前述のパーティション分割により、これを簡単に処理できます。 - プロバイダーが一部のデータを修正する場合があります。これにより、削除クエリと更新クエリを実行する必要があります。これは、このような小さなテーブルでは非常に高速です。さらに、それらは常に特定の
SeasonCompetition
の一部のゲームにのみ関係します 、したがって、今は1つのテーブルのみを操作する必要があります。
短所
- これらのサブテーブルでクエリを実行する前に、
seasonCompetitionId
を知っておく必要があります。 興味のあるゲームに関連付けられています。これは、seasonCompetitionId
が原因です。 テーブル名には値が使用されます。したがって、バックエンドは、分析中のゲームを調べてクエリを実行する前に、この情報を取得する必要があります。これは、わずかなオーバーヘッドを表しています。 - クエリに多くの
SeasonCompetitions
を含む一連のゲームが含まれる場合 、バックエンドアプリケーションは各サブテーブルでクエリを実行する必要があります。したがって、これらの場合、データベースレベルでデータを集約することはできなくなり、アプリケーションレベルで集約する必要があります。これにより、バックエンドロジックが複雑になります。同時に、これらのクエリを並行して実行できます。また、取得したデータを効率的かつ並行して集約できます。 - 数千のテーブルを含むデータベースの管理は簡単ではなく、クライアントでの調査が難しい場合があります。同様に、各テーブルに新しい列を追加したり、既存の列を更新したりするのは面倒で、カスタムスクリプトが必要です。
パフォーマンスに対するデータコンテキストベースのパーティショニングの影響
ここで、新しいパーティションデータベースでクエリを実行するときに達成される時間の改善を見てみましょう。
- 平均的な場合の時間の改善(1つの
SeasonCompetition
のみを含むクエリ ):20倍から40倍 - 一般的な場合の時間の改善(1つ以上の
SeasonCompetitions
を含むクエリ ):5倍から10倍
最終的な考え
データベースのパーティション分割は、特に大規模なデータベースでパフォーマンスを向上させるための優れた方法であることは間違いありません。ただし、特定のアプリケーションドメインを考慮せずにそれを行うと、間違いであるか、非効率的なソリューションにつながる可能性があります。代わりに、専門家やユーザーにインタビューし、最も実行されたクエリを調べてドメインを調査することは、非常に効率的なパーティショニング基準を考案するために重要です。この記事では、これを行う方法を示し、実際のケーススタディを通じてそのようなアプローチの結果を示しました。