しかし、話題の個人的なポイント分析から、この競争の下で、唯一のいくつかの個人的な経験、学習交流を含む〜
予選ラウンド
データ
order 表的数据量 2000
CREATE TABLE `order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar ( 32) COLLATE utf8_bin NOT NULL,
`creator` varchar(24) COLLATE utf8_bin NOT NULL,
`price` varchar(64) COLLATE utf8_bin NOT NULL,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`status` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
);
order_item 表的数据量 499760
CREATE TABLE `order_item` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(32) COLLATE utf8_bin NOT NULL,
`parent` bigint(20) NOT NULL,
`status` int(11) NOT NULL,
`type` varchar(12) COLLATE utf8_bin NOT NULL DEFAULT '0',
`quantity` int(11) NOT NULL DEFAULT '1',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
選択1
分析
SELECT * FROM `order` o INNER JOIN order_item i ON i.parent = o.id
ORDER BY o.status ASC, i.update_time DESC LIMIT 0, 20;
mysql> explain SELECT * FROM `order` o INNER JOIN order_item i
-> ON i.parent = o.id ORDER BY o.status ASC, i.update_time DESC LIMIT 0, 20;
+----+-------------+-------+------------+--------+---------------+---------+---------+---------------------------------+--------+----------+---------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+---------------------------------+--------+----------+---------------------------------+
| 1 | SIMPLE | i | NULL | ALL | NULL | NULL | NULL | NULL | 497839 | 100.00 | Using temporary; Using filesort |
| 1 | SIMPLE | o | NULL | eq_ref | PRIMARY | PRIMARY | 8 | sql_optimization_match.i.parent | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+---------------------------------+--------+----------+---------------------------------+
内側の2つの表は、フィールドは、このように外部ソートを引き起こし、インデックスをソートするために使用することができないので、書き込みの一時テーブルで得られた、実施計画、駆動テーブル選択ORDER_ITEMによれば、ソート2つのテーブルのフィールド、および矛盾方向ため、参加します。
- 分析は、フィールドに参加します
order_item.parent = order.id
mysql> select count(distinct(parent)) from order_item; // 区分度较低,忽略
+-------------------------+
| count(distinct(parent)) |
+-------------------------+
| 300 |
+-------------------------+
mysql> select count(distinct(id)) from `order`; // 区分度较高,但 id 上已有主键索引
+---------------------+
| count(distinct(id)) |
+---------------------+
| 2000 |
+---------------------+
1 row in set (0.00 sec)
order_item.parentなしインデックス;
order.idインデックス。
- 分析集計フィールド
ORDER BY o.status ASC, i.update_time DESC
mysql> select count(distinct(status)) from `order`; //区分度很低
+-------------------------+
| count(distinct(status)) |
+-------------------------+
| 2 |
+-------------------------+
mysql> select count(distinct(update_time)) from `order_item`; // 区分度一般
+------------------------------+
| count(distinct(update_time)) |
+------------------------------+
| 32768 |
+------------------------------+
フィールドの順序によって順序の2つの表は、テーブル上の元のインデックスがフィールドをソートしませんが、あなたが、外部ソートを使用する必要があり、同じではありません。
最適化
前回のクエリプランによると見られることができ、それはORDER_ITEMは、外部ソートした後、全表スキャンを実施しています。SQLセマンティクスは、2つのソートを必要とするので、従って、それによって時間の消費を低減、外部ソートのデータ量を削減する他の方法です。
ソートフィールドの注文状況テーブルの唯一の2つの異なる値、ステータスを削除してください並べ替えフィールドが大幅に速度を向上した後、今度は、UPDATE_TIMEフィールドによって順にインデックスを増加しようと、差別はまた、要件を満たします。
唯一の2つのステータスは、労働組合は、すべての異なるソート順使えなくインデックス内の異なるテーブルによって順序を避けるために、代わりに使用することができます。
ここで与えられた公式の推薦は(MLが自動的に上書きすべきではない)であるが、これはステータスが型TINYINT(1)でない場合の方法は、いくつかの制限、制限されたアプリケーションのシナリオを持って書き換え、SQL、および新しいタイプの場合は、将来的に増加します、SQLの必要性は常に書き換えられています。
私たちは、インデックスを再最適化し、ビジネスの変革を推進しようとすることができます。
さらに、SQLは書き換えた後、それは関節インデックス指定されたインデックスのアドバイスを増加させることである(
update_time
、parent
)、上記の分析を見ることができる、parent
より低い分化の程度、又はここで関節指数をするだけ増加update_time
率、ほぼ同じパフォーマンスを向上させます。
# sql 改写
SELECT o.*,i.* FROM (
( SELECT o.id, i.id item_id FROM `order_1` o
INNER JOIN order_item i ON i.parent =o.id
WHERE o.status = 0
ORDER BY i.update_time DESC LIMIT 0, 20)
UNION ALL
(SELECT o.id, i.id item_id FROM `order_1` o
INNER JOIN order_item i ON i.parent =o.id
WHERE o.status = 1
ORDER BY i.update_time DESC LIMIT 0, 20)
) tmp
INNER JOIN `order_1` o ON tmp.id = o.id
INNER JOIN order_item i ON tmp.item_id = i.id
ORDER BY o.status ASC,
i.update_time DESC
LIMIT 0, 20
# 增加索引
alter table order_item add index `item_idx_1` (`update_time`,`parent`);
# 执行计划
+----+-------------+------------+------------+--------+---------------+------------+---------+---------------------------------+------+----------+---------------------------------+order_item i ON tmp.ite
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+--------+---------------+------------+---------+---------------------------------+------+----------+---------------------------------+
| 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 40 | 100.00 | Using temporary; Using filesort |
| 1 | PRIMARY | o | NULL | eq_ref | PRIMARY | PRIMARY | 8 | tmp.id | 1 | 100.00 | NULL |
| 1 | PRIMARY | i | NULL | eq_ref | PRIMARY | PRIMARY | 8 | tmp.item_id | 1 | 100.00 | NULL |
| 2 | DERIVED | i | NULL | index | NULL | item_idx_1 | 12 | NULL | 20 | 100.00 | Using index |
| 2 | DERIVED | o | NULL | eq_ref | PRIMARY | PRIMARY | 8 | sql_optimization_match.i.parent | 1 | 10.00 | Using where |
| 3 | UNION | i | NULL | index | NULL | item_idx_1 | 12 | NULL | 20 | 100.00 | Using index |
| 3 | UNION | o | NULL | eq_ref | PRIMARY | PRIMARY | 8 | sql_optimization_match.i.parent | 1 | 10.00 | Using where |
+----+-------------+------------+------------+--------+---------------+------------+---------+---------------------------------+------+----------+---------------------------------+
概要
1. 对区分度极低的字段如果有排序、范围比较等操作,可以转换为 union all;
2. 对排序字段,尝试使用索引避免 filesort,如果不可避免,在 filesort 之前尝试减少排序的数据量;
2.アップデート
分析
update `order` set create_time = now()
where id in (select parent from order_item where type = 2 );
# 执行计划
mysql> explain update `order_1` set create_time = now() where id in (select parent from order_item where type = 2 );
+----+--------------------+------------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+------------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
| 1 | UPDATE | order_1 | NULL | index | NULL | PRIMARY | 8 | NULL | 2000 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | order_item | NULL | ALL | NULL | NULL | NULL | NULL | 496836 | 1.00 | Using where |
+----+--------------------+------------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
更新条件サブクエリでの方法である、タイプ依存サブクエリを留意選択するために説明し、外部を表す最初のクエリを実行し、行の数と一致するように、外側のクエリは、次に、次のN個のクエリの次男、非常に低い効率となり、Nです。
サブクエリ通常の練習にもテーブルが結合問合せに変換することです。
最適化
- 1.最も簡単なサブクエリ - >最適化作業に参加
update `order` o inner join (select parent from `order_item` where type = 2) tmp on o.id = tmp.parent set create_time = now();
mysql> explain update `order` o inner join (select parent from `order_item` where type = 2) tmp on o.id = tmp.parent set create_time = now() \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_item
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 497839
filtered: 10.00
Extra: Using where
*************************** 2. row ***************************
id: 1
select_type: UPDATE
table: o
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: sql_optimization_match.order_item.parent
rows: 1
filtered: 100.00
Extra: NULL
2 rows in set (0.00 sec)
コンバージョン率は、はるかに速く、以前よりもされているそこには依存サブクエリはありませんが、それでも二級、ORDER_ITEMテーブル選択テーブルを駆動するが、基本的には全表スキャンされた後に、手段49ワットラインデータと注文テーブルを使用して参加します、コストは依然として非常に大きいです。
最も原始的な遅い更新を見て、条件が唯一のID、更新無意味に重複親、従って、重合のORDER_ITEMの親サブクエリ範囲の分野で行うことができることができれば、テーブルの順序だけを変更する(GROUP BYサブ条件クエリはorder_item.type = 2を持っているので)、従って、タイプフィールドは同時に重合させてもよいです。
唯一の主キー索引ORDER_ITEMので、同等の重合条件および動作はテーブルインデックスORDER_ITEMベストを使用して決定され、従って、関節インデックスは、インデックス等価演算の優先順位を確立することができます。
追加のノートでは、我々は、インデックスを作成したい、SQLフィールドタイプで同等のインデックスを入力し、一致しています。
-
- でも、テーブルクエリの最適化
增加索引:
alter table `order_item` add index idx_1(type,parent);
sql 优化:
update `order` o inner join (
select parent from `order_item`
where type = '2' group by type, parent) i
on o.id = i.parent set create_time = now();
mysql> explain update `order` o inner join ( select parent from `order_item` where type = '2' group by type, parent ) i on o.id = i.parent set create_time = now()\G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: <derived2>
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 571
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: UPDATE
table: o
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: i.parent
rows: 1
filtered: 100.00
Extra: NULL
*************************** 3. row ***************************
id: 2
select_type: DERIVED
table: order_item
partitions: NULL
type: range
possible_keys: idx_1
key: idx_1
key_len: 46
ref: NULL
rows: 571
filtered: 100.00
Extra: Using where; Using index for group-by
最適化の後、ORDER_ITEMテーブルの使用は、テーブル駆動を行うが、ここでも一時テーブルを生成した後、上記インデックスidx_1を構築するために使用し、テーブル結合注文します。理由によって基は、結果セットの数が少ないORDER_ITEMを発生するので、駆動テーブルとして選択しました。
ここで注意すべきもう一つは、(タイプによって親やグループによるgroiupものの、親が結果の行数を返します同じですが、まだ大きなギャップ実行計画)2を使用して、グループで、これはidx_1インデックスを使用することであるということです
実行ミリ秒後に最適な時間。
概要
1. 驱动表的选择,始终是小表驱动大表,驱动表会走全表扫,所以通常索引都是在被驱动表上增加;
2. 如果执行计划中出现了 DEPENDENT SUBQUERY,一定会对 sql 的执行效率有影响(同时 DEPENDENT SUBQUERY 还会潜在地造成一定程度的锁放大), in + 子查询 方式很容易引起,可以将子查询优化为 join 操作;
3. 对于 join 连表查询,进行连表的数据越少,执行效率就越高,因此,在不改变sql语义的前提下,尽量使参加 join 的数据量减少;
4. 关于索引顺序: 等值条件 > group by > order by
5. 注意索引字段类型和 sql 中的的判断条件中的数据类型是否一致。
決勝
データ
差別算出処理は、直接悪い差別を与えるここでは省略されています
計算差別にいくつかのテーブルの行のより多くの数が別個の5000行の前に統計の使用の値であるように、一部の極端なケース(そうすることができる発電環境は、それがもたらしたオーバーヘッド計算差別を減らすことができます)小さなテーブルは、行数を意味し、偽陽性を引き起こす可能性がありますが稀にインデックスを付けないテーブルは素晴らしいではありません。
# customer 数据量 1,200,000
CREATE TABLE `customer` (
`custkey` int(11) NOT NULL, // 区分度 OK
`name` varchar(25) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, // 区分度 OK
`address` varchar(40) NOT NULL,
`nationkey` int(11) NOT NULL, // 区分度较低
`phone` char(15) NOT NULL, // 区分度 OK
`acctbal` decimal(15,2) NOT NULL,
`mktsegment` char(10) NOT NULL, // 区分度较低
`comment` varchar(117) NOT NULL,
PRIMARY KEY (`custkey`),
KEY `idx_nationkey` (`nationkey`)
);
# nation 数据量 25
CREATE TABLE `nation` (
`nationkey` int(11) NOT NULL, // 区分度 OK
`name` char(25) NOT NULL,
`regionkey` int(11) NOT NULL,
`comment` varchar(152) DEFAULT NULL,
PRIMARY KEY (`nationkey`),
KEY `idx_4_0` (`name`)
);
# orders 数据量12,000,000
CREATE TABLE `orders` (
`orderkey` int(11) NOT NULL,
`custkey` int(11) NOT NULL, // 区分度 OK
`orderstatus` varchar(1) NOT NULL,
`totalprice` decimal(15,2) NOT NULL, // 区分度 OK
`orderdate` date NOT NULL,
`orderpriority` char(15) NOT NULL,
`clerk` char(15) NOT NULL, // 区分度 OK
`shippriority` int(11) NOT NULL,
`comment` varchar(79) NOT NULL,
PRIMARY KEY (`orderkey`)
);
# region 数据量 5
CREATE TABLE `region` (
`regionkey` int(11) NOT NULL,
`name` varchar(25) NOT NULL,
`comment` varchar(152) DEFAULT NULL,
PRIMARY KEY (`regionkey`)
);
選択1
分析
select c.custkey, c.phone, sum(o.totalprice) totalprice
from nation n
inner join customer c on c.nationkey = n.nationkey
inner join orders o on o.clerk = c.name
where n.name = "CHINA" and c.mktsegment = "HOUSEHOLD" and c.phone like "28-520%"
group by c.custkey, c.phone;
# 执行计划
+----+-------------+-------+------------+------+---------------+------+---------+------+----------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+----------+----------+----------------------------------------------------+
| 1 | SIMPLE | n | NULL | ALL | PRIMARY | NULL | NULL | NULL | 25 | 10.00 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | c | NULL | ALL | NULL | NULL | NULL | NULL | 1189853 | 0.11 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | o | NULL | ALL | NULL | NULL | NULL | NULL | 10963843 | 10.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+----------+----------+----------------------------------------------------+
三人の顧客C] [表;国民のn] [;] [オーダーO
- 顧客
where条件:
c.mktsegment = "HOUSEHOLD":区分度较低,放弃
✔ c.phone like "28-520%":区分度较好,考虑添加索引
聚合条件:
group by c.custkey: 区分度较好,但是已经是主键,放弃
✔ c.phone:同where,考虑添加
join 条件:
c.nationkey = n.nationkey:区分度较低,放弃
✔ o.clerk = c.name:区分度较高,考虑添加索引
advice:
add index `dx_1_0`(name);
add index `idx_1_1` (phone);
- 国家
数据量 25,不考虑添加索引
nation 表可以考虑增加索引 add index `idx_1_0`(name); 但意义不大
- 受注
join 条件:
✔ o.clerk = c.name:区分度较高,考虑添加索引
advice:
add index `idx_1_0` (clerk)
最適化
按上述分析增加三条索引后,执行计划如下
+----+-------------+-------+------------+--------+----------------+---------+---------+-------------------+----------+----------+---------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+----------------+---------+---------+-------------------+----------+----------+---------------------------------------------------------------------+
| 1 | SIMPLE | c | NULL | range | dx_1_0,idx_1_1 | idx_1_1 | 45 | NULL | 46 | 10.00 | Using index condition; Using where; Using temporary; Using filesort |
| 1 | SIMPLE | n | NULL | eq_ref | PRIMARY | PRIMARY | 4 | dbaas.c.nationkey | 1 | 10.00 | Using where |
| 1 | SIMPLE | o | NULL | ALL | idx_1_0 | NULL | NULL | NULL | 10963843 | 10.00 | Range checked for each record (index map: 0x2) |
+----+-------------+-------+------------+--------+----------------+---------+---------+-------------------+----------+----------+---------------------------------------------------------------------+
概要
1. 在 inner join 的情况下,我们无法判断出驱动表,因此,我们会选择在合适的字段上都添加索引;
2. 在 sql 中的条件类型较多时,选择把等值条件和聚合条件添加为组合索引,join 条件单独增加索引;
3. 如果数据量过少,增加索引意义不大,可以不考虑;
4. dbrain 给出的是组合索引,两者相比,性能基本一致;
選択2.
分析
select * from (
select custkey, orderdate, sum(totalprice) as totalprice
from orders group by custkey, orderdate
) o
where orderdate = "2019-08-01"
# 执行计划
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+----------+----------+---------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+----------+----------+---------------------------------+
| 1 | PRIMARY | <derived2> | NULL | ref | <auto_key0> | <auto_key0> | 3 | const | 10 | 100.00 | NULL |
| 2 | DERIVED | orders | NULL | ALL | NULL | NULL | NULL | NULL | 10963843 | 100.00 | Using temporary; Using filesort |
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+----------+----------+---------------------------------+
一つだけのテーブル、filesortレコードを使用してグループを含み、SQLは複雑に見えないが、それは駆動表を持っていました。
ネストされた冗長化、削除することが考えられる(サブクエリ)SELECT * FROMで見つかったビューSQLは、、SQLは次のように書き換えることができます。
select custkey, orderdate, sum(totalprice) as totalprice
from orders where orderdate = "2019-08-01" group by custkey, orderdate;
インデックス分析
where 条件:
✔ orderdate = "2019-08-01":区分度较高,考虑增加索引
聚合条件:
✔ group by custkey, orderdate:两个字段区分区都较高,考虑增加索引
advice:
等值条件 优先于 聚合条件
add index `idx_2_0` (orderdate, custkey)
最適化
使用优化后的 sql,增加联合索引,执行计划为
+----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-----------------------+
| 1 | SIMPLE | orders | NULL | ref | idx_2_0 | idx_2_0 | 3 | const | 1 | 100.00 | Using index condition |
+----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-----------------------+
如果是增加两个单独的索引,
add index `idx_2_1` (custkey);
add index `idx_2_2` (orderdate);
执行计划为
+----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+--------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+--------------------------------------------------------+
| 1 | SIMPLE | orders | NULL | ref | idx_2_2 | idx_2_2 | 3 | const | 1 | 100.00 | Using index condition; Using temporary; Using filesort |
+----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+--------------------------------------------------------+
使用到了 filesort,只有custkey可以使用索引,因此建议联合索引。
結論
1. 单张表的sql如果执行计划出现 filesort 等需要关注,频繁嵌套的子查询,会对性能有一定影响,可以考虑 sql 重写;
2. 关于加索引,等值条件要优先于聚合、join等条件;
選択3.
分析
select c.custkey, sum(o.totalprice) totalprice from customer c
left join orders o on o.custkey = c.custkey
where c.phone like "33-64%" and c.name like concat("Customer#00003", "%")
group by c.custkey
在已经有前两条 sql 增加的索引前提下,执行计划为
+----+-------------+-------+------------+-------+------------------------+---------+---------+------+----------+----------+---------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+------------------------+---------+---------+------+----------+----------+---------------------------------------------------------------------+
| 1 | SIMPLE | c | NULL | range | PRIMARY,dx_1_0,idx_1_1 | idx_1_1 | 45 | NULL | 552 | 1.63 | Using index condition; Using where; Using temporary; Using filesort |
| 1 | SIMPLE | o | NULL | ALL | NULL | NULL | NULL | NULL | 10963843 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+-------+------------------------+---------+---------+------+----------+----------+---------------------------------------------------------------------+
顧客テーブルには、インデックスに後で分析他のインデックスを増やす必要が使用されています。
受注テーブルには、インデックスが欠落している可能性がありなくなっ全表スキャン、データのスキャン1200万行、です。
二顧客C]表[;] [オーダーO
-
顧客
where条件: c.phone like "33-64%":第一条 select 已经添加过索引 c.name like concat("Customer#00003", "%"):第一条 select 已经添加过索引 聚合条件: group by c.custkey: 区分度较好,但是已经是主键,放弃 join 条件: o.custkey = c.custkey:区分度较好,但是已经是主键,放弃 advice: 无建议
-
注文
join 条件: o.custkey = c.custkey:区分度较高,考虑增加索引 advice: add index `idx_3_0` (custkey)
最適化
增加索引之后的执行计划为:
+----+-------------+-------+------------+-------+------------------------+---------+---------+-----------------+------+----------+---------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+------------------------+---------+---------+-----------------+------+----------+---------------------------------------------------------------------+
| 1 | SIMPLE | c | NULL | range | PRIMARY,dx_1_0,idx_1_1 | idx_1_1 | 45 | NULL | 552 | 1.63 | Using index condition; Using where; Using temporary; Using filesort |
| 1 | SIMPLE | o | NULL | ref | idx_3_0 | idx_3_0 | 4 | dbaas.c.custkey | 13 | 100.00 | NULL |
+----+-------------+-------+------------+-------+------------------------+---------+---------+-----------------+------+----------+---------------------------------------------------------------------+
增加索引之后。扫描 order 表的行数已经大大减少,执行效率也提升很高
概要
1. 关于 【using where】, 【using index】, 【using index condition】; 【Using where &&Using index】的区别(为什么总结这个呢,我这边的话是建立了两个库,数据和基本的表结构是一致的,但是其中一个库中表的索引是按照我自己分析的情况加上去的,另一个库是官方给出的建议,发现在执行效率都很高的情况下,两者执行计划的 extra 内容有所区别,本想 google 解决,但是看了排名前三的博客,两篇的内容是一样的,和第三篇的解释完全不同,自己尝试了下,这里给出结论,最后附上测试流程):
選択4.
分析
select c.custkey, c.phone from nation n
inner join customer c on c.nationkey = n.nationkey
where n.name = "CHINA" and exists (
select 1 from orders o where o.custkey = c.custkey and o.orderdate = "1998-08-11");
在上面已有的索引前提下,执行计划为
+----+--------------------+-------+------------+------+-----------------+---------+---------+-----------------------+---------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+-------+------------+------+-----------------+---------+---------+-----------------------+---------+----------+----------------------------------------------------+
| 1 | PRIMARY | n | NULL | ref | PRIMARY,idx_1_0 | idx_1_0 | 75 | const | 1 | 100.00 | Using index |
| 1 | PRIMARY | c | NULL | ALL | NULL | NULL | NULL | NULL | 1189853 | 10.00 | Using where; Using join buffer (Block Nested Loop) |
| 2 | DEPENDENT SUBQUERY | o | NULL | ref | idx_2_0,idx_3_0 | idx_2_0 | 7 | const,dbaas.c.custkey | 1 | 100.00 | Using index |
+----+--------------------+-------+------------+------+-----------------+---------+---------+-----------------------+---------+----------+----------------------------------------------------+
参照DEPENDENT SUBQUERYをサブクエリ/ +が存在する条件下で、。しばしば起こり、どのような上記の害、この事の出現、およびSQLを書き換える方法を見つけることを説明することがあります。それが存在しているので+サブクエリの最適化戦略は、参加としてそれを書き換えることです。
最通俗的改写方式:先全部 inner join,最后加 where 条件
select c.custkey, c.phone from nation n
inner join customer c on c.nationkey = n.nationkey
inner join orders o on o.custkey = c.custkey
where n.name = "CHINA" and o.orderdate = "1998-08-11";
官方给出的 sql 比较复杂,但做的事情差不多,多考虑了一点试图使用 group by 来减少 join 的数据量,给出官方答案,这里不多解释它【不过这里去掉 group by 会更好】
SELECT `t1`.`custkey`, `t1`.`phone` FROM
( SELECT * FROM `dbaas`.`nation` AS `t` WHERE `t`.`name` = 'CHINA' ) AS `t0`
INNER JOIN `dbaas`.`customer` AS `t1`
ON `t0`.`nationkey` = `t1`.`nationkey`
INNER JOIN (
SELECT `t2`.`custkey` FROM `dbaas`.`orders` AS `t2`
WHERE `t2`.`orderdate` = '1998-08-11' GROUP BY `t2`.`custkey` ) AS `t5`
ON `t1`.`custkey` = `t5`.`custkey`
インデックスの提案は、あまりここではありません、とフィールド条件は、対応するインデックスされています。
最適化
优化后的执行计划如下:
+----+-------------+-------+------------+--------+-----------------+---------+---------+-----------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+-----------------+---------+---------+-----------------+------+----------+-------------+
| 1 | SIMPLE | n | NULL | ref | PRIMARY,idx_1_0 | idx_1_0 | 75 | const | 1 | 100.00 | Using index |
| 1 | SIMPLE | o | NULL | ref | idx_2_0,idx_3_0 | idx_2_0 | 3 | const | 20 | 100.00 | Using index |
| 1 | SIMPLE | c | NULL | eq_ref | PRIMARY | PRIMARY | 4 | dbaas.o.custkey | 1 | 10.00 | Using where |
+----+-------------+-------+------------+--------+-----------------+---------+---------+-----------------+------+----------+-------------+
2つのドライブ選択した各テーブルNおよびO、refはconstのあるを見ることができます参加し、このパフォーマンスは大きく依存SUBQUERYよりも優れています
概要
1. 并不是所有的复杂 join 都要使用 group by,和数据分布有关,如果 group by 并不能显著降低 join 行数的话, 没有必要;
物事を想像してみて
MySQLのクエリオプティマイザ比較的より複雑なロジックである限り、それはより良い仕事を楽しみにしてできるように、SQL文言が合理的であるが、また、適切なインデックスを持っている必要があります。
私たちは、多くの場合、SQLの最適化を検討して行く、SQLを最適化し、最適化されたSQLに基づいて、所望のインデックスを追加します。(サービスプロバイダの特に2B、実際のデータベースの開発プロセスでは、我々は、インデックスの増加が、その後、私たちはビジネスを続けていくための作業を必要としない場合は、ビジネスへの変更を必要とせず、遅いクエリの問題を解決するために必要なインデックスの試みを高めるために優先権を与えます対応する変換)
まず、SQL書き換え、MySQLのオプティマイザので、もっと検討し、エグゼキュータ自動的に最適化されたAIによって上書きすることができますあまりにも多くのことを行う必要があり、そして、想像することはできません。。。(DBAだけでなく、解雇の人々の波)などのサブクエリー・リライトなどの改善を、考慮すべき異常箇所の実施計画に従って、その経験をもとに人工の言葉は、労働組合はすべての練習を書き換える状況により順番に予備のタイトルのような他の参加します;ここでは、あまりにも柔軟な汎用的な方法をいくつかの効果を持っているのではなく、
第二に、これとは対照的に、既知のSQLとテーブル構造の場合には、AIの指標に依存するが、より多くの人々に本当を与えることが示唆されました感覚、インデックス付け、その後、いくつかの一般的なルール、夏謝、オンラインプレゼンテーションの多くの独自のポイント、のような表情があります
1. 找出所有条件字段,计算字段区分度,区分度很低的字段没有必要加索引,数据量很少的字段一样,加上意义也不大;
2. 条件的话优先级 等值 > 聚合(group/order by) > join ,同一优先级根据区分度建立联合索引;
3. 在聚合条件下,如果聚合后的行数太多,回表量太大的情况下,mysql 可能不会使用这些索引;
4. 驱动表不需要考虑什么索引,驱动表的数据必然都在join的结果集中,对于 inner join 这种无法确定驱动表的,可以考虑在两张表的合适字段上都添加索引
プルに進みます。。。
今、クラウドに移行するために、ビジネスの誰もが、クラウド上のDBインテリジェント診断だけであることを将来的に回避することは困難です。AIは、その後何が起こったのか知っている、知らない、何のステップを行うことができます。