子查询优化--explain与profiling分析语句

  

  

  今天想的利用explain与progiling分析下语句然后进行优化。本文重点是如何通过explain与profiling分析SQL执行过程与性能。进而明白索引的重要性。

 

表的关系如下所示:

 

 

 

 

 原始的查询SQL:(根据用户的ID查看用户的权限)

SELECT *
FROM permission
WHERE permissionid IN(SELECT
                        permissionid
                      FROM rolepermission
                      WHERE roleid IN(SELECT
                                        roleid
                                      FROM user_role
                                      WHERE userid = 'b33b938faada40aeac2b5ca228336473'))

 

 

-----------------------------------不加索引的分析以及测试-----------------------------------------

1.分析

首先将三个表的索引全部去掉  (只剩下主键索引)

查看三个表的索引:

mysql> SHOW INDEX FROM  permission\G
*************************** 1. row ***************************
        Table: permission
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: permissionID
    Collation: A
  Cardinality: 50
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
1 row in set (0.00 sec)

mysql> SHOW INDEX FROM  rolepermission\G
*************************** 1. row ***************************
        Table: rolepermission
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: rolePermissionId
    Collation: A
  Cardinality: 66
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
1 row in set (0.00 sec)

mysql> SHOW INDEX FROM  user_role\G
*************************** 1. row ***************************
        Table: user_role
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: userRoleID
    Collation: A
  Cardinality: 4
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
1 row in set (0.00 sec)

mysql>

 

 

1.  explain 分析上面SQL的执行计划:

EXPLAIN
SELECT *
FROM permission
WHERE permissionid IN(SELECT
                        permissionid
                      FROM rolepermission
                      WHERE roleid IN(SELECT
                                        roleid
                                      FROM user_role
                                      WHERE userid = 'b33b938faada40aeac2b5ca228336473'))

 

 

结果:

 

 

 分析:

  ID越大越先执行,ID相同的按顺序从上向下执行。

  查询rolepermission和userrole都是全表扫描(type为All),且未用到任何索引,只有查询permission用到了主键索引。且看到后连个表的命中率也都比较低。

  关于更全的参数解释参考:https://www.cnblogs.com/qlqwjy/p/7767479.html

 

2.通过profiling分析语句:

0.可以在执行之前清空查询缓存

reset query cache;

1、 开启 profiling 参数

root@localhost : (none) 10:53:11> set profiling=1;
Query OK, 0 rows affected (0.00 sec)

通过执行 “set profiling”命令,可以开启关闭 Query Profiler 功能。

 

 2.执行query:

SELECT *
FROM permission
WHERE permissionid IN(SELECT
                        permissionid
                      FROM rolepermission
                      WHERE roleid IN(SELECT
                                        roleid
                                      FROM user_role
                                      WHERE userid = 'b33b938faada40aeac2b5ca228336473'))

 

 

3、获取系统中保存的所有 Query 的 profile 概要信息

mysql> show profiles\G
*************************** 1. row **************************
Query_ID: 1
Duration: 0.01237475
   Query: SELECT *
FROM permission
WHERE permissionid IN(SELECT
                        permissionid
                      FROM rolepermission
                      WHERE roleid IN(SELECT
                                        roleid
                                      FROM user_role

1 row in set, 1 warning (0.00 sec)

  分析  :     可以看到总共花了 12ms。

通过执行 “SHOW PROFILE” 命令获取当前系统中保存的多个 Query 的 profile 的概要信息。

 

4、针对单个 Query 获取详细的 profile 信息。

在获取到概要信息之后,我们就可以根据概要信息中的 Query_ID 来获取某个 Query 在执行过程中

详细的 profile 信息了,具体操作如下(1是上面的queryID)

mysql> show profile for query 1;
+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000819 |
| checking permissions | 0.000536 |
| checking permissions | 0.000519 |
| checking permissions | 0.000525 |
| Opening tables       | 0.000700 |
| init                 | 0.000958 |
| System lock          | 0.000989 |
| optimizing           | 0.000557 |
| statistics           | 0.000587 |
| preparing            | 0.000519 |
| executing            | 0.000492 |
| Sending data         | 0.001388 |
| end                  | 0.000512 |
| query end            | 0.000492 |
| removing tmp table   | 0.000492 |
| query end            | 0.000479 |
| closing tables       | 0.000517 |
| freeing items        | 0.000720 |
| cleaning up          | 0.000577 |
+----------------------+----------+
19 rows in set, 1 warning (0.00 sec)

 

 

如果想查看cpu,io等信息更具体的信息可以:

mysql> show profile cpu, block io for query 1;
+----------------------+----------+----------+------------+--------------+-----
---------+
| Status               | Duration | CPU_user | CPU_system | Block_ops_in | Bloc
_ops_out |
+----------------------+----------+----------+------------+--------------+-----
---------+
| starting             | 0.000819 | 0.000000 |   0.000000 |         NULL |
    NULL |
| checking permissions | 0.000536 | 0.000000 |   0.000000 |         NULL |
    NULL |
| checking permissions | 0.000519 | 0.000000 |   0.000000 |         NULL |
    NULL |
| checking permissions | 0.000525 | 0.000000 |   0.000000 |         NULL |
    NULL |
| Opening tables       | 0.000700 | 0.000000 |   0.000000 |         NULL |
    NULL |
| init                 | 0.000958 | 0.000000 |   0.000000 |         NULL |
    NULL |
| System lock          | 0.000989 | 0.000000 |   0.000000 |         NULL |
    NULL |
| optimizing           | 0.000557 | 0.000000 |   0.000000 |         NULL |
    NULL |
| statistics           | 0.000587 | 0.000000 |   0.000000 |         NULL |
    NULL |
| preparing            | 0.000519 | 0.000000 |   0.000000 |         NULL |
    NULL |
| executing            | 0.000492 | 0.000000 |   0.000000 |         NULL |
    NULL |
| Sending data         | 0.001388 | 0.000000 |   0.000000 |         NULL |
    NULL |
| end                  | 0.000512 | 0.000000 |   0.000000 |         NULL |
    NULL |
| query end            | 0.000492 | 0.000000 |   0.000000 |         NULL |
    NULL |
| removing tmp table   | 0.000492 | 0.000000 |   0.015625 |         NULL |
    NULL |
| query end            | 0.000479 | 0.000000 |   0.000000 |         NULL |
    NULL |
| closing tables       | 0.000517 | 0.000000 |   0.000000 |         NULL |
    NULL |
| freeing items        | 0.000720 | 0.000000 |   0.000000 |         NULL |
    NULL |
| cleaning up          | 0.000577 | 0.000000 |   0.000000 |         NULL |
    NULL |
+----------------------+----------+----------+------------+--------------+-----
---------+

 

 

2.测试修改SQL

将上述子查询SQL改为内连接查询:

SELECT *
FROM permission
  INNER JOIN rolepermission
    ON permission.permissionID = rolepermission.permissionId
  INNER JOIN user_role
    ON rolepermission.roleId = user_role.roleID
WHERE user_role.userID = 'b33b938faada40aeac2b5ca228336473'

 

1.explain分析:

 分析:

EXPLAIN 
SELECT *
FROM permission
  INNER JOIN rolepermission
    ON permission.permissionID = rolepermission.permissionId
  INNER JOIN user_role
    ON rolepermission.roleId = user_role.roleID
WHERE user_role.userID = 'b33b938faada40aeac2b5ca228336473'

 

结果:

 

分析:上面的子查询为四次查询,变为内连接查询为3此,且前两个未加索引,最后用到了主键索引。

 

 2.通过profilies查看查询效率:

mysql> show profiles\G
*************************** 1. row ***************************
Query_ID: 1
Duration: 0.02015850
   Query: SELECT *
FROM permission
WHERE permissionid IN(SELECT
                        permissionid
                      FROM rolepermission
                      WHERE roleid IN(SELECT
                                        roleid
                                      FROM user_role

*************************** 2. row ***************************
Query_ID: 2
Duration: 0.01072575
   Query: EXPLAIN
SELECT *
FROM permission
  INNER JOIN rolepermission
    ON permission.permissionID = rolepermission.permissionId
  INNER JOIN user_role
    ON rolepermission.roleId = user_role.roleID
WHERE user_role.userID = 'b33b938faada40aeac2b5ca228336473'
2 rows in set, 1 warning (0.00 sec)

 

 

发现变为内连接查询所花费的时间变为原来的一班,原来20ms,现在10ms。

 

多查询几次,查看效率:

mysql> show profiles\G
*************************** 1. row ***************************
Query_ID: 1
Duration: 0.01448950
   Query: SELECT *
FROM permission
WHERE permissionid IN(SELECT
                        permissionid
                      FROM rolepermission
                      WHERE roleid IN(SELECT
                                        roleid
                                      FROM user_role

*************************** 2. row ***************************
Query_ID: 2
Duration: 0.01417150
   Query: SELECT *
FROM permission
WHERE permissionid IN(SELECT
                        permissionid
                      FROM rolepermission
                      WHERE roleid IN(SELECT
                                        roleid
                                      FROM user_role

*************************** 3. row ***************************
Query_ID: 3
Duration: 0.01466100
   Query: SELECT *
FROM permission
WHERE permissionid IN(SELECT
                        permissionid
                      FROM rolepermission
                      WHERE roleid IN(SELECT
                                        roleid
                                      FROM user_role

*************************** 4. row ***************************
Query_ID: 4
Duration: 0.01308100
   Query: SELECT *
FROM permission
  inner join rolepermission
    on permission.permissionID = rolepermission.permissionId
  INNER join user_role
    on rolepermission.roleId = user_role.roleID
where user_role.userID = 'a9e65788297e4a8cb68a369522ee5af7'
*************************** 5. row ***************************
Query_ID: 5
Duration: 0.01334675
   Query: SELECT *
FROM permission
  inner join rolepermission
    on permission.permissionID = rolepermission.permissionId
  INNER join user_role
    on rolepermission.roleId = user_role.roleID
where user_role.userID = 'a9e65788297e4a8cb68a369522ee5af7'
*************************** 6. row ***************************
Query_ID: 6
Duration: 0.01289250
   Query: SELECT *
FROM permission
  inner join rolepermission
    on permission.permissionID = rolepermission.permissionId
  INNER join user_role
    on rolepermission.roleId = user_role.roleID
where user_role.userID = 'a9e65788297e4a8cb68a369522ee5af7'
6 rows in set, 1 warning (0.00 sec)

 

 

  前三条是子查询,后3条是内连接,发现内连接稍微快于子查询。

 

 

-----------------------------------加索引的分析以及测试-----------------------------------------

 0.在上面的表上加上索引

 

mysql> alter table user_role add index roleIdIndex(roleId);  #添加索引
Query OK, 0 rows affected (0.59 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table user_role add index userIdIndex(userId);
Query OK, 0 rows affected (0.53 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table rolepermission add index per_roleIdIndex(roleId);
Query OK, 0 rows affected (0.49 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table rolepermission add index per_perssionIdIndex(permissionId);
Query OK, 0 rows affected (0.38 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show index from user_role\G  #查看索引
*************************** 1. row ***************************
        Table: user_role
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: userRoleID
    Collation: A
  Cardinality: 4
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 2. row ***************************
        Table: user_role
   Non_unique: 1
     Key_name: roleIdIndex
 Seq_in_index: 1
  Column_name: roleID
    Collation: A
  Cardinality: 3
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 3. row ***************************
        Table: user_role
   Non_unique: 1
     Key_name: userIdIndex
 Seq_in_index: 1
  Column_name: userID
    Collation: A
  Cardinality: 3
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
3 rows in set (0.01 sec)

mysql> show index from rolepermission\G  #查看索引
*************************** 1. row ***************************
        Table: rolepermission
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: rolePermissionId
    Collation: A
  Cardinality: 66
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 2. row ***************************
        Table: rolepermission
   Non_unique: 1
     Key_name: per_roleIdIndex
 Seq_in_index: 1
  Column_name: roleId
    Collation: A
  Cardinality: 4
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 3. row ***************************
        Table: rolepermission
   Non_unique: 1
     Key_name: per_perssionIdIndex
 Seq_in_index: 1
  Column_name: permissionId
    Collation: A
  Cardinality: 52
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:
3 rows in set (0.01 sec)

 

 

 

1.针对子查询进行分析

1.explain分析:

EXPLAIN
SELECT *
FROM permission
WHERE permissionid IN(SELECT
                        permissionid
                      FROM rolepermission
                      WHERE roleid IN(SELECT
                                        roleid
                                      FROM user_role
                                      WHERE userid = 'a9e65788297e4a8cb68a369522ee5af7'))

 

结果:

 

发现加上索引之后查询效果明显提升,三个表到用到索引,且命中率为100%。

 

2.profile分析:

mysql> show profiles\G*************************** 2. row ***************************
Query_ID: 2
Duration: 0.01505400
   Query: SELECT *
FROM permission
WHERE permissionid IN(SELECT
                        permissionid
                      FROM rolepermission
                      WHERE roleid IN(SELECT
                                        roleid
                                      FROM user_role

*************************** 3. row ***************************
Query_ID: 3
Duration: 0.01624350
   Query: SELECT *
FROM permission
WHERE permissionid IN(SELECT
                        permissionid
                      FROM rolepermission
                      WHERE roleid IN(SELECT
                                        roleid
                                      FROM user_role

3 rows in set, 1 warning (0.00 sec)

mysql>

 

 

 数据量小没有多大影响。

 

2.针对显示内连接进行分析

 1.explain分析

EXPLAIN 
SELECT *
FROM permission
  INNER JOIN rolepermission
    ON permission.permissionID = rolepermission.permissionId
  INNER JOIN user_role
    ON rolepermission.roleId = user_role.roleID
WHERE user_role.userID = 'a9e65788297e4a8cb68a369522ee5af7'

 

 

结果:

 

 

 2.show profile分析

*************************** 4. row ***************************
Query_ID: 4
Duration: 0.01447100
   Query: SELECT *
FROM permission
  INNER JOIN rolepermission
    ON permission.permissionID = rolepermission.permissionId
  INNER JOIN user_role
    ON rolepermission.roleId = user_role.roleID
WHERE user_role.userID = 'a9e65788297e4a8cb68a369522ee5af7'
*************************** 5. row ***************************
Query_ID: 5
Duration: 0.01502600
   Query: SELECT *
FROM permission
  INNER JOIN rolepermission
    ON permission.permissionID = rolepermission.permissionId
  INNER JOIN user_role
    ON rolepermission.roleId = user_role.roleID
WHERE user_role.userID = 'a9e65788297e4a8cb68a369522ee5af7'
*************************** 6. row ***************************
Query_ID: 6
Duration: 0.01471300
   Query: SELECT *
FROM permission
  INNER JOIN rolepermission
    ON permission.permissionID = rolepermission.permissionId
  INNER JOIN user_role
    ON rolepermission.roleId = user_role.roleID
WHERE user_role.userID = 'a9e65788297e4a8cb68a369522ee5af7'
6 rows in set, 1 warning (0.00 sec)

 

posted @ 2018-05-23 12:54  QiaoZhi  阅读(1249)  评论(0编辑  收藏  举报