知らない MySQL 最適化の実践は禁止されています (2)



ここに画像の説明を挿入

余計なことは言わず、ただ料理を提供してください。


11. 結合インデックスを使用する場合は、インデックス列の順序に注意し、通常は左端の一致原則に従います。

テーブル構造: (結合インデックス idxuseridage があり、userId が最初にあり、age が続きます)

CREATE TABLE `user` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `userId` int(11) NOT NULL,  `age` int(11) DEFAULT NULL,  `name` varchar(255) NOT NULL,  PRIMARY KEY (`id`),  KEY `idx_userid_age` (`userId`,`age`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

反例:

select * from user where age = 10;

ここに画像の説明を挿入

正例:

//符合最左匹配原则select * from user where userid=10 and age =10;//符合最左匹配原则select * from user where userid =10;

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-rIFQEWMQ-1689213442322)(img/image-20200416004457692.png)]

理由:

  • (k1, k2, k3) などの結合インデックスを作成する場合、それは 3 つのインデックス (k1)、(k1, k2)、および (k1, k2, k3) を作成するのと同等であり、これが左端のマッチング原則です。
  • 結合インデックスは左端の原則を満たさないため、インデックスは通常失敗しますが、これは Mysql オプティマイザーにも関連しています。

12. クエリを最適化するには、where および order by に関係する列にインデックスを構築し、テーブル全体のスキャンを避けることを検討する必要があります。

反例:

select * from user where address ='深圳' order by age ;

ここに画像の説明を挿入

正例:

添加索引alter table user add index idx_address_age (address,age)

ここに画像の説明を挿入


13. 挿入するデータが多すぎる場合は、バッチ挿入を検討してください。

反例:

for(User u :list){ INSERT into user(name,age) values(#name#,#age#)}

正例:

//一次500批量插入,分批进行
insert into user(name,age) values
<foreach collection="list" item="user" index="index" separator=",">  
(#{user.name},#{user.age})
</foreach>

insert into user(name,age) values("zs",20),("ls",21)

理由:

  • バッチ挿入のパフォーマンスが良好で、時間を節約できます

たとえて言うと、10,000 個のレンガを建物の最上階まで移動する必要がある場合、エレベーターがあり、エレベーターは一度に適切な量のレンガ (最大 500 個) を置くことができます。1 つのレンガを運ぶことを選択できます。または、一度に 500 個のレンガを運ぶことができます。どちらが最も時間がかかると思いますか?


14. 必要に応じて、カバーインデックスを使用します。

インデックスをカバーすると、SQL ステートメントがテーブルに戻る必要がなくなり、インデックスにアクセスするだけで必要なデータをすべて取得できるため、クエリの効率が大幅に向上します。

反例:

// like模糊查询,不走索引了select * from user where userid like '%123%'

ここに画像の説明を挿入

正例:

//id为主键,那么为普通索引,即覆盖索引登场了。select id,name from user where userid like '%123%';

ここに画像の説明を挿入


15.distinct キーワードの使用には注意してください

通常、distinct キーワードは、重複レコードをフィルタリングして重複しないレコードを返すために使用されます。1 つまたは少数のフィールドをクエリする場合に使用すると、クエリに最適化効果がもたらされます。ただし、フィールドの数が多い場合、クエリの効率が大幅に低下します。

反例:

SELECT DISTINCT * from  user;

正例:

select DISTINCT name from user;

理由:

  • distinct を使用したステートメントの CPU 時間と占有時間は、distinct を使用しないステートメントよりも長くなります。多くのフィールドをクエリする場合、distinct を使用すると、データベース エンジンはデータを比較し、重複データを除外しますが、この比較とフィルタリングのプロセスはシステム リソースと CPU 時間を占有します。

16. 冗長および重複したインデックスの削除

反例:

  KEY `idx_userId` (`userId`)    KEY `idx_userId_age` (`userId`,`age`)

正例:

  //删除userId索引,因为组合索引(A,B)相当于创建了(A)和(A,B)索引  KEY `idx_userId_age` (`userId`,`age`)

理由:

  • 重複したインデックスは維持する必要があり、オプティマイザはクエリを最適化するときに重複したインデックスを 1 つずつ考慮する必要があるため、パフォーマンスに影響します。

17. データの量が多い場合は、変更/削除ステートメントを最適化します。

同時に大量のデータを変更または削除しないでください。CPU 使用率が高くなり、他のユーザーのデータベースへのアクセスに影響を与えるためです。

反例:

//一次删除10万或者100万+?delete from user where id <100000;//或者采用单一循环操作,效率低,时间漫长for(User user:list){   delete from user; }

正例:

//分批进行删除,如每次500delete user where id<500delete product where id>=500 and id<1000;

理由:

  • 一度に大量のデータを削除すると、ロック待ちタイムアウト超過のエラーが発生する可能性があるため、一括で操作することを推奨します。

18. where 句で null の代わりにデフォルト値を使用することを検討してください。

反例:

select * from user where age is not null;

ここに画像の説明を挿入

正例:

//设置0为默认值select * from user where age>0;

ここに画像の説明を挿入

理由:

  • is null または is not null を使用するとインデックスが使用されなくなるというわけではなく、mysql のバージョンとクエリのコストに関係します。

mysql オプティマイザは、インデックスを使用するコストがインデックスを使用しないコストよりも高いと判断した場合、必ずインデックスを放棄します。これらの条件は、インデックスが無効になると考えられることがよくあります。実際、それはクエリのコストが原因です!=,>isnull,isnotnull。通常の状況ではこの値は高く、オプティマイザは自動的にインデックスを放棄します。

  • NULL 値をデフォルト値に置き換えると、多くの場合、インデックスを通過できると同時に、式の意味が比較的明確になります。

19. テーブル接続は 5 つを超えないでください

  • リンクされたテーブルが多いほど、コンパイル時間とオーバーヘッドが大きくなります。
  • 接続テーブルを小さな実行に分割すると、読みやすくなります。
  • データを取得するために多くのテーブルを結合する必要がある場合、それは設計が間違っていることを意味します。

20.exist&in の合理的な使用

テーブル A が特定の会社の従業員テーブルを表し、テーブル B が部門テーブルを表しているとします。すべての部門のすべての従業員をクエリするには、次の SQL を簡単に作成できます。

select * from A where deptId in (select deptId from B);

これを書くことは次と同等です。

最初に部門テーブル B をクエリします

BからdeptIdを選択

次に、部門の deptId を使用して、A の従業員をクエリします。

select * from A where A.deptId = B.deptId

次のようなループに抽象化できます。

   List<> resultSet ;   
for(int i=0;i<B.length;i++) {
    
            
  for(int j=0;j<A.length;j++) {
    
          
    if(A[i].id==B[j].id) {
    
               
      resultSet.add(A[i]);            
      break;         
    }      
  }    
}

明らかに、次のように、in を使用するだけでなく、exists を使用して同じクエリ関数を実現することもできます。

select * from A where exists (select 1 from B where A.deptId = B.deptId);

存在するクエリの理解は、まずメインクエリを実行し、データを取得した後にサブクエリに入れて条件検証を行い、その検証結果(真か偽)に応じて、データがそのデータの結果であるかどうかを判断します。メインクエリは保存されます。

次に、これを書くことは次と同等です。

select * from A、最初に A テーブルからループを実行します

select * from B where A.deptId = B.deptId を実行し、B テーブルからループします。

同様に、次のようなサイクルに抽象化できます。

List<> resultSet ;    
for(int i=0;i<A.length;i++) {
    
              
  for(int j=0;j<B.length;j++) {
    
              
    if(A[i].deptId==B[j].deptId) {
    
                 
      resultSet.add(A[i]);             
      break;          
    }      
  }    
}

データベースで最も手間がかかるのは、プログラムとリンクしてリリースすることです。リンクを 2 回行ったとします。そのたびに、数百万回のデータセットクエリを実行し、チェックしてから離れるので、リンクを行うのは 2 回だけですが、逆に、数百万回のリンクを確立し、リンク解放の申請が繰り返されるため、システムは影響を受けますが、それ以上はできません。つまり、mysql 最適化の原則は、小さなテーブルが大きなテーブルを駆動し、小さなデータ セットが大きなデータ セットを駆動するため、パフォーマンスが向上します。

したがって、外側のループが最も小さいものを選択する必要があります。つまり、B のデータ量が A より小さい場合は、B のデータ量が A より大きい場合に使用するのに適しています。選べるものが存在します



ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/m0_60915009/article/details/131718857