インデックス作成は、MySQL データベースのクエリ パフォーマンスを最適化するための重要なツールです。クエリ条件とテーブル データのインデックスを作成することにより、MySQL はデータを迅速に見つけてクエリ効率を向上させることができます。しかし、実際のデータベースの開発と保守では、インデックスが失敗してクエリが非常に遅くなり、インデックスを使用してクエリを最適化することさえできず、システムのパフォーマンスに重大な影響を与える状況に遭遇することがよくあります。 。では、インデックスの失敗の原因は何でしょうか?
一般的な状況は次のとおりです。
- インデックスブレーク
- データ型の不一致
- クエリ条件は関数演算を使用します
- プレファジークエリ
- ORクエリ
- インデックス作成時に関数を使用する
- インデックスの差別化は高くありません
実例を通して具体的にお話しましょう。ここで文字テーブルがあり、テーブル作成ステートメントが次のようになっているとします。
CREATE TABLE `person` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL,
`score` int(11) NOT NULL,
`age` int(11) NOT NULL,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
1. ジョイントインデックスの中断
クエリにジョイント インデックスを使用する場合、ジョイント インデックス内の列にインデックス中断があると、ジョイント インデックス全体が失敗し、インデックスをクエリの最適化に使用できなくなります。
例: ジョイント インデックスの場合(name, score)
、条件に if しかない場合score
、インデックスが失敗します。
CREATE INDEX idx_name_score ON person (`name`,`score`);
select * from person where score = 90
次の状況ではインデックスが使用されます。
select * from person where name = '31a'
select * from person where score = 90 and name = '31a'
select * from person where name = '31a' and score = 90
2. データ型が一致しない
クエリ条件のインデックスと一致しないデータ型の値を使用すると、MySQL はインデックスを使用してクエリを最適化できなくなり、インデックスが失敗します。
たとえば、列の型が文字列の場合、条件内でデータを引用符で囲む必要があります。そうしないと、インデックスが失敗します。
CREATE INDEX idx_name ON person (`name`);
-- 这里 name 是 varchar 类型
select * from person where name = 31
ただし、インデックスが int 型で、クエリ パラメータが varchar 型の場合、文字列は暗黙的に値に変換されるため、曖昧さはなく、インデックスが使用されます。
CREATE INDEX idx_age ON person (`age`);
-- 这里 age 是 int 类型
select * from person where age = '90'
MySQL が 31 を暗黙的に文字列に変換しないのはなぜですか? この質問に対する答えは、MySQL の公式ドキュメントで説明されています。
値 1 の場合、文字列 '1'、'1a'、'001'、'1 ' などと同じになり、あいまいさが生じます。例を見てみましょう:
2 つのデータを挿入します。
INSERT INTO test.person (id, name, score, age, create_time) VALUES(1, '00031', 90, 18, '2023-04-15 16:29:39');
INSERT INTO test.person (id, name, score, age, create_time) VALUES(2, '31a', 96, 19, '2023-04-15 16:29:39');
次に、クエリ操作を実行します。
select * from persion where name = 31;
3. クエリ条件は関数演算を使用します
クエリ条件で関数演算を使用すると、インデックスが無効になります。例えば:
CREATE INDEX idx_name ON person (`name`);
select * from person where UPPER(name) = '31A';
4. フロントファジークエリ
クエリ条件でプレファジー クエリを使用すると、MySQL は B ツリー インデックスのプレフィックス マッチング クエリを使用できなくなり、インデックスが失敗します。例えば:
CREATE INDEX idx_name ON person (`name`);
select * from person where name LIKE '%a';
5. ORクエリ
クエリ条件で複数の条件をORで結合する場合、ORの前後の条件にインデックスが含まれる場合はインデックスが使用され、ORの前後にインデックスが含まれない条件がある場合はインデックスが使用されます。失敗します。例えば:
CREATE INDEX idx_age ON person (`age`);
select * from person where name = 'John' OR age > 20;
6. インデックス作成時に関数を使用する
インデクス作成時に関数演算を使用した場合、インデクス列を使用してもインデクスは有効になりません。例えば:
CREATE INDEX idx_name ON person (LOWER(name));
-- 如果使用 LOWER(name) 函数建立索引,那么下面查询将导致索引失效
select * from person where name = 'John';
7. インデックスの差別化は高くありません
インデックス列の値の識別が高くない場合、MySQL はインデックスの使用を諦めてテーブル全体のスキャンを選択し、インデックスが無効になる可能性があります。たとえば、次の 2 つのインデックスを作成しました。
CREATE INDEX idx_name ON person (`name`);
CREATE INDEX idx_create_time ON person (`create_time`);
次に、100000 個のデータを挿入します。
create PROCEDURE `insert_person`()
begin
declare c_id integer default 3;
while c_id <= 100000 do
insert into person values(c_id, concat('name',c_id), c_id + 100, c_id + 10, date_sub(NOW(), interval c_id second));
set c_id = c_id + 1;
end while;
end;
CALL insert_person();
次に、以下を実行します。
explain select * from person where NAME>'name84059' and create_time>'2023-04-15 13:00:00'
結果は次のとおりです。
上記の実行計画からわかるように、type=All は、テーブル全体のスキャンであることを示しています。