MySQL Execution Plan--合理利用隐式的业务逻辑

问题描述

优化过程中遇到一个SQL:

SELECT
SUM(user_value)
FROM user_log
WHERE del_flag = 0
AND product_id = 2324
AND user_type = 1;

其执行计划为:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: user_log
   partitions: NULL
         type: ref
possible_keys: index_sId_ty_vl_ct,IDX_product_id_oth1
          key: IDX_product_id_oth1
      key_len: 12
          ref: const,const,const
         rows: 14884
     filtered: 100.00
        Extra: Using index
1 row in set, 1 warning (0.00 sec

从执行计划来看,使用Using index(覆盖索引)已经是最优的执行计划,但每次查询扫描数据较多,影响整体查询性能。

 

优化方案

查询需要使用SUM计算user_value的总和,借用1+1+0+0+0+0+0=1+1=2的例子,进行如下测试:

SELECT
SUM(CASE WHEN user_value>0 THEN 1 ELSE 0 END) AS count1,
COUNT(1) AS count2
FROM user_log
WHERE del_flag = 0
AND product_id = 2324
AND user_type = 1;

+--------+--------+
| count1 | count2 |
+--------+--------+
|    680 |   8067 |
+--------+--------+

在假设user_value没有负值的情况下,下面两条SQL的结果相同:

##测试SQL1
SELECT
SUM(user_value),
COUNT(1) AS count2
FROM user_log
WHERE del_flag = 0
AND product_id = 2324
AND user_type = 1;

##测试SQL2
SELECT
SUM(user_value)
FROM user_log
WHERE del_flag = 0
AND product_id = 2324
AND user_type = 1
AND user_value>0;

测试SQL1的执行时间为0.00327250,其资源消耗为:

+----------------------+----------+----------+------------+--------------+---------------+-------+
| Status               | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out | Swaps |
+----------------------+----------+----------+------------+--------------+---------------+-------+
| starting             | 0.000066 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| checking permissions | 0.000012 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| Opening tables       | 0.000016 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| init                 | 0.000021 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| System lock          | 0.000015 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| optimizing           | 0.000016 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| statistics           | 0.000127 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| preparing            | 0.000019 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| executing            | 0.000011 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| Sending data         | 0.002867 | 0.002999 |   0.000000 |            0 |             0 |     0 |
| end                  | 0.000012 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| query end            | 0.000013 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| closing tables       | 0.000012 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| freeing items        | 0.000054 | 0.000000 |   0.000000 |            0 |             8 |     0 |
| cleaning up          | 0.000012 | 0.000000 |   0.000000 |            0 |             0 |     0 |
+----------------------+----------+----------+------------+--------------+---------------+-------+

而测试SQL2的执行时间为0.00072325,其资源消耗为:

+----------------------+----------+----------+------------+--------------+---------------+-------+
| Status               | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out | Swaps |
+----------------------+----------+----------+------------+--------------+---------------+-------+
| starting             | 0.000072 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| checking permissions | 0.000013 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| Opening tables       | 0.000016 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| init                 | 0.000021 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| System lock          | 0.000014 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| optimizing           | 0.000020 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| statistics           | 0.000089 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| preparing            | 0.000020 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| executing            | 0.000011 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| Sending data         | 0.000365 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| end                  | 0.000012 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| query end            | 0.000014 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| closing tables       | 0.000012 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| freeing items        | 0.000035 | 0.000000 |   0.000000 |            0 |             0 |     0 |
| cleaning up          | 0.000012 | 0.000000 |   0.000000 |            0 |             0 |     0 |
+----------------------+----------+----------+------------+--------------+---------------+-------+

在Sending data部分,两者在Durion部分差距约10倍,而测试SQL2在CPU_user部分差距更明显。

 

总结:

DBA在优化SQL时,除了从数据分布/索引结构等方面入手外,还需要从业务逻辑方面入手。

 

PS:上面的优化是假设user_value没有负值,而实际业务逻辑中user_value可能存在负值,因此以上优化纯属于瞎编。

你可能感兴趣的