MySQL パフォーマンスの最適化 (2) インデックス

最適化手段

  1. テーブル インデックスは完全であるほど良いですか?
  2. 性別の箇条書きが一般的に索引付けされないのはなぜですか?
  3. ID カードを主キーとして使用することが推奨されないのはなぜですか?
  4. xx%、%xx%、%xx などのあいまい一致ではインデックスを使用しますか?
  5. select * が廃止されたのはなぜですか?

準備

create table user_innodb
(
    id int not null primary key,
    username varchar(255) null,
    gender char(1) null,
		phone char(11) null
) ENGINE=INNODB;

create table user_myisam
(
    id int not null primary key,
    username varchar(255) null,
    gender char(1) null,
		phone char(11) null
) ENGINE=myisam;

create table user_memory
(
    id int not null primary key,
    username varchar(255) null,
    gender char(1) null,
		phone char(11) null
) ENGINE=memory;

SET @i = 1;
INSERT INTO user_innodb (id, username,gender, phone)
SELECT @i := @i + 1 AS id,
       CONCAT('user', LPAD(@i, 5, '0')) AS username,
			 IF(FLOOR(RAND() * 2) = 0, '1', '0') AS gender,
       CONCAT('1', LPAD(FLOOR(RAND() * 10000000000), 10, '0')) AS phone
FROM   INFORMATION_SCHEMA.TABLES,
       INFORMATION_SCHEMA.TABLES AS t2
WHERE  @i < 5000000;

select max(id) from user_innodb

ケース

-- 没有索引的查询时间
select * from user_innodb where username = 'huathy'
> OK
> 时间: 5.872s
-- 为username字段加上索引
alter table user_innodb add index idx_user_innodb_name(username);
-- 走索引的name查询时间开销
select * from user_innodb where username = 'huathy'
> OK
> 时间: 0.017s

索引付けの性質

データベース インデックス: クエリの効率を向上させるために、データベース管理システムで並べ替えられたデータ構造。

  • インデックスは列ごとに分類されます: 単一列インデックス、結合インデックス
  • インデックスの種類: 通常、空間、一意の一意のインデックス (空)、主キー インデックス (空でない)、フルテキストのフルテキスト インデックス (大きなテキスト フィールド、単語の分割の効果は中国語では良くない、ES を置き換える)
  • インデックス方式:B+ツリー、ハッシュインデックス
    画像.png

インデックス付きデータ構造

  1. 二分探索の連結リスト構造:二分探索木。
    左側のサブツリーのノードは親ノードよりも小さく、右側のサブツリーのノードは親ノードよりも大きくなっています。

    二分木には極端な状況があり、すべてのノードが親ノードよりも大きい場合、二分木は連鎖リスト構造に退化します。

  2. バランス バイナリ ツリー (AVL Three)
    の左サブツリーと右サブツリーの深さの差の絶対値は1 を超えることはできません。
    左右形→右利き、右利き形→左利き。

画像.png

  1. 多方向バランス検索ツリー (B ツリー) は、
    分割とマージによってバランスを維持します. この分割とマージは、InnoDB ページの分割とマージです.
    キーの順序が間違っていると、ディスクへの保存時に断片化が発生する可能性があります。そうID
    画像.png

4. 多方向平衡探索木の B+ ツリー拡張版
すべてのデータは葉ノードに格納され、葉ノード間に双方向ポインターがあり、リンクされたリスト構造を形成します。

画像.png
アドバンテージ:

  • B ツリーは、AVL ツリーのノードがデータでいっぱいではなく、深すぎるという問題を解決します。
  • ライブラリとテーブルのスキャンのパフォーマンスが向上
  • IO の数は少なくなります。強力なディスク読み取りおよび書き込み機能
  • より強力な選別能力
  • 効率がより安定します

画像.png

MySQL がインデックス データ構造として赤黒木を使用しないのはなぜですか? 赤黒ツリーの目的は、最大深度が最小深度の 2 倍を超えないようにすることです。赤黒木のバランスが悪い。ディスク上のデータ構造には適用されません。メモリを防ぐことができます。

  • ノードは赤または黒に分類されます。
  • ルート ノードは黒である必要があります。
  • リーフ ノードはすべて黒の NULL ノードです。
  • 赤のノードの子は両方とも黒です (2 つの隣接する赤のノードは許可されません)。
  • 任意のノードから開始して、各リーフ ノードへのパスには、同じ数の黒いノードが含まれます。

5. ハッシュ インデックス時間の複雑さは常に O(1)
クエリ速度です。ハッシュされたデータは本質的に順序付けされていません。そのため、値の比較には時間がかかります。ハッシュの衝突は避けられません。
このインデックス タイプは InnoDB では使用できません。しかし、それは他のエンジンで使用できます。メモリエンジンなど。

異なるストレージ エンジンでのインデックス作成の実践

MyIsam (インデックスにはプライマリとセカンダリがなく、MYI ファイルに格納されます)

主キー インデックス

画像.png

その他の指標

画像.png

InnoDB (データはインデックス、インデックスはデータ)

インデックスとデータは 1 つのファイルに格納されます。その B+ ツリーのリーフ ノードは、データを直接格納します。

主キー インデックス - クラスター化インデックス

葉ノードはデータを保存します
画像.png

クラスター化インデックス

インデックス キー値の順序がデータ行の物理的な格納順序と一致している場合、クラスター化インデックスになります。

その他の指標

リーフ ノードには主キーが格納されます。

画像.png
質問: データのプライマリ キーがアドレスではなくセカンダリ インデックスに格納されるのはなぜですか?
データの追加・削除、B+ツリーの分割・統合により、アドレスが変わります。
テーブルに戻る: セカンダリ インデックスをクエリした後、プライマリ キーに従ってテーブル内のデータをクエリする必要があります。図中の一番長い赤線はテーブル復帰動作を示しています。

主キーがない場合はどうなりますか?

公式の回答: MySQL :: MySQL 5.7 リファレンス マニュアル :: 14.6.2.1 クラスター化インデックスとセカンダリ インデックス

主キーのインデックスがある場合は、主キーのインデックスを使用します。主キー インデックスがない場合は、null 以外の一意のインデックスが使用されます。適切な主キーと一意のインデックスがない場合は、非表示の行 ID をインデックスとして使用します。

// 但是我在这里查询的时候,好像提示以下错误信息:
// 1054 - Unknown column '_rowid' in 'field list'
select _rowid from test ;

説明はこちらにあります: https://blog.csdn.net/u011196295/article/details/88030451

主キーを明示的に定義せずにテーブルを作成する場合。

  1. まず、テーブルに空でない整数の一意のインデックスがあるかどうかを判断します。ある場合、その列は主キーです (この時点で select _rowid from table を使用して主キー列をクエリできます)。
  2. 修飾されたものがない場合、6 バイトの主キーが自動的に作成されます (主キーが見つかりません)。

インデックスの作成と使用の原則

インデックスが多いほど良いですか?

いいえ。インデックスはディスク領域を占有し、時間と引き換えに領域を交換します。

列の離散性: count(distinct(column_name)):count(*)

性別と電話のうち、分散度が高いのはどちらですか? 電話には高度な分散があります。
したがって、離散性の低いキーにインデックスを作成する必要はありません。インデックスを通過するときにテーブルに戻る操作があるため、パフォーマンスが低下します。

関節インデックスの左端一致原理

ジョイント インデックスは最初のフィールドから開始する必要があり、中断することはできません。最も問い合わせの多いものを左側に配置することをお勧めします。

alter table user_innodb add index comidx_name_phone(username,phone);

EXPLAIN select * from user_innodb t where t.phone = '13603108202' and t.username='huathy';	-- 使用索引
EXPLAIN select * from user_innodb t where t.username='huathy' and t.phone = '13603108202';	-- 使用索引
EXPLAIN select * from user_innodb t where t.username='huathy';			-- 使用索引
EXPLAIN select * from user_innodb t where t.phone = '13603108202';	-- 不使用索引

画像.png

利用シーン:
ID番号や検査番号など、同時に取得しなければならないデータについては、結合インデックスを利用できます。

冗長インデックス

上記のインデックスを使用して、上記のクエリ用に別のインデックスを作成する必要がありますか。不要です。インデックスは冗長です。

select * from user_innodb t where t.username='huathy';	
alter table user_innodb add index idx_user_innodb_name(username);

カバリングインデックス

クエリ対象の列が使用されているインデックスに既に含まれている場合は、テーブルに戻る必要はありません。これをカバリングインデックスと呼びます。カバリング インデックスは、インデックスを使用する場合です。
カバリングインデックスを使用するかどうかの判断方法: Extra の Using Index である場合は、カバリングインデックスが使用されていることを意味します。

EXPLAIN select username,phone from user_innodb t where  t.username='huathy';	-- 使用覆盖索引
EXPLAIN select username from user_innodb t where t.username='huathy' and t.phone = '13603108202';	-- 使用覆盖索引
EXPLAIN select username from user_innodb t where t.phone = '13603108202';			-- 使用覆盖索引
EXPLAIN select * from user_innodb t where t.username='huathy';	-- 不使用覆盖索引,不得不回表操作

インデックス コンディション プッシュダウン (ICP)

innoDB は自動的に有効になり、自動的に最適化されます。
インデックス作成は、データの格納を担当するストレージ エンジンに実装され、データのフィルタリングと計算はサービス レイヤーに実装されます。インデックスに基づいてクエリを実行できると、より効率的です。このストレージ エンジンでフィルタリングできない条件を最初にストレージ エンジンでフィルタリングします。このアクションは、インデックス条件のプッシュ ダウンです。

インデックス条件プッシュダウンを使用しているかどうかの判断方法: 実行計画の Extra に Using インデックス条件が存在する場合は、インデックス条件プッシュダウンが使用されていることを示します。インデックス条件の正式名称:インデックス条件押し下げ。

-- 创建员工表
CREATE TABLE `employees` (
 `emp_no` int(11) NOT NULL,
 `birth_date` date  NULL,
 `first_name` varchar(14) NOT NULL,
 `last_name` varchar(16) NOT NULL,
 `gender` enum('M','F') NOT NULL,
 `hire_date` date  NULL,
 PRIMARY KEY (`emp_no`)
) ENGINE=InnoDB ;
-- 在姓、名列上加上索引
alter table employees add index idx_lastname_firstname(last_name,first_name);

-- 进行查询
EXPLAIN SELECT * FROM employees t WHERE t.last_name = 'Wu'  AND t.first_name like '%x'
-- 可以看到Extra中Using index condition表示用到了索引条件下推。

-- 查看操作开关是否开启索引条件下推
show global variables like '%optimizer_switch%';
-- index_condition_pushdown=on
-- 关闭索引条件下推
set optimizer_switch = 'index_condition_pushdown=off' 
-- 再次查看是否使用了索引条件下推
EXPLAIN SELECT * FROM employees t WHERE t.last_name = 'Wu'  AND t.first_name like '%x'
-- 可以看到返回 Using Where 表示在server层过滤

上記のクエリ メソッド、クエリ プロセスは次のとおりです。

  • インデックス プッシュ ダウン処理が実行されない場合:
    セカンダリ インデックスの取得データ-- テーブルに戻る -->主キー インデックス リーフ ノードで完全なレコードを取得-->サーバー層でデータをフィルター処理 (N レコードは実行されません)同様の条件を満たす、サーバー層自体のフィルターが必要)
  • インデックス プッシュダウンのクエリ プロセス:セカンダリ インデックス検索データ-->セカンダリ インデックス Wu,xをフィルター処理 -- テーブルを返す -->主キー インデックス リーフ ノードで完全なレコードを取得する-->サーバー層に戻る (類似条件 N を満たす)レコード、サーバー層のフィルタリングは不要)

索引付けの原則:

  1. どこでの判定、並べ替え、リンクの結合、グループごとのグループ化に使用されるフィールドにインデックスを作成します。
  2. インデックスの数は多すぎないようにしてください。
  3. 識別度が最も低いフィールド (列の分散度が最も低いフィールド) は、インデックスを作成する必要はありません。
  4. 頻繁に更新される値は、主キーまたはインデックスとして使用しないでください。
  5. 順序付けされていない値 (ID カード、UUID) をインデックスとして使用することはお勧めしません。これにより、B+ ツリーの多数の構造調整が発生し、コンピューティング パフォーマンスが消費されます。
  6. 複合インデックスは、分散の高い列を前に置きます。
  7. 単一列のインデックスを変更するのではなく、共通の適合インデックス。
  8. フィールドが長すぎる場合は、プレフィックス インデックスを作成します。

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

一部のテキストが長すぎます。接頭辞だけで照合する必要があります。文字列をインターセプトして、接頭辞のインデックスを使用できます。テキストが長すぎるとストレージ スペースを占有し、短すぎると区別がつかなくなります。ここでは、適切な長さを計算する必要があります。

-- 前缀索引:
CREATE TABLE `pre_test` (
  `content` varchar(20) DEFAULT NULL,
  KEY `pre_idx` (`content`(6))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

インデックス使用の原則

インデックスを使用しない場合

  1. インデックス付きの列で関数 (replace、substr、concat、sum、count、avg) と式を使用する
  2. 文字列は引用符で囲まれておらず、暗黙的な変換が行われます。
  3. like 条件の前には % が付きます。左端一致の原則に違反します。もちろん、インデックス条件がプッシュ ダウンされた場合は例外です。
  4. オプティマイザーのバージョン、データベースのバージョンなどに関連して、ネガティブ クエリの状況を判断できません。<>、!=、not in、not exists

オプティマイザ

  1. コストベースのオプティマイザー (MySQL で使用): IO、CPU
  2. ルールベースのオプティマイザー (Oracle 以前のバージョン):

おすすめ

転載: blog.csdn.net/qq_40366738/article/details/130009562