开发者学堂课程【如何在 PolarDB-X 中优化慢 SQL:如何在 PolarDB-X 中优化慢 SQL】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/987/detail/14930
如何在 PolarDB-X 中优化慢 SQL
//应急工作已经做完,线上运行的业务 A 系统已经恢复正常,接下来分析刚才的 sql 慢的原因,如何解决。找到刚才的 sql “SELECT id, k, c, pad FROM sbtest1 WH ERE K IN (?)”知道是业务 B 发起的 sql ,业务 B 对应的数据库 APP B 里面用 explain 语法对 sql 进行解析,以 k 为条件把所有的列查出来。
//explain 是 PolarDB-X 提供的另外一个来解释 sql 语句执行计划,在 PolarDB-X 提供的执行计划里面,跟 Mysql 有些不一样,也可以在直播结束之后看一下文档里面每一个算子到底是什么意思。简单来说明一下像在 sql 里面最终是要扫描这张表的16个分片,然后就意味着可能是一个代价非常高的 sql 。可以再进一步看它在物理层,也就是前面说的 DNA 层上之前的 sql 是怎么样的,这个时候会提供另外一个 sql 的语法 explain 后面再加一个 execute ,执行看一下大概 DNA 上面 sql 经过分布式计算层解析在下层的 mysql 是怎样执行的。
mysql> use app_b;
Database changed
mysql>
mysql>
mysql> explain SELECT id, k,c, pad FROM sbtest1 WHERE k IN (3);
+-------------------------------------------------------------------------------+
| LOGICAL EXECUTIONPLAN
|
+-------------------------------------------------------------------------------+
| Gather(concurrent=true)
| LogicalView(tables="[000000-000015].sbtest1_D2dF",shardCount=16,sql="SELECT`id`,`k°,`c’,`pad`FROM `sbtest1` AS `sbtest1` WHERE(`k`IN(?))") |
| HitCache:true
|
| Source:PLAN_CACHE
|
| TemplateId:12789b9b
|
+-------------------------------------------------------------------------------+
5 rows in set (0.02 sec)
mysel>explain execute SELECT id,k,c,pad FROM sbtest1 WHERE k IN(3);
//mysel explain 的一个格式
+--+--------------+------+-----------+----+-----------------+---+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+--+---------+-------+--------+----+-------+-------+------+----+
| 1 | SIMPLE | sbtest1 | NULL | ALL | NULL | NULL | NULL | NULL |1921632 | 10 | Using where|
+--+---------+-------+--------+----+-------+-------+------+----+
1 row in set (0.01 sec)
//这里面没有命中任何的索引,大概要去扫描接近200万的数据,所以它是非常慢的 sql ,定位到 sql 之后,因为这里没有命中任何的所有,通常来说第一直觉是要加个索引,那怎么加、在哪加索引?假设这个机构很复杂,分析过程可能会需要一些时间,这个时候就可以用 PolarDB-X提供的另外一个工艺也就是前面说的 sql advisor 工具,它的语法也比较简单,还是刚才那个 sql 在explain 后面加上 advisor 。
mysql>
mysql> explain advisor SELECT id,k,c,pad FROM sbtest1 WHERE k IN (3)
//执行完之后会
| INFO |
+---------+-------------+-------------+--------+----------+-----+----------------+------------+-----------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------+-----------+
| 9850.8% |-65.5% |-100.0% | 1775918.2%|137.5% | 1000560010| 10| 0| 195362| 4.7|
1.00550292E7237 | 29 | 2| 237.6 | 11| 2| ALTER TABLE `app_b`.`sbtest1’ ADD GLOBAL INDEX` __advise_index_gsi _sbtest1_
k`(`k`)DBPARTITION BY HASHC`k`);
Project(id="id",k="k",c="c",pad="pad")
BKAJoin(condition="_drds_implicit_id_ = _drds_implicit_id_ AND id <=> id",type="inner")
IndexScan(tables="APP_B_000003_GROUP.sbtest1__what_if_gsi_k_IPK5",sql="SELECT `id`,`k’,_drds_implicit_id_’FROM `sbtest1..what.i f. gsi_k` AS'sbtest1__what_if_gsi_k` WHERE (`k` IN(?))")
Gather(concurrent=true)
LogicalView(tables="[000000-000015].sbtest1_D2dF",shard Count=16,sql="SELECT `id`,`c’,`pad`,`_drds_implicit_id_` FROM `sbtest1`
AS `sbtest1` WHERE (C`k’IN(?)) AND (`_drds_implicit_id_` IN (...)))")
+---------+-------------+-------------+--------+----------+-----+----------------+------------+-----------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------+-----------+
1 row in set (0.0
2 sec)
mysql>explain advisor SELLECT id,k,c,pad FROM sbtest WHERE k IN (3)\G
//格式化一下
****************************1.row**********************************
IMPROVE_VALUE:9850.8%
IMPROVE_CPU:-65.5%
IMPROVE_MEM:-100.0%
IMPROVE_IO:1775918.2%
IMPROVE_NET:137.5%
BEFORE_VALUE:1000560010
BEFORE_CPU:10
BEFORE_MEM: 0
BEFORE IO:195362
BEFORE_NET:4.7
AFTER_VALUE:100550292E7
AFTER_CPU:29
AFTER_MEM:237.6
AFTER_I0:11
AFTER_NET:2
ADVISE_INDEX: ALTER TABLE `app_b`.`sbtest1’ ADD GLOBAL INDEX__advise_indexgsi_sbtest1_k`(`k`) DBPARTITION BY HASH(`k`);
NEW_PLAN:
Project(id="id",k="k",c="c",pad="pad")
BKAJoin(condition="_drds_implicit_id_ =_drds_implicit_id_ AND id <=> id", type="inner")
IndexScan(tables="APP_B_000003_GROUP.sbtest1__what_if_qsi_k_iakx",sal="SELECT `id’。`k’’_drds_implicit_id_’FROM `sbtest1..what_if. gsi_k` AS `sbtest1__what_if_gsi_k` WHERE (`k` IN(?))")
Gather(concurrent=true)
LogicalView(tables="[000000-000015].sbtest1_D2dF",shardCount=16,sql="SELECT `id`,`C`,`pad`,'_drds_implicit_id_` FROM `sbtest1
AS`sbtest1’ WHERE (C`k’IN(?))AND (_drds_implicit_id_` IN (...)))"
Gather(concurrent=true)
LoaicalViewCtables="[000000-0000151.sbtest1_D2dF".shardCount=16.sal="SELECT `id`.`c’、`pad`.`_drds_implicit_id_` FROM `sbtestl AS `sbtest1` WHERE(C`k’IN(?)) AND(`_drds_implicit_id_` IN (.….)))")
INFO:GLOBAL_INDEX
1 row in set (0.04 sec)
//一行一行看给出的建议,第一个是”improve value“ 意思是如果按照建议去做这条 sql 语句能提升的性能大概是原来的100倍,接近这样一个效率的提升,还是非常可观的。然后其他下面会给出一系列在 CPU,内存, IO和网络等方面的提升,这都是一些其他的指标。第一部分就是会给出按照建议,如果加索引的话,加完前后性能通过各个视角来进行一个对比。
//第二部分就具体建议加的索引,这里就直接给出了加索引的 sql ,首先需要 ALTER TABLE sbtest1 这张表加一个全局二级索引,有个 GLOBAL INDEX 这样的索引它加在了 k 列上。所以那张表的 sql 语句使用 k 来做 where 条件,但是 k 不是我们的索引,所以按照经验来看,应该是在 k 上加一个点,刚好 sql advisor也给出了同样的一个建议。
//第三部分是加索引之后,新的执行计划看上去比之前更复杂,这里面关键的一个点是会首先用索引表查到对应的主键 ID 然后用这个ID 再跟其他表做 BKA 的join,会加速查询的过程。这就是一个简单的 sql advisor 输出。想了解详细的说明可以看官方文档或者关注知乎专栏,里面都有详细的说明。了解这些之后就采纳它的建议。直接无脑的将它给的建议 sql 直接复制过来执行,因为这张表里面现在有3200万数据需要等待,大概十几分钟的时间。所以这边提前准备了另外一个已经加好所有的一张表,也就是刚才看到的另外一个库。
mysql> use app_b_idx;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+-----------------------------+
| TABLES_IN_APP_B_IDXI |
+-----------------------------+
| sbtest1 |
+-----------------------------+
1 row in set(0.00 sec)
mysql>show create table
//跟 app_b库里面的表一模一样,除了加入了 k 索引,执行完 sql advisor 建议之后的效果,同时看一下这张表的数据。
|
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| sbtest1 | CREATE TABLE 'sbtest1`(
'id'int(10) UNSIGNED NOT NULL,
'k’int(10) UNSIGNED NOT NULL DEFAULT '0',
'c’char(120) NOT NULL DEFAULT…
'pad` char(60) NOT NULL DEFAULT'',
KEY `xid`(id`),
GLOBAL INDEX`__advise_index_gsi_sbtest1_k`(`k`) COVERING (`id`)DBPARTITION BY HASHC`k)
)ENGINE=InnODB AUTO_INCREMENT =32703542 DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE =utf8mb4_0900_ai_ci dbpartition by hash(`id`)|
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql>select count (id)from sbtest1;
+-------------+
| count (id)|
+-------------+
| 32000000 |
+-------------+
1 row in set (11.29 sec)
mysel>
//假设按照 sql advisor 的建议在这张表加上索引,把刚才的业务停掉,切到 app_b _ 库里面来模拟成功加上索引
//因为 app_b 是新的业务对应的库,数据比较多,加索引时间比较长,所以已经建好了一个已经加载完成加索引一模一样的数据的库,这个时候需要在业务里面把之前的库切为 app_b index 来模拟看执行效果。
admin@polardb-x-demo~/class-6>1s
sql-advisor-and-ccl.yaml sysbench-prepare-app_a.yaml sysbench-select-k-idx.yaml
sysbench-oltp.yaml sysbench-prepare-app_b.yaml sysbench-select-k.yaml
admin@polardb-x-demo ~/class-6> kc apply-f sysbench-select-k.yaml job.batch/sysbench-point-select-k-test created
admin@polardb-x-demo ~/class-6> kc get jobs
NAME COMPLETIONS DURATION AGE
sysbench-oltp-test 0/1 16m 16m
sysbench-point-select-k-test 0/1 12m 12m
sysbench-prepare-app-a-d
ata-test 1/1 14s 17m
admin@polardb-x-demo ~/class-6> kc delete job sysbench-point-select-k-test
job.batch “sysbench-point-select-k-test" deleted
admin@polardb-x-demo ~/class-6>vimdiff sysbench-selsct- k-idx.yaml sysbench-selsct-k-idx.yaml
//对比一下即将要跑的任务跟原来任务一样,除了所对应的库不一样
+-- 22 lines: apiVersion: batch/v1-------------------------------
args:
- --db-driver=mysql
- --mysql-host=$(SQL_ADVISOR_AND_CCL_SERVICE_HOST)
- --mysql-port=$(SQL_ADVISOR_AND_CCL_SERVICE_PORT )
- --mysql-user=$(SQL_ADVISOR_AND_CCL_USER)
- --mysql_password-$(SQL_ADVISOR_AND_CCL_PASSWD)
- --mysql-db=app_b_idx
- --mysql-table-engine=innodb
- --rand-init=on
- --max-requests=0
- --oltp-tables-count=1
- --report-interval=5
- --oltp-table-size=32000000
+-- 10 lines:- --oltp_skip_trx=on---------------------------------
+-- 22 lines: apiVersion: batch/v1-------------------------------
args:
- --db-driver=mysql
- --mysql-host=$(SQL_ADVISOR_AND_CCL_SERVICE_HOST)
- --mysql-port=$(SQL_ADVISOR_AND_CCL_SERVICE_PORT )
- --mysql-user=$(SQL_ADVISOR_AND_CCL_USER)
- --mysql_password-$(SQL_ADVISOR_AND_CCL_PASSWD)
- --mysql-db=app_b
- --mysql-table-engine=innodb
- --rand-init=on --max-requests=0
- --oltp-tables-count=1
- --report-interval=5
- --oltp-table-size=32000000
+-- 10 lines:- --oltp_skip_trx=on---------------------------------
//把业务 B 起来
2 files to edit
admin@polardb-x-demo ~/class-6> kc apply -f sysbench-sele ct-k-idx.yaml
job.batch/sysbench-point-select-k-test created
admin@polardb-x-demo ~/class-6>
//再来看监控数据情况,给系统一点时间把没加索引之前的 sql 消化掉,报错已经在下降了,sql 限流功能所限制的 sql 越来越少,已经清零。
mysql> show processlist;
//再来看一下连接情况
+-----------+
1 row in set(11.29 sec)
mysql> show processlist;
+-+-------------+----------------+-----+--------+----+----------+
| ID | USER | HOST | DB COMMAND | TIME | STATE | INFO | TRACEID |
|18192 | polardbx_root |172.17.0.1:7113 | app_b_idx | SLEEP | 0 | | NULL | 3f8770ebc802004 |
| 17972 | polardbx_root | 172.17.0.1:34086 | app_a | Query | 0 | |SELECT c FROM sbtest1 WHERE id | 13f8770ec4402001 |
| 18196 | polardbx_root | 172.17.0.1:44693 | ap_b_idx | Query | 0 | | SELECT id,k,c, pad | 13f8770ec4802003|
| 17976 | polardbx_root | 172.17.0.1:49259 | app_a | Query | 0 | | UPDATE sbtest1 SET c=30604590|13f8770ec4402002 |
| 17969| polardbx_root | 172.17.0.1:63296 | app_a | Query | 0 | | SELECT c FROM sbtest1 WHERE id | 13f8770ec4802002 |
| 18193| polardbx_root | 172.17.0.1:56543 | app_b_idx | SLEEP | 0 | | NULL | 13f8770ebc402005 |
| 17973| polardbx_root | 172.17.0.1:7172 | app_a | Query | 0| |SELECT c FROM sbtest1 WHERE id | 13f8770ec4402003 |
| 18197 | polardbx_root | 172.17.0.1:5651 | app_b_idxi | Query |0| |SELECT id,k,c,pad | 13f8770ebc402006 |
| 9 | polardbx_root | 172.17.0.1:45597 | __cdc__ | SLEEP | 0 | | NULL | 13f8770eb9802005 |
| 18173| polardbx_rootl | 172.17.0.1:16261 | __cdc__ | SLEEP | 0 | | NULL | 13f877046f002000 |
| 17935 | polardbx_root | 127.0.0.1:40582 | app_b_idx | Query | 0 | |show processlist | 13f8770ec4402000 |
| 17970 | polardbx_root | 172.17.0.1:63100 | app_a | SLEEP | 0 | | NULL | 13f8770ebc802000|
| 18194 | polardbx_root | 172.17.0.1:23327 | app_b_idxI | Query | 0 | | SELECT id. pad |13f8770ebc802008 |
| 17974 I polardbx_root | 172.17.0.1:21582 | app_a | Query | 0 | | SELECT c FROM sbtest1 WHERE id | 13f8770ebc802007 |
| 18198 | polardbx_root | 172.17.0.1:20263 | app_b_idx | SLEEP | 0 | NULL |13f8770ebc802002 |
| 17971 | polardbx_root | 172.17.0.1:52470 | app_a ISLEEP | 0 | | NULL |13f8770ebc402003 |
| 18195 I polardbx_root | 172.17.0.1:28678 | app_b_idx | Query | 0 | SELECT id,k, c, pad |13f8770ebc402004|
| 17975 | polardbx_root | 172.17.0.1:60296 | app_a | Query | 0 | | IDELETE FROM sbtest1 WHERE id=8 | 13f8770ec4802000 |
| 18199 | polardbx_root | 172.17.0.1:21821 | app_b_idxI | Query | 0 | | SELECT id,k,c, pad | 13f8770ec4802001|
19 rows in set (0.03 sec)
mysql>show ccl_rules;
//业务流量来自于两个库,一个是 app_a 老的业务,第二个是 app_b_idx 加完了索引新的业务在正常运行。
//目前系统总的 QPS 大概恢复到了4500,老的业务恢复正常,新的业务正常运行,期间用到了 PolarDB-X 提供的一系列跟处理 mysql 相关的能力。
//看加完索引之后 sql 的执行效率
ATED_TIME |
+----+---------------+------------+------------+--------+-------+-------------+----------------+-----------------------+---------------------------------+
| 1 | block_bad_sal | 0| 0| 2963657 | 2963656 | 2963657| 1| 0|
0| 600| 1| 1 | SELECT | polardbx_root@% | app_b.* | ["pad"] | NULL | NULL | 2022-03-11 16:18:42 |
+----+---------------+------------+------------+--------+-------+-------------+----------------+-----------------------+---------------------------------+
1 row in set (0.00 sec) mysql> show ccl_rules;
+----+---------------+------------+------------+--------+-------+-------------+----------------+-----------------------+---------------------------------+
|NO. | RULE_NAME | RUNNING | WAITING | KILLED | MATCH _HIT_CACHE |TOTAL_MATCH |ACTIVE_NODE_COUNT | MAX_CONC URRENCY_PER_NODE | WAIT_QUEUE_SIZE_PER_NODE | WAIT_ TIMEOUT|FAST_MATCH|LIGHT_WAIT|SQL_TYPEIUSER |TABLE| KEY WORDS |TEMPLATE ID |QUERY|CRE ATED_TIME|
+----+---------------+------------+------------+--------+-------+-------------+----------------+-----------------------+---------------------------------+
| 1 | block_bad_sql | 0 | 0| 3963657 | 2963656 | 2963657 | 1 | 0 | 0 | 600 | 1| 1| SELECT I polardbx_root@% | app_b.* | ["pad"] | NULL |2-03-11 16:18:42 |
+----+---------------+------------+------------+--------+-------+-------------+----------------+-----------------------+---------------------------------+
1 row in set (0.00 sec)
mysql> drop ccl_rule block_bad_sql;
Query OK, 1 row affected (0.02 sec)
mysql> show ccl_rules;
Empty set (0.03 sec)
mysql> SELLECT id,k,c,pad FROM sbtest1 WHERE k IN(3);
//只需要0.01秒执行完成
Empty set (0.01 sec)
mysql>show slow;
//用 clear slow 指令把当前库里面 mysql 都清理掉,clear slow 目前按照库级别来清理 mysql ,有三个库需要清理三次
| 13f87458a2002001 | polardbx_root |172.17.0.1|2022-03-11 08:18:04 |11716| 2| SELECT id. k. c. pad FROM sbtest1 WHERE k IN (?)|
| 13f873cdf8802000 | polardbxroot | 172.17.0.1 |2022-03-11 08:15:42| 11684 | 0 I SELECT id, k, c, pad FROM sbtest1 WHER
E k IN(?)|
| 13f8746333002001| polardbx_root |172.17.0.1 2022-03-11 08:18:15 |11641| 0 | SELECT id,k, c, pad FROM sbtest1 WHER E k IN (?)|
| 13f873fbdb002000 |polardbxroot|172.17.0.1 2022-03-11 08:16:29|11628 | 0| SELECT id. k. c. pad FROM sbtest1 WHER E k IN(?)|
| 13f87474cc002001 | polardbx_root |172.17.0.1 2022-03-11 08:18:33 | 11624 0 | SELECT id, k, c, pad FROM sbtest1 WHER k IN (?)|
| 13f8746f9f002000 | polardbx_root |172.17.0.1 2022-03-11 08:18:28 |11609| 0 | SELECT id. k. c. pad FROM sbtest1 WHER E k IN(?)|
| 13f873c07c402005 | polardbx_root |172.17.0.1 2022-03-11 08:15:29 |11606| 0 | SELECT id. k. c. pad FROM sbtest1 WHER
E k IN (?)|
| 13f87407ed002001 | polardbx_root|172.17.0.1 2022-03-11 08:16:42 |11578 |1 | SELECT id, k, c, pad FROM sbtest1 WHER
E k IN(?)|
+------------+-------------+-----------+------------+-------------------------------+
100 rows in set (0.01 sec)
//1、
mysql >clear slow;
Query OK,0 rows affected (0.00 sec)
mysql >clear slow;
//2、
mysql> use app_b;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> clear slow;
Query OK, 0 rows affected (0.02 sec)
mysql> use app_b;
//3、三个库需要清理三次
mysql> use app_a;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed mysql> clear slow;
Query OK, 0 rows affected (0.00 sec)
mysql> use app_a;
//经过前面一系列操作之后,系统的 mysql 已经全部成功的被处理掉,新的 mysql 已经不再产生
eat__`(id,sname, gmt_modified) values(1,"heartbeat','2022- 03-11 08:16:07.577') |
+----------------------+-----------+-----------------+----------------------------------------------+
2 rows in set (0.00 sec)
mysql> show slow;
+----------------------+-----------+-----------------+----------------------------------------------+
| TRACE_ID | USER | HOST | START_TIME | EXECUTE_TIME | AFFECT_ROW | SQL
+----------------------+-----------+-----------------+----------------------------------------------+
| 13f873ce0e402000-4 | polardbx_root | 172.17.0.1 |2022-03-11 08:15:34 | 3397| 2 | replace into`__cdc__`.`__cdc_heartb
eat__`(id,sname, gmt_modified) values(1,"heartbeat','2022- 03-11 08:15:30.831') |
| 13f873f1f1402000-4 | polardbx_root | 172.17.0.1 | 2022-03-11 08:16:10 | 2710 | 2 | replace into `__cdc__`.`__cdc_heartb eat __(id,sname.gmtmodified) values(1.heartbeat','2022-03-11 08:16:07.577')
+----------------------+-----------+-----------------+----------------------------------------------+
2 rows in set (0.00 sec)
mysql>
4、PolarDB-X SQL Advisor 原理
SQL Advisor 原理简单的理解分为三个步骤。第一个 PolarDB-X 内部有一个基于代价计算的一个优化器,是一个很神奇的装置, advisor在做这样的事情首先针对 SQL 拿出、分析出里面涉及到几张表,比如在这张图里面的第二部分,分析出这个 SQL 一共涉及到一张叫 part 另外一个叫 lineitem 两张表,之后会分析两张表结构 SQL ,会看 where 条件里面用了哪些列, group by 里面用了哪些列、order by 里面用了哪些列、还有 join 用了哪些列等等,它会把这些列全部给找出来。在这个图里面也就是part 里面,比如第一列是 p brand 第二列是 container 第三列是partkey 等等。第二张表也有它对应的列,找到这些列最后取了个名字叫做 Indexable Column 是可索引的列,因为不管这些列目前有没有加索引,它把它作为一个潜在的可以加索引的一个对象,先把它们全部给列出来,这就是第一步叫做可索引列的分析。
第二步是构建索引。构建索引是将这些可索引的列选其中一列做索引或选其中的多列做一个联合的索引的方式,所以它把所有的可能性全部列出来这就叫做构建候选索引。
最后一步是基于几个假设,第一个假设是通常来说在系统当中能够产生最重要影响的索引不超过两个,第二个基于这样的假设它把前面所列出的候选的索引全部做了一个排列组合,类似于 CN2 的一个排列组合,拿到这些排列组合之后,就继续用代价评估的优化器来做计算,假设是用这样的一个索引组合、索引的集合来做索引, SQL 的代价是多少这样把所有可能的索引集合全部遍历一遍,来分析每一个可能的索引组合的代价,直到找到代价最小的,也是它认为这个就是它推荐的用来做索引这样的一个例子或者是联合索引,这就是 SQL Advisor 的基本原理,简单来说是把 SQL 面可索引的列全部找到,排列组合出所有的可能性,再做一系列的评估,找到代价最小的,然后它就告诉你,如果用这个做索引 SQL 的执行代价一定是比你现在要小的,小多少它会有前后代价的差异,这就是 SQL Advisor 给出的提升的部分数据的来源。
这就是刚才用了 CPU 的优化器里面的一个功能,叫做 what if 如果用这个来做索引代价是多少呢,如果用另外一个等等,最后会给出一个它认为最优的选项来做推荐,做完推荐之后,会生成相应一个建索引的一个语法,也就是刚才无脑复制的索引的一条 SQL 语句。这就是 SQL Advisor 基本的原理。
5、关于慢 SQL
● 业务问题:明显的数据倾斜、不合理的分片策略设置、数据返回过多等和业务使用相关问题;
● 系统问题:流量太大,资源成为瓶颈或者网络抖动造成的问题:
● 执行问题:如选错索引,选错 Join类型或顺序等问题。
SQL 限流相对来说比较容易理解,是在 SQL 执行的一个链路当中插入一个点,这个点对 CPU 做分析之后做一个规则的匹配,如果匹配到了设置的那些规则会对它进行一个并发度控制,或者进行一个拦截。造成慢 SQL 的原因是非常多的,如果抽象地来讲就是从应用将一条 SQL 发给系统,到这个系统 CN 接收之后进行解析、进行优化、进行调度,最后执行再下发到下面的 DN ,它会经过很多的步骤这个地方 mysql 可能会有三部分的原因来导致。
第一个是使用方的问题也就是业务方面的问题,有明显的数据倾斜,因为数据都是分片的,拆分数据的不合理,导致现在比如有16个分片,但是事实上的数据只分布在了两个分别上等等。明显的数据倾斜或者不合理的分配的策略,这是一个使用的问题。第二个是刚才演示的它给某张表 SQL 用了 K 来进行查询,查询 where 条件的查询,但是并没有建对应的索引,这也是属于使用的不当。另外一个是本身业务发起的 SQL 是获取到的数据量非常多,正常的业务逻辑,这个 SQL 也是个慢 SQL 但是它是个正常的慢 SQL ,这是业务视角还有很多的其他的场景。
第二个是系统的问题,比如 PolarDB-X 是因为资源不足才导致没有办法来承载那么大的业务量的请求,本身的表结构 SQL 都是没有问题的,就只是因为目前资源瓶颈,这是一类的。还有一种是应用跟 PolarDB-X 之间的网络可能出现了带宽不够或者是偶发的抖动或者是 CN 和 DN 之间也有类似的网络问题等等,这些都会导致 mysql 这些是偏外部的。
SHOW SLOW;
CLEAR SLOW;
SHOW PHYSICAL SLOW;
SHOW INFO FROM ;
AVALYZE TABLE [table_name];
SHOW STATISTICS;
EXPLAIN ;
EXPLAIN EXECUTE ;
EXPLAIN ANALTZE ;
TRACE;
SHOW TRACE;
EXPLAIN ADVISOR ;
第二个内部是 SQL 在到达 CN 之后会经过一系列复杂的处理,这个处理过程当中可能有些地方的决策会做错,所以要具体的来分析哪一步到底做错了,比如说是不是选错了索引或者是选错 join 类型等等,然后,再针对性的解决这些问题。除了 Demo 当中用到的那些工具或者指令之外 PolarDB-X 还提供了跟处理 mysql 相关的很多其他的一些工具的指令,比如可以查看系统当中的 mysql show slow 然后也可以把它清除掉 clear slow,也可以去查看下面 DN 层的物理执行的 mysql show physical slow 。另外一个指令可以看到一张表里面数据的分布,也就是可以看数据分布是否清晰。里面应该是写错了,少写了一个叫show table info from 等等还有其他一系列工具。