MySQLの最適化ツール(説明)

ツールの紹介を説明します

使用すると、SQL文を実行するクエリまたはSELECT文の前に、MySQLがクエリにマークを設定します説明キーワード構造の増加でパフォーマンスのボトルネックを分析し、クエリを返す実行プラン情報を実行するために、オプティマイザをシミュレートすることができ、キーワードEXPLAIN代わりに、SQL注意が実行されますから、サブクエリが含まれている場合は、一時テーブルにサブクエリ結果を実行します

分析例1を説明します

例表:

表1

DROP TABLE IF EXISTS `actor`;
CREATE TABLE `actor` (`id` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
`update_time` datetime DEFAULT NULL,PRIMARY KEY (`id`)) 
ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES 
(1,'a','2017‐12‐22 15:27:18'),(2,'b','2017‐12‐22 15:27:18'),(3,'c','2017‐12‐22 15:27:18');

表2

DROP TABLE IF EXISTS `film`;
CREATE TABLE `film` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `film` (`id`, `name`) VALUES (3,'film0'),(1,'film1'),(2,'film2');

表3

DROP TABLE IF EXISTS `film_actor`;
CREATE TABLE `film_actor` (
`id` int(11) NOT NULL,
`film_id` int(11) NOT NULL,
`actor_id` int(11) NOT NULL,
`remark` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_film_actor_id` (`film_id`,`actor_id`)) 
ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`) VALUES (1,1,1),(2,1,2),(3,2,1);

二つのテーブルがある場合は、クエリ内の行は、参加して、クエリに参加各テーブル意志の出力は、出力は2行になります

二つの変形を説明

1)拡張について説明し、余分なクエリの最適化は、説明に基づいていくつかの情報を提供します。どのようなオプティマイザの最適化を参照するには、show警告コマンドによってクエリが続いて最適化することができます

主キーのクエリ:

explain select * from pea_goods where pea_goods_Id=190919181614003;

出力

ID SELECT_TYPE テーブル パーティション タイプ possible_keys キー key_lenに REF フィルタ エクストラ
1 SIMPLE pea_goods CONST PRIMARY PRIMARY 8 CONST 1 100.00

警告を表示します。

出力

レベル コード メッセージ
注意 1003 / * *#1 / SELECT '190,919,181,614,003' AS SELECT pea_goods_id、 '190,919,145,343,001' AS pea_supplier_id、 '190,919,175,230,001' AS pea_customer_id、 'エスタゾラム' AS goods_name、 'XXX007' AS goods_code、'10 'AS goods_type、' T 'AS goods_unit、AS NULL safe_date、100' 'AS width' 100 'AS length' 100 'AS height' 100 'AS weight、AS NULL volume、' 10000.08 'AS price、' 2019年10月30日午後十一時59分59秒'AS warranty'30' AS allowed_day'慎重に、危険な'AS吹き付けdescription、AS NULL line_no、' Y 'AS is_active' 190,815,135,958,005 'AS created_by、' 2019年9月19日午後06時16分14秒'AS created' 190,815,135,958,005 'AS updated_by、' 2019年10月10日夜4時53分02秒「AS updated「190,815,135,742,002」AS ad_client_idからvip_tulinpea_goodsここで、1

非プライマリキーのクエリ

explain select * from pea_goods where pea_customer_id=190919175230001;

出力

ID SELECT_TYPE テーブル パーティション タイプ possible_keys キー key_lenに REF フィルタ エクストラ
1 SIMPLE pea_goods すべて 50 10.00 使い方 どこ

警告を表示します。

出力

レベル コード メッセージ
注意 1003 / *
select#1 */ select `vip_tulin`.`pea_goods`.`pea_goods_id` AS `pea_goods_id`,`vip_tulin`.`pea_goods`.`pea_supplier_id` AS `pea_supplier_id`,`vip_tulin`.`pea_goods`.`pea_customer_id` AS `pea_customer_id`,`vip_tulin`.`pea_goods`.`goods_name` AS `goods_name`,`vip_tulin`.`pea_goods`.`goods_code` AS `goods_code`,`vip_tulin`.`pea_goods`.`goods_type` AS `goods_type`,`vip_tulin`.`pea_goods`.`goods_unit` AS `goods_unit`,`vip_tulin`.`pea_goods`.`safe_date` AS `safe_date`,`vip_tulin`.`pea_goods`.`width` AS `width`,`vip_tulin`.`pea_goods`.`length` AS `length`,`vip_tulin`.`pea_goods`.`height` AS `height`,`vip_tulin`.`pea_goods`.`weight` AS `weight`,`vip_tulin`.`pea_goods`.`volume` AS `volume`,`vip_tulin`.`pea_goods`.`price` AS `price`,`vip_tulin`.`pea_goods`.`warranty` AS `warranty`,`vip_tulin`.`pea_goods`.`allowed_day` AS `allowed_day`,`vip_tulin`.`pea_goods`.`description` AS `description`,`vip_tulin`.`pea_goods`.`line_no` AS `line_no`,`vip_tulin`.`pea_goods`.`is_active` AS `is_active`,`vip_tulin`.`pea_goods`.`created_by` AS `created_by`,`vip_tulin`.`pea_goods`.`created` AS `created`,`vip_tulin`.`pea_goods`.`updated_by` AS `updated_by`,`vip_tulin`.`pea_goods`.`updated` AS `updated`,`vip_tulin`.`pea_goods`.`ad_client_id` AS `ad_client_id` from `vip_tulin`.`pea_goods` where (`vip_tulin`.`pea_goods`.`pea_customer_id` = 190919175230001)

idカラム

ID SELECT_TYPE テーブル パーティション タイプ possible_keys キー key_lenに REF フィルタ エクストラ
1 SIMPLE pea_goods すべて 50 10.00 使い方 どこ
  id 列的编号是select 的序列号,有几个select 就有几个id,并且顺序是按照select 顺序增长的。
  id 越大执行优先级越高,id 相同从上往下执行,id 为null 最后执行。

2. SELECT_TYPE列

シンプル:サブクエリ、ユニオンの含まれていない単純なクエリのクエリを表し
プライマリを:最も外側の複雑なクエリを選択する
サブクエリ:select句のサブクエリは、
派生:サブクエリ句から

SQL:

set session optimizer_switch='derived_merge=off'; 
-- 关闭 mysql 5.7 新特性对衍生表的合并优化
EXPLAIN SELECT
	( SELECT 1 FROM pea_goods WHERE pea_goods_id = 190919182516001 ) 
FROM
	( SELECT * FROM pea_customer WHERE pea_customer_id = 190919175230001 ) goods

主、サブクエリと派生型の比較>>出力:

ID SELECT_TYPE テーブル パーティション タイプ possible_keys キー key_lenに REF フィルタ エクストラ
1 PRIMARY システム 1 100.00
3 派生 pea_customer CONST PRIMARY PRIMARY 8 CONST 1 100.00
2 SUBQUERY pea_goods CONST PRIMARY PRIMARY 8 CONST 1 100.00

組合:2回目以降の組合を選択

出力:

ID SELECT_TYPE テーブル パーティション タイプ possible_keys キー key_lenに REF フィルタ エクストラ
1 PRIMARY 使用されませんテーブル
1 連合

3.テーブルの列

この列はxeplain行がテーブルにアクセスして表します。
サブクエリが存在する、テーブルの列から 最初のクエリ実行ID = Nに現在のクエリは、クエリID = N依存性を示している
組合がある場合、UNIONは、テーブルの列の値を結果1 <組合1,2>及び2は組合と選択IDの関与を示しています。

4.型の列

会合またはアクセスタイプのタイプは、MySQLでこの列のショーは、テーブルの行を見つけ、データの行のおおよその範囲を見つける方法を決定します。
最高から最悪にシステム>のconst>でeq_ref> ref>を rangge>インデックス> ALLは、 一般的には:好ましくは最大refに、クエリがレベル範囲に達したことを確認する必要があり

NULL:MySQLは、実装フェーズでアクセスまたはインデックスを持っていない、最適化の段階で問い合わせることができます。

CONST、システム:MySQLは一定にそれをクエリの一部のために最適化と変換することができる
最大1個の一致する行で、一回読み取るように、すべての列と一定の主キーまたはテーブルの一意のキーと比較した場合(表示の警告) 、より速く。constのシステムは特殊なケースです。ときにシステムという名前の1つのリストだけマッチングタプル。

5. possible_keys列

この列が表示され、それらを見つけるためにインデックスを使用することができ
possible_keys列を説明し、NULLに表示キー、この状況は表内の多くのデータはないため、このクエリ少し助け指数を考えるMYSQL、テーブル全体を選択するがある場合に発生する可能性がありますことをお問い合わせ

列はNULL、無関連の指標である場合。あなたは、クエリのパフォーマンスを向上させるために、適切なインデックスを作成することができますこの場合、あなたは、where句をチェックして結果を表示するために説明し、見ることができます

6 key 列

このインデックスのMySQLが実際に、テーブルへの最適化アクセスに使用される列が示す
クエリでpossible_keyの列インデックス、使用force_index、ignore_indexを強制または無視したい場合は、インデックスは、NULLとして使用されていない場合。

7.key_len列

この列が示すインデックスMySQLで使用されるバイト数は、その特定のインデックス列の数で計算することができます。

pea_goods_IdのBIGINTタイプとは8バイトです。

主キーのクエリ:

explain select * from pea_goods where pea_goods_Id=190919181614003;

出力

ID SELECT_TYPE テーブル パーティション タイプ possible_keys キー key_lenに REF フィルタ エクストラ
1 SIMPLE pea_goods CONST PRIMARY PRIMARY 8 CONST 1 100.00

key_lenに、以下の計算規則:

  • CHAR(N):Nバイト長
  • VARCHAR(N):2バイト文字列の長さストレージ、それがUTF-8である場合、長さ3N + 2
数値型
  • TINYINT:1つのバイト
  • SMALLINT:2つのバイト
  • INT:4つのバイト
  • BIGINT:8つのバイト
タイムタイプ
  • 日付:3つのバイト
  • タイムスタンプ:4つのバイト
  • 日時:8バイトのフィールドNULLは、記録する必要がNULLバイト1であれば可能に

8 REF列

この列が示す共通に使用されるテーブルルックアップ列の値または定数のレコードのインデックスキー列は、次のとおりCONST(定数)、フィールド名(例:film.id

9行列

この列には、読むべきであり、これは結果セット内の行の数ではないことを検出、ノートの行数を見積もるのMySQL

10エクストラコラム

この列が示す追加情報は、以下の重要な共通を参照してください。

  • インデックスは使用:使用カバーインデックス

  • どこを使用:使用結果に声明、非インデックス列カバークエリを

  • 条件条件のインデックスが使用するクエリの列が完全に条件が先頭列の範囲ですインデックスによってカバーされていません。

  • 一時は使用:MySQLは、クエリを処理するために一時テーブルを作成する必要があり、このような状況は、最適化することが一般的である、最適化するために、インデックスを使用しての最初に考えました。

    1. 一時テーブルを作成するには、この時点で何のインデックスをgoods_nameありません

    pea_goodsからSELECT DISTINCT goods_nameをEXPLAIN

いいえインデックス結果がありません

ID SELECT_TYPE テーブル パーティション タイプ possible_keys キー key_lenに REF フィルタ エクストラ
1 PRIMARY pea_goods ALL Using temporary
EXPLAIN SELECT DISTINCT  goods_name from  pea_goods

有索引结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY pea_goods ALL Using index
  • Using filesort

将用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘完成排序。这种情况下一般也是要考虑使用索引来优化的

  1. goods_name 未创建索引,pea_goods,保存排序关键字pea_goods和对应的id,然后排序pea_goods并检索行记录
    EXPLAIN SELECT * from pea_goods ORDER BY goods_name
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY pea_goods ALL Using filesort
  1. goods_name 创建索引
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY pea_goods ALL Using index
  • Select tables optimized away

使用某些聚合函数(比如 max、min)来访问存在索引的某个字段是
EXPLAIN SELECT min(pea_goods_id) from pea_goods ORDER BY goods_name

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE Select tables optimized away

索引最佳实践

创建表

CREATE TABLE `employees` ( 
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,     
`name` VARCHAR ( 24 ) NOT NULL DEFAULT '' COMMENT '姓名',   
`age` INT ( 11 ) NOT NULL DEFAULT '0' COMMENT '年龄',   
`position` VARCHAR ( 20 ) NOT NULL DEFAULT '' COMMENT'职位',
`hire_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',  
PRIMARY KEY ( `id` ), KEY `idx_name_age_position` ( `name`, `age`, `position` ) USING BTREE  )    
ENGINE = INNODB AUTO_INCREMENT = 4 DEFAULT
CHARSET = utf8 COMMENT = '员工记录表'; 
    
INSERT INTO employees ( NAME, age, position, hire_time ) VALUES ( 'LiLei',22,'manager',NOW());
INSERT INTO employees ( NAME, age, position, hire_time ) VALUES ('HanMeimei', 23, 'dev', NOW()); 
INSERT INTO employees ( NAME, age, position, hire_time ) VALUES ('Lucy',23,  'dev', NOW());

1.全值匹配

1. EXPLAIN SELECT *  FROM     employees  WHERE     NAME = 'LiLei'; 
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE employees ref idx_name_age_position idx_name_age_position 74 const 1
EXPLAIN SELECT     *  FROM     employees  WHERE
NAME = 'LiLei'      AND age = 22; 
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE employees ref idx_name_age_position idx_name_age_position 78 const,const 1
EXPLAIN SELECT     *  FROM     employees  WHERE     NAME = 'LiLei' AND age = 22
ANDposition = 'manager'; 
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE employees ref idx_name_age_position idx_name_age_position 140 const,const,const 1

2.最左前缀法则

如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。

EXPLAIN SELECT * FROM employees WHERE age = 22 AND position ='manager';

结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE employees 3 33.33 Using where
EXPLAIN SELECT * FROM employees WHERE position = 'manager';

结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE employees 3 33.33 Using where
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei';

结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE employees ref idx_name_age_position idx_name_age_position 74 const 1 100.00

3.不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描

EXPLAIN SELECT * FROM employees WHERE name ='LiLei';EXPLAIN SELECT * FROM employees    
WHERE left(name,3) = 'LiLei';

结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE employees ref idx_name_age_position idx_name_age_position 74 const 1 100.00

给hire_time增加一个普通索引:

   ALTER TABLE `employees`2ADD INDEX `idx_hire_time` (`hire_time`) USING BTREE ; 
    EXPLAIN  select * from employees where date(hire_time) ='2018-09-30';

结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE employees 3 100.00 Using where

转化为日期范围查询,会走索引:

EXPLAIN  select * from employees where hire_time >='2018-09-30 00:00:00'  
andhire_time <='2018-09-30 23:59:59';

还原最初索引状态

ALTER TABLE `employees` DROP INDEX `idx_hire_time`;

4.存储引擎不能使用索引中范围条件右边的列

EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22 AND position ='manager';
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age > 22 AND position ='manager';

5. 尽量使用覆盖索引(只访问索引的查询(索引列包含查询列)),减少select *语句

EXPLAIN SELECT name,age FROM employees WHERE name= 'LiLei' AND age = 23 AND position ='manager';
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 23 AND position ='manager';

6. mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描

EXPLAIN SELECT * FROM employees WHERE name != 'LiLei';

7. is null,is not null 也无法使用索引

EXPLAIN SELECT * FROM employees WHERE name is null

8. like以通配符开头('$abc...')mysql索引失效会变成全表扫描操作

EXPLAIN SELECT * FROM employees WHERE name like '%Lei' 
EXPLAIN SELECT * FROM employees WHERE name like 'Lei%'
问题:解决like'%字符串%'索引不被使用的方法?

a)使用覆盖索引,查询字段必须是建立覆盖索引字段

    EXPLAIN SELECT name,age,position FROM employees WHERE name like '%Lei%';

b)如果不能使用覆盖索引则可能需要借助搜索引擎

9.字符串不加单引号索引失效

EXPLAIN SELECT * FROM employees WHERE name = '1000';
EXPLAIN SELECT * FROM employees WHERE name = 1000;

10.少用or或in

用它查询时,mysql不一定使用索引,mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引,详见范围查询优化

EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' or name = 'HanMeimei';

11.范围查询优化给年龄添加单值索引

ALTER TABLE `employees`2ADD INDEX `idx_age` (`age`) USING BTREE ;
explain select * from employees where age >=1 and age <=2000;

没走索引原因:mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引。比如这个例子,可能是由于单次数据量查询过大导致优化器最终选择不走索引优化方法:可以讲大的范围拆分成多个小范围

explain select * from employees where age >=1 and age <=1000;
explain select * from employees where age >=1001 and age <=2000;

还原最初索引状态

1ALTER TABLE employees2DROP INDEX idx_age;

索引使用总结:

假设 index(a,b,c)

where 语句 索引是否被使用
where a=3 Y ,使用到a
where a =3 and b =4 Y ,使用到 a,b
where a =3 and b =4 and c=5 Y ,使用到 a,b,c
where b=3 / where b= 3 and c=4 /where c=5 N
where a=3 and c=5 使用到a ,但是c不可以, b中间断了
where a=3 and b like 'KK%' and c=4 Y ,使用到a,b,c
where a=3 and b like '%KK' and c =4 Y 只用到a
where a =3 and b like '%KK%' and c=4 Y 只用到a
where a =3 and b like 'K%KK%' and c=4 Y 用到a,b,c

like KK%相当于=常量,%KK和%KK% 相当于范围

おすすめ

転載: www.cnblogs.com/wxs121/p/12550107.html