[MYSQL 記事] mysql パフォーマンス最適化の概要

序文

MYSQL のパフォーマンス チューニングに関して言えば、ほとんどの場合、達成したいのはクエリを高速化することです。クエリ アクションは多くのリンクで構成されており、各リンクに時間がかかります。クエリにかかる時間を短縮したい場合は、各リンクから開始する必要があります。

画像-20211006202806875

MYSQLのSQL文の実行処理についてよく分からない場合は、こちらの記事をご覧ください: 【MySQL】Select文の原理を詳しく解説

構成の最適化

最初のリンクはクライアントをサーバーに接続することです。この接続ではどのようなパフォーマンスの問題が発生する可能性がありますか? サーバー側の接続数が不足しており、アプリケーションが接続を取得できない可能性があります。たとえば、Mysql: error 1040: Too many connectionsエラー。接続数不足は 2 つの側面から解決できます。

サーバーから

サーバー側で利用可能な接続の数を増やすことができます。

複数のアプリケーションまたは同時にデータベースにアクセスする多数のリクエストがあり、接続数が十分でない場合は、次のことが可能です。

  1. 構成パラメータを変更して、使用可能な接続の数を増やし、max_connections のサイズを変更します。
show variables like 'max_connections'; -- 修改最大连接数,当有多个应用连接的时候
  1. あるいは、非アクティブな接続を適時に解放します。対話型クライアントと非対話型クライアントのデフォルトのタイムアウト期間は 28800 秒、つまり 8 時間ですが、この値を減らすことができます。
show global variables like 'wait_timeout'; --及时释放不活动的连接,注意不要释放连接池还在使用的连接

クライアントからの

サーバーから取得するコネクション数を削減できます。このとき、接続の再利用を実現するために接続プールを導入できます。

ORM レベル (MyBatis には接続プールが付属しています)、または専用の接続プール ツール (Ali's Druid、Spring Boot 2.x バージョンのデフォルトの接続プール、Hikari、昔ながらの DBCP および C3P0) を使用します。

サーバー側の接続数とクライアント側の接続プールのサイズを適切に設定することに加えて、クライアント側とデータベース サーバー側の間の接続数を減らすには、他にどのような解決策があるでしょうか? アーキテクチャの観点から最適化の詳細について話しましょう。

アーキテクチャの最適化

キャッシュ

アプリケーション システムの同時実行性が非常に大きい場合、キャッシュがないと 2 つの問題が発生します。1 つはデータベースに多大な負荷をもたらすことです。一方、アプリケーションレベルでは、データの動作速度にも影響します。この問題は、Redis などのサードパーティのキャッシュ サービスを使用して解決できます。

独立したキャッシュ サービスを実行することは、アーキテクチャ レベルでの最適化です。

単一データベース サーバーの読み取りおよび書き込みの負荷を軽減するために、アーキテクチャ レベルで他にどのような最適化手段を実行できるでしょうか?

マスター/スレーブ レプリケーション

単一のデータベース サービスではアクセス要件を満たせない場合は、データベース クラスター ソリューションを実行できます。

クラスタは必然的に問題、つまり異なるノード間のデータの一貫性の問題に直面します。複数のデータベース ノードが同時に読み書きされる場合、すべてのノードのデータの一貫性を保つにはどうすればよいでしょうか?

このとき、レプリケーション技術(レプリケーション)を利用する必要があり、複製されたノードをマスター、複製されたノードをスレーブと呼びます。

マスター/スレーブ レプリケーションはどのように実装されますか? 前に述べたように、update ステートメントは論理ログである binlog を記録します。この binlog を使用して、スレーブ サーバーはマスター サーバーの binlog ファイルを取得し、内部の SQL ステートメントを解析してスレーブ サーバー上で実行して、マスターとスレーブのデータの一貫性を保ちます。

理解できない場合は、binlogこの記事を読んでください: [MYSQL] mysql の REDO ログと binlog を理解するための 1 つの記事

主に、binlog スレッド、I/O スレッド、SQL スレッドの 3 つのスレッドが関与します。

  • binlog スレッド: マスターサーバー上のデータ変更をバイナリログ (バイナリログ) に書き込む責任を負います。
  • I/O スレッド: マスター サーバーからバイナリ ログを読み取り、スレーブ サーバーのリレー ログ (リレー ログ) に書き込む役割を果たします。
  • SQL スレッド: リレー ログの読み取り、マスター サーバーが実行したデータ変更の解析、およびスレーブ サーバーでの再生 (リプレイ) を担当します。

以下の図は、マスター/スレーブ レプリケーションに関与する 3 つのスレッドを示しています。

画像-20220223092908103

読み取りと書き込みの分離

マスター/スレーブ レプリケーション スキームを実装した後は、データをマスター ノードに書き込むだけで、読み取りリクエストをスレーブ ノードに分散できます。このスキームを読み取りと書き込みの分離と呼びます。

画像-20220223093043275

読み取りと書き込みを分離することでパフォーマンスが向上する理由は次のとおりです。

  • マスターサーバーとスレーブサーバーはそれぞれの読み取りと書き込みを担当するため、ロックの競合が大幅に軽減されます。
  • スレーブ サーバーは MyISAM を使用してクエリのパフォーマンスを向上させ、システム オーバーヘッドを節約できます。
  • 冗長性を高め、可用性を向上させます。

読み取りと書き込みを分離すると、データベース サーバーのアクセス圧力をある程度軽減できますが、マスターとスレーブのデータの一貫性には特別な注意を払う必要があります。

マスター/スレーブ レプリケーションを行った後でも、単一のマスター ノードまたは単一のテーブルに格納されているデータが大きすぎる場合 (たとえば、テーブルに数億のデータがある場合)、単一テーブルのクエリ パフォーマンスは依然として低下します。単一テーブルをさらに改善する必要がある データベース ノードのデータは、サブデータベースとサブテーブルに分割されます。

サブライブラリとサブテーブル

モールシステムを例に、データベースがどのように段階的に進化するかを説明します。

単一アプリケーション 単一データベース

上の図に示すように、モール システムには、ホームページのポータル テンプレート、ユーザー モジュール、注文モジュール、在庫モジュールなどが含まれます。すべてのモジュールはデータベースを共有し、通常、データベースには多くのテーブルがあります。ユーザーの数は多くないため、このようなアーキテクチャは初期段階では十分に適用できます。

複数のアプリケーションの単一データベース

この一連のシステムは継続的に反復的に更新されるため、コードの量はますます増大し、アーキテクチャはますます肥大化し、システム アクセスへの圧力が徐々に増大しているため、システムを分割することが不可欠です。業務を円滑に進めるため、システムアーキテクチャの再構築も何段階にもわたって行われます。

第 1 段階では、モール システムの単一アーキテクチャが、ポータル サービス、ユーザー サービス、注文サービス、在庫サービスなどの機能モジュールに応じてサブサービスに分割されます。

上の図に示すように、複数のサービスがデータベースを共有します。これは、基礎となるデータベース アクセス ロジックを変更せずに維持し、影響を最小限に抑えることが目的です。

複数のアプリケーションと複数のデータベース

ビジネス推進の強化に伴い、いよいよデータベースがボトルネックとなり、複数のサービスでデータベースを共有することは基本的に不可能となっているのが現状です。各サービスに関連するテーブルを分離し、実際にはサブデータベースである別のデータベースを構築する必要があります。

単一のデータベースでサポートされる同時実行の量には制限があるため、データベースを複数のデータベースに分割すると、サービス間の競合がなくなり、サービスのパフォーマンスが向上します。

上図のように、大きなデータから複数の小さなデータベースを分離し、それぞれのサービスをデータベースに対応させるのが、システムがある程度発展した段階で必要となるデータベースの分割作業です。

マイクロサービスアーキテクチャでも同様で、データベースを分割せずにアプリケーションだけを分割すると根本的な問題は解決できず、システム全体がボトルネックになりやすくなります。

サブテーブル

システムが高速開発段階にある場合、モールシステムを例に挙げると、1 日の注文量が数十万件になる可能性があり、データベース内の注文テーブルが非常に急速に増大し、データベースクエリの効率が低下します。ある段階まで成長すると大きく下がります。

そのため、単一テーブルのデータ増加が速すぎる場合、業界ではデータ量が500万を超えていると噂されており、テーブルを検討する必要があります。もちろん、500万は経験値であり、実際の状況に応じて判断できます。

水平分割を例に挙げると、各テーブルは複数のサブテーブルに分割され、複数のサブテーブルが同じデータベース内に存在します。たとえば、次のユーザー テーブルはユーザー 1 テーブルとユーザー 2 テーブルに分割されています。

データベース内のテーブルを複数のサブテーブルに分割すると、単一テーブルのクエリ パフォーマンスの問題はある程度解決できますが、単一データベースのストレージのボトルネックという問題も発生します。

したがって、業界ではサブテーブルを複数のデータベースに分割することがより一般的に使用されています。たとえば、次の図では、ユーザー テーブルが 2 つのサブテーブルに分割されており、2 つのサブテーブルは異なるデータベースに存在します。

テーブルの分割は主に、1 つのテーブルのサイズを削減し、1 つのテーブル内のデータ量によって生じるパフォーマンスの問題を解決するために行われます。

複雑

サブデータベースとサブテーブルは確かに多くの問題を解決しますが、システムに多くの複雑さをもたらします。

データベース間の関連付けクエリ

テーブルが 1 つのデータベースに分割される前は、結合操作を使用して複数のテーブルをクエリ データに簡単に関連付けることができますが、データベースが分割されてテーブルが分割された後は、2 つのテーブルが同じデータベース内に存在しない可能性があります。結合を使用しますか?

それを解決するには、いくつかのオプションがあります。

  1. フィールドの冗長性: 結合操作を回避するために、関連付ける必要があるフィールドをメイン テーブルに配置します。
  2. データの抽象化: ETL などを通じてデータを集約し、新しいテーブルを生成します。
  3. グローバル テーブル: たとえば、いくつかの基本的なテーブルを各データベースに配置できます。
  4. アプリケーション層のアセンブリ: 基本データを見つけ出し、アプリケーションの計算を通じてそれを組み立てます。
分散トランザクション

単一のデータベースはローカル トランザクションで解決できますが、複数のデータベースを使用する場合は分散トランザクションでのみ解決できます。

一般的に使用されるソリューションには、信頼性の高いメッセージ (MQ)、2 フェーズ トランザクション コミット、柔軟なトランザクションなどに基づくソリューションが含まれます。

配布ID

Mysql データベースを使用して、単一データベースおよび単一テーブルの主キーとして ID 自己インクリメントを使用する場合、データベースがテーブルに分割された後は機能せず、ID が重複します。

一般的に使用される分散 ID ソリューションは次のとおりです。

  • グローバルに一意な ID (GUID) を使用します。
  • 各シャードの ID 範囲を指定します。
  • 分散 ID ジェネレーター (Twitter の Snowflake アルゴリズムなど)。
複数のデータソース

サブデータベースのサブテーブルが複数のデータベースまたは複数のサブテーブルからデータを取得する問題に直面した場合、一般的な解決策は、クライアントの適応とプロキシ層の適応です。
業界で一般的に使用されているミドルウェアには次のものがあります。

  1. shardingsphere(前身 sharding-jdbc)
  2. 私の猫

まとめ

データベースに問題が発生した場合は、急いでデータベースを分割したりテーブルを分割したりせず、まず従来の手段を使用して解決できるかどうかを確認してください。

サブデータベースとサブテーブルはシステムに大きな複雑性をもたらすため、絶対に必要な場合を除き、事前に使用しないことはお勧めできません。システムアーキテクトとして、システムを柔軟かつスケーラブルにすることができますが、過剰な設計や過度の設計は避けてください。

クエリのパフォーマンスの最適化

説明付きで分析する

Explain は SELECT クエリー・ステートメントの分析に使用され、開発者は Explain 結果を分析することでクエリー・ステートメントを最適化できます。

より重要なフィールドは次のとおりです。

  • select_type : 単純なクエリ、結合クエリ、サブクエリなどを含むクエリ タイプ。
  • key : 使用するインデックス。
  • rows : スキャンされた行数。

データアクセスの最適化

1. 要求されるデータ量を減らす

  • 必要な列のみを返します。SELECT * ステートメントは使用しないことをお勧めします。
  • 必要な行のみを返す: LIMIT ステートメントを使用して、返されるデータを制限します。
  • 繰り返しクエリされるデータをキャッシュする: キャッシュを使用すると、データベースでのクエリを回避できます。特に、クエリ対象のデータが頻繁に繰り返しクエリされる場合、キャッシュによるクエリ パフォーマンスの向上は非常に明白です。

2. サーバーによってスキャンされる行数を減らす

最も効率的な方法は、インデックスを使用してクエリをカバーすることです。

クエリメソッドのリファクタリング

1.大規模なクエリをセグメント化する

大規模なクエリが一度に実行されると、一度に大量のデータがロックされ、トランザクション ログ全体が占有され、システム リソースが枯渇し、多くの小規模だが重要なクエリがブロックされる可能性があります。

2.大規模な結合クエリを分解する

大規模な結合クエリを各テーブルの単一テーブル クエリに分解し、アプリケーションで関連付けを実行する利点は次のとおりです。

  • キャッシュをより効率的にします。結合クエリの場合、テーブルの 1 つが変更されると、クエリ キャッシュ全体が使用できなくなります。複数のクエリを分解した後、テーブルの 1 つが変更された場合でも、他のテーブルのクエリ キャッシュは引き続き使用できます。
  • 複数の単一テーブル クエリに分解されると、これらの単一テーブル クエリのキャッシュされた結果は他のクエリで使用される可能性が高くなるため、冗長レコードのクエリが削減されます。
  • ロック競合を軽減します。
  • アプリケーション層で接続するとデータベースの分割が容易になり、高いパフォーマンスとスケーラビリティを実現しやすくなります。
  • クエリ自体の効率も向上する可能性があります。たとえば、次の例では、結合クエリの代わりに IN() を使用すると、ID の順序に従って MySQL クエリを作成でき、ランダム結合より効率的である可能性があります。
SELECT * FROM tag
JOIN tag_post ON tag_post.tag_id=tag.id
JOIN post ON tag_post.post_id=post.id
WHERE tag.tag='mysql';
SELECT * FROM tag WHERE tag='mysql';
SELECT * FROM tag_post WHERE tag_id=1234;
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);

インデックスの最適化

1. 列を分ける

クエリを作成する場合、インデックス付きの列を式の一部にしたり、関数のパラメータにすることはできません。そうでない場合、インデックスは使用できません。

たとえば、次のクエリでは、actor_id 列のインデックスを使用できません。

SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;

2. 複数列インデックス

クエリの条件として複数の列を使用する必要がある場合は、複数の単一列インデックスを使用するよりも複数列インデックスを使用する方が適しています。たとえば、次のステートメントでは、actor_id と film_id を複数列インデックスとして設定することをお勧めします。

SELECT film_id, actor_ id FROM sakila.film_actor
WHERE actor_id = 1 AND film_id = 1;

3. インデックス列の順序

最も選択的なインデックス列を最初に配置します。

インデックス選択性とは、レコードの総数に対する一意のインデックス値の比率を指します。最大値は 1 で、この時点で各レコードにはそれに対応する一意のインデックスがあります。選択性が高いほど、各レコードの識別性が高くなり、クエリ効率が高くなります。

たとえば、以下に示す結果では、customer_id は Staff_id よりも選択性が高いため、customer_id 列を複数列インデックスの前に配置することをお勧めします。

SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
COUNT(*)
FROM payment;
   staff_id_selectivity: 0.0001
customer_id_selectivity: 0.0373
               COUNT(*): 16049

4. プレフィックスインデックス

BLOB、TEXT、および VARCHAR タイプの列の場合は、文字の最初の部分のみにインデックスを付けるプレフィックス インデックスを使用する必要があります。

プレフィックス長の選択は、インデックス選択性に応じて決定する必要があります。

5. カバリングインデックス

インデックスには、クエリが必要なすべてのフィールドの値が含まれています。

次のような利点があります。

  • 通常、インデックスはデータ行のサイズよりもはるかに小さいため、インデックスを読み取るだけでデータ アクセスの量を大幅に削減できます。
  • 一部のストレージ エンジン (MyISAM など) はインデックスのみをメモリにキャッシュし、データはオペレーティング システムのキャッシュに依存します。したがって、インデックスのみにアクセスすると、システム コール (通常は時間がかかります) の使用を回避できます。
  • InnoDB エンジンの場合、セカンダリ インデックスでクエリをカバーできる場合は、プライマリ インデックスにアクセスする必要はありません。

mysql インデックスについてあまり詳しくない場合は、関連する知識については、次の 2 つの記事を参照してください。

【MYSQL記事】mysqlインデックスの原理を1つの記事で理解する

[MYSQL の記事] mysql のさまざまなストレージ エンジンでインデックスはどのように実装されますか?

ストレージエンジン

ストレージエンジンの選択

異なるビジネス テーブルに対して異なるストレージ エンジンを選択します。たとえば、MyISAM を使用して、多くの操作を含むビジネス テーブルのクエリと挿入を行います。一時データにはメモリを使用します。InnoDB は、通常の同時大規模更新テーブルに使用されます。

フィールド定義

原則: データを正しく保存できる最小のデータ型を使用します。各列に適切なフィールド タイプを選択します。

整数型

TINYINT、SMALLINT、MEDIUMINT、INT、および BIGINT は、それぞれ 8、16、24、32、および 64 ビットの記憶領域を使用します。一般に、列が小さいほど優れています。INT(11) の数値は、対話型ツールによって表示される文字数を規定するだけであり、保存や計算には意​​味がありません。

文字タイプ

可変長の場合、varchar はより多くのスペースを節約しますが、varchar フィールドの場合、長さを記録するために 1 バイトが必要です。固定長には varchar ではなく char を使用してください。

外部キー、トリガー、ビューを使用しないでください

可読性が低下します。データベースのパフォーマンスに影響します。計算はプログラムに引き渡され、データベースはストレージに集中する必要があります。データの整合性はプログラム内でチェックする必要があります。

大容量ファイルストレージ

画像 (base64 エンコードなど) や大きなファイルを保存するためにデータベースを使用しないでください。

ファイルを NAS に置くと、データベースは URI (相対パス) を保存し、アプリケーションで NAS サーバーのアドレスを構成するだけで済みます。

テーブルの分割またはフィールドの冗長性

あまり使用されないフィールドを分割して、列が多すぎたりデータが多すぎたりしないようにします。

たとえば、業務システムでは送受信したすべてのメッセージを記録する必要があり、メッセージを XML 形式で BLOB またはテキストに保存して、重複を追跡および判断し、テーブルを作成してメッセージを保存できます。

要約する

面接中に「どの次元からデータベースを最適化しますか」という質問に遭遇したら、どう答えますか?

  • SQLとインデックス
  • ストレージ エンジンとテーブルの構造
  • データベーススキーマ
  • MySQLの構成
  • ハードウェアとオペレーティング システム

コード、SQL ステートメント、テーブル定義、スキーマ、構成の最適化に加えて、ビジネス レベルでの最適化も無視できません。いくつかの例を挙げると、次のようになります。

  1. ある年のダブルイレブンで、なぜ余額宝にリチャージすると、残高にボーナス アクティビティ (300 をリチャージして 50 を獲得するなど) が含まれるのでしょうか?

なぜなら、残高や余額宝を使って支払うとローカルまたは内部のデータベースに記録され、銀行カードを使って支払うとインターフェースを呼び出す必要があり、内部データベースを操作する方が確実に速いからです。

  1. 昨年のダブルイレブンでは、なぜ今日以外の早朝の請求書照会を禁止したのでしょうか?

これは、現在の本業を確保するための格下げ措置です。

  1. 近年のダブルイレブンですが、なぜダブルイレブンでは1週間前から価格が決まっているのでしょうか?

販売前の流用。

アプリケーション レベルでは、電流制限や MQ ピーク シェービングの導入など、データベースへの負荷を可能な限り軽減するために最適化するソリューションが他にも多数あります。

なぜ MySQL も使われるのでしょうか? 数千万の同時実行に耐えられる企業もあれば、数百の同時実行に耐えられない企業もあり、使い方が鍵となります。したがって、データベースの使用が遅いということは、データベース自体が遅いということではなく、場合によっては上位層まで最適化する必要があります。

おすすめ

転載: blog.csdn.net/jiang_wang01/article/details/131343977
おすすめ