SQL优化--一般步骤

使用MySQL案例库:sakila

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sakila             |
| staffdb            |
| sys                |
| test               |
| test1              |
| world              |
+--------------------+
9 rows in set (0.00 sec)
mysql> use sakila;
Database changed
mysql> show tables;
+----------------------------+
| Tables_in_sakila           |
+----------------------------+
| actor                      |
| actor_info                 |
| address                    |
| category                   |
| city                       |
| country                    |
| customer                   |
| customer_list              |
| film                       |
| film_actor                 |
| film_category              |
| film_list                  |
| film_text                  |
| inventory                  |
| language                   |
| nicer_but_slower_film_list |
| payment                    |
| rental                     |
| sales_by_film_category     |
| sales_by_store             |
| staff                      |
| staff_list                 |
| store                      |
+----------------------------+
23 rows in set (0.03 sec)

 

1.定位执行效率低的SQL语句

  • 慢查询日志,用--log-slow-queries[=file_name]选项启动时,mysqld写一个包含所有执行时间超过long_query_time秒的SQL语句的日志文件;
  • 慢查询日志在查询结束后记录,在应用反映执行效率出现问题时,慢查询日志不能定位问题,可以使用show processlist命令查看当前MySQL 在进行的进程,包括线程的状态,是否表锁等,可以实时查看SQL执行情况,同时对一些锁表操作进行优化。

2.使用explain分析低效SQL的执行计划

mysql> explain select sum(amount)from customer a,payment b where a.customer_id=b.customer_id
    -> and email='[email protected]'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
   partitions: NULL
         type: ALL
possible_keys: PRIMARY
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 599
     filtered: 10.00
        Extra: Using where
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: b
   partitions: NULL
         type: ref
possible_keys: idx_fk_customer_id
          key: idx_fk_customer_id
      key_len: 2
          ref: sakila.a.customer_id
         rows: 26
     filtered: 100.00
        Extra: NULL
2 rows in set, 1 warning (0.06 sec)

使用explain extended 加上show warnings可以看到SQL在真正被执行之前优化器做了哪些SQL改写。

3.通过show profile 分析SQL

 有时候仅仅通过explain分析执行计划不能很快定位。

查看当前MySQL是否支持profile:

mysql> select @@have_profiling;
+------------------+
| @@have_profiling |
+------------------+
| YES              |
+------------------+
1 row in set, 1 warning (0.06 sec)

 默认profiling关闭,用set语句在session级别开启profiling:

mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
|           0 |
+-------------+
1 row in set, 1 warning (0.03 sec)

mysql> set profiling = 1;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
|           1 |
+-------------+
1 row in set, 1 warning (0.03 sec)

 可以看到具体时间花费在哪里:

mysql> select count(*) from payment;
+----------+
| count(*) |
+----------+
|    16049 |
+----------+
1 row in set (0.08 sec)

mysql> show profiles;
+----------+------------+------------------------------+
| Query_ID | Duration   | Query                        |
+----------+------------+------------------------------+
|        1 | 0.02803550 | select @@profiling           |
|        2 | 0.08277525 | select count(*) from payment |
+----------+------------+------------------------------+
2 rows in set, 1 warning (0.03 sec)

mysql> show profile for query 2;
+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.030659 |
| checking permissions | 0.000043 |
| Opening tables       | 0.000042 |
| init                 | 0.000024 |
| System lock          | 0.000017 |
| optimizing           | 0.000011 |
| statistics           | 0.000056 |
| preparing            | 0.000019 |
| executing            | 0.000003 |
| Sending data         | 0.051475 |
| end                  | 0.000095 |
| query end            | 0.000051 |
| closing tables       | 0.000020 |
| freeing items        | 0.000234 |
| cleaning up          | 0.000027 |
+----------------------+----------+
15 rows in set, 1 warning (0.04 sec)

4.通过trace分析优化器如何选择执行计划

能够进一步了解为什么优化器选择A执行计划而不选择B执行计划。、

打开trace:

mysql> set optimizer_trace="enabled=on";
Query OK, 0 rows affected (0.00 sec)

设置trace可使用的最大内存大小:

mysql> SET OPTIMIZER_TRACE_MAX_MEM_SIZE=1000000;
Query OK, 0 rows affected (0.03 sec)

MySQL是如何执行SQL:

mysql> select rental_id from rental where 1=1 and rental_date >= '2005-05-25 04:00:00'and rental_date <= '2005-05-25 05:00:00' and inventory_id=4466;
+-----------+
| rental_id |
+-----------+
|        39 |
+-----------+
1 row in set (0.00 sec)

mysql> select * from information_schema.optimizer_trace\G
*************************** 1. row ***************************
                            QUERY: select rental_id from rental where 1=1 and rental_date >= '2005-05-25 04:00:00'and rental_date <= '2005-05-25 05:00:00' and inventory_id=4466
                            TRACE: {
  "steps": [
    {
      "join_preparation": {
        "select#": 1,
        "steps": [
          {
            "expanded_query": "/* select#1 */ select `rental`.`rental_id` AS `rental_id` from `rental` where ((1 = 1) and (`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00') and (`rental`.`inventory_id` = 4466))"
          }
        ]
      }
    },
    {
      "join_optimization": {
        "select#": 1,
        "steps": [
          {
            "condition_processing": {
              "condition": "WHERE",
              "original_condition": "((1 = 1) and (`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00') and (`rental`.`inventory_id` = 4466))",
              "steps": [
                {
                  "transformation": "equality_propagation",
                  "resulting_condition": "((1 = 1) and (`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00') and multiple equal(4466, `rental`.`inventory_id`))"
                },
                {
                  "transformation": "constant_propagation",
                  "resulting_condition": "((1 = 1) and (`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00') and multiple equal(4466, `rental`.`inventory_id`))"
                },
                {
                  "transformation": "trivial_condition_removal",
                  "resulting_condition": "((`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00') and multiple equal(4466, `rental`.`inventory_id`))"
                }
              ]
            }
          },
          {
            "substitute_generated_columns": {
            }
          },
          {
            "table_dependencies": [
              {
                "table": "`rental`",
                "row_may_be_null": false,
                "map_bit": 0,
                "depends_on_map_bits": [
                ]
              }
            ]
          },
          {
            "ref_optimizer_key_uses": [
              {
                "table": "`rental`",
                "field": "inventory_id",
                "equals": "4466",
                "null_rejecting": false
              }
            ]
          },
          {
            "rows_estimation": [
              {
                "table": "`rental`",
                "range_analysis": {
                  "table_scan": {
                    "rows": 16008,
                    "cost": 3300.7
                  },
                  "potential_range_indexes": [
                    {
                      "index": "PRIMARY",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
                      "index": "rental_date",
                      "usable": true,
                      "key_parts": [
                        "rental_date",
                        "inventory_id",
                        "customer_id"
                      ]
                    },
                    {
                      "index": "idx_fk_inventory_id",
                      "usable": true,
                      "key_parts": [
                        "inventory_id",
                        "rental_id"
                      ]
                    },
                    {
                      "index": "idx_fk_customer_id",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
                      "index": "idx_fk_staff_id",
                      "usable": false,
                      "cause": "not_applicable"
                    }
                  ],
                  "best_covering_index_scan": {
                    "index": "rental_date",
                    "cost": 3229.9,
                    "chosen": true
                  },
                  "setup_range_conditions": [
                  ],
                  "group_index_range": {
                    "chosen": false,
                    "cause": "not_group_by_or_distinct"
                  },
                  "analyzing_range_alternatives": {
                    "range_scan_alternatives": [
                      {
                        "index": "rental_date",
                        "ranges": [
                          "0x9975b24000 <= rental_date <= 0x9975b25000"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": false,
                        "using_mrr": false,
                        "index_only": true,
                        "rows": 10,
                        "cost": 3.0254,
                        "chosen": true
                      },
                      {
                        "index": "idx_fk_inventory_id",
                        "ranges": [
                          "4466 <= inventory_id <= 4466"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": true,
                        "using_mrr": false,
                        "index_only": false,
                        "rows": 5,
                        "cost": 7.01,
                        "chosen": false,
                        "cause": "cost"
                      }
                    ],
                    "analyzing_roworder_intersect": {
                      "usable": false,
                      "cause": "too_few_roworder_scans"
                    }
                  },
                  "chosen_range_access_summary": {
                    "range_access_plan": {
                      "type": "range_scan",
                      "index": "rental_date",
                      "rows": 10,
                      "ranges": [
                        "0x9975b24000 <= rental_date <= 0x9975b25000"
                      ]
                    },
                    "rows_for_plan": 10,
                    "cost_for_plan": 3.0254,
                    "chosen": true
                  }
                }
              }
            ]
          },
          {
            "considered_execution_plans": [
              {
                "plan_prefix": [
                ],
                "table": "`rental`",
                "best_access_path": {
                  "considered_access_paths": [
                    {
                      "access_type": "ref",
                      "index": "idx_fk_inventory_id",
                      "rows": 5,
                      "cost": 6,
                      "chosen": true
                    },
                    {
                      "rows_to_scan": 10,
                      "access_type": "range",
                      "range_details": {
                        "used_index": "rental_date"
                      },
                      "resulting_rows": 10,
                      "cost": 5.0254,
                      "chosen": true
                    }
                  ]
                },
                "condition_filtering_pct": 100,
                "rows_for_plan": 10,
                "cost_for_plan": 5.0254,
                "chosen": true
              }
            ]
          },
          {
            "attaching_conditions_to_tables": {
              "original_condition": "((`rental`.`inventory_id` = 4466) and (`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00'))",
              "attached_conditions_computation": [
              ],
              "attached_conditions_summary": [
                {
                  "table": "`rental`",
                  "attached": "((`rental`.`inventory_id` = 4466) and (`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00'))"
                }
              ]
            }
          },
          {
            "refine_plan": [
              {
                "table": "`rental`"
              }
            ]
          }
        ]
      }
    },
    {
      "join_execution": {
        "select#": 1,
        "steps": [
        ]
      }
    }
  ]
}
MISSING_BYTES_BEYOND_MAX_MEM_SIZE: 0
          INSUFFICIENT_PRIVILEGES: 0
1 row in set (0.00 sec)

5.确定问题并采取相应措施

加索引,以及常用的优化方法。。

见下篇。

你可能感兴趣的:(面经--数据库)