mysql优化总结

SQL优化目的:

降低响应时间
直接影响用户体验度

降低资源使用率
主要体现在IO和CPU上,网络、内存消耗

优化原则:

1.IN子查询改成JOIN
2.NOT IN子查询改成LEFT JOIN
3.消除无效子查询
4.禁用select *,只读取所需字段
5.LIMIT M, N 大分页修改成JOIN形式
6.大结果集 limit限制,where限制,分页读取
7.使用where xx IN (),IN子句中元素建议小于200
8.多表Join时,注意比较字段类型一致,避免隐式类型转换
9.Where子句右值都用单引号括起,避免隐式类型转换
10.Update/delete根据索引删除,禁用Update/delete … limit N结构SQL
11.禁止对索引列进行数值计算

1.类型转化(避免隐式的类型转换)

mysql> desc tb_shop_order
    -> ;
+----------------------+----------------+------+-----+---------------------+-----------------------------+
| Field                | Type           | Null | Key | Default             | Extra                       |
+----------------------+----------------+------+-----+---------------------+-----------------------------+
| id                   | varchar(60)    | NO   | PRI | NULL                |                             |
| gorder_id            | varchar(60)    | NO   | MUL | NULL                |                             |
| product_type         | char(4)        | NO   | MUL | NULL                |                             |
| order_time           | timestamp      | NO   | MUL | 0000-00-00 00:00:00 |                             |
| order_status         | tinyint(2)     | NO   | MUL | NULL                |                             |
| order_amount         | decimal(12,2)  | NO   |     | 0.00                |                             |
| save_amount          | decimal(12,2)  | NO   |     | 0.00                |                             |
| pay_amount           | decimal(12,2)  | NO   |     | 0.00                |                             |
| logistics_amount     | decimal(12,2)  | YES  |     | NULL                |                             |
| fee                  | decimal(12,2)  | NO   |     | 0.00                |                             |
| original_pay_amount  | decimal(12,2)  | NO   |     | 0.00                |                             |
| cost_amount          | decimal(12,2)  | NO   |     | 0.00                |                             |
| buy_account_id       | varchar(60)    | NO   | MUL | NULL                |                             |
| pre_close_time       | timestamp      | NO   | MUL | 0000-00-00 00:00:00 |                             |
| update_time          | timestamp      | NO   | MUL | CURRENT_TIMESTAMP   | on update CURRENT_TIMESTAMP |
| settle_up            | tinyint(1)     | NO   | MUL | NULL                |                             |
| success_time         | timestamp      | NO   |     | 0000-00-00 00:00:00 |                             |
| merchant_id          | int(10)        | NO   | MUL | NULL                |                             |
| merchant_name        | varchar(100)   | NO   | MUL | NULL                |                             |
| merchant_account     | varchar(100)   | YES  |     | NULL                |                             |
| merchant_tel         | varchar(60)    | YES  |     | NULL                |                             |
| merchant_order_id    | varchar(100)   | YES  | MUL | NULL                |                             |
| merchant_amount      | decimal(12,2)  | YES  |     | NULL                |                             |
| activity_id          | int(10)        | YES  |     | NULL                |                             |
| activity_type        | tinyint(2)     | YES  |     | NULL                |                             |
| order_desc           | varchar(1000)  | YES  |     | NULL                |                             |
| remark               | varchar(500)   | YES  |     | NULL                |                             |
| can_deliver          | tinyint(1)     | YES  |     | NULL                |                             |
| can_settle_up        | tinyint(1)     | YES  |     | NULL                |                             |
| can_refund           | tinyint(1)     | YES  |     | NULL                |                             |
| misc                 | varchar(14100) | YES  |     | NULL                |                             |
| key_words            | varchar(200)   | YES  |     | NULL                |                             |
| delivery_begin_time  | timestamp      | NO   | MUL | 0000-00-00 00:00:00 |                             |
| delivery_end_time    | timestamp      | NO   | MUL | 0000-00-00 00:00:00 |                             |
| relation_id          | varchar(60)    | YES  |     | NULL                |                             |
| order_ip             | varchar(60)    | YES  |     | NULL                |                             |
| closed_reason        | varchar(60)    | YES  |     | NULL                |                             |
| status_desc          | varchar(60)    | YES  |     | NULL                |                             |
| note                 | varchar(5120)  | YES  |     | NULL                |                             |
| refund_amount        | decimal(12,2)  | NO   |     | 0.00                |                             |
| distributor_order_id | varchar(100)   | NO   | MUL |                     |                             |
+----------------------+----------------+------+-----+---------------------+-----------------------------+
41 rows in set (0.00 sec)

 定义: `buy_account_id` varchar(60) NOT NULL COMMENT '卡号',

mysql> explain select * from tb_shop_order where buy_account_id = 190000000033703177;
+----+-------------+---------------+------------+------+--------------------+------+---------+------+--------+----------+-------------+
| id | select_type | table         | partitions | type | possible_keys      | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+---------------+------------+------+--------------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | tb_shop_order | NULL       | ALL  | ix_order_accountid | NULL | NULL    | NULL | 179981 |    10.00 | Using where |
+----+-------------+---------------+------------+------+--------------------+------+---------+------+--------+----------+-------------+
1 row in set, 3 warnings (0.00 sec)

改造之后:ref 走的是const,rows扫描的行数表少,不在是全表扫描了

mysql> explain select * from tb_shop_order where buy_account_id = '190000000033703177';
+----+-------------+---------------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------+
| id | select_type | table         | partitions | type | possible_keys      | key                | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | tb_shop_order | NULL       | ref  | ix_order_accountid | ix_order_accountid | 182     | const | 1418 |   100.00 | NULL  |
+----+-------------+---------------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

原则:Where子句右值都用单引号括起,避免隐式类型转换!!!

2.字段计算(使用函数在字段上,不走索引)

mysql> explain select * from tb_shop_order where DATE_FORMAT (order_time,'%Y-%m-%d') = '2016-04-05';
+----+-------------+---------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table         | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+---------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | tb_shop_order | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 179981 |   100.00 | Using where |
+----+-------------+---------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

改造之后:row 扫描的行数变少,不再是全表烧苗,而是范围扫描,而且

Extra Using index

这是性能很高的一种情况。当查询所需的数据可以直接从索引树中检索到时,就会出现。

mysql> explain select * from tb_shop_order where order_time > '2016-04-05 00:00:00' and order_time < '2016-04-05 23:59:59';
+----+-------------+---------------+------------+-------+----------------+----------------+---------+------+------+----------+-----------------------+
| id | select_type | table         | partitions | type  | possible_keys  | key            | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+---------------+------------+-------+----------------+----------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | tb_shop_order | NULL       | range | ix_order_otime | ix_order_otime | 4       | NULL |   89 |   100.00 | Using index condition |
+----+-------------+---------------+------------+-------+----------------+----------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

3.避免全表扫描更新

(1) update XXX set batch = '2016-09-27 16:21:01'  

where batch = '1970-01-01 08:00:30';

(2) delete from XXX where batch   <   '2016-09-26 16:21:00';

改造成根据主键批量更新:

select min(pk), max(pk) from table where batch = '1970-01-01 08:00:30';

Update … where batch = '1970-01-01 08:00:30' and pk >= min and pk < i
Update … where batch = '1970-01-01 08:00:30' and pk >= i and pk < j
Update … where batch = '1970-01-01 08:00:30' and pk >= j and pk < max

4.字段传值正确性

错误的拼接:

select * from tb_shop_order where order_time > CONCAT('2016-04-05 00:00:00','00:00:00') and order_time < CONCAT('2016-04-05 23:59:59','00:00:00');

select * from tb_shop_order where order_time > '2016-04-05 00:00:00' and order_time < '2016-04-05 23:59:59./';

5. in OR not in优化

mysql> explain select * from tb_shop_order where buy_account_id  ='190000000039541381' and product_type in (select product_type from tb_shop_gorder );
+----+-------------+----------------+------------+------+----------------------------------+--------------------+---------+-------------------------------+-------+----------+----------------------------------------+
| id | select_type | table          | partitions | type | possible_keys                    | key                | key_len | ref                           | rows  | filtered | Extra                                  |
+----+-------------+----------------+------------+------+----------------------------------+--------------------+---------+-------------------------------+-------+----------+----------------------------------------+
|  1 | SIMPLE      | tb_shop_order  | NULL       | ref  | ix_order_accountid,ix_order_prod | ix_order_accountid | 182     | const                         |     6 |   100.00 | NULL                                   |
|  1 | SIMPLE      | tb_shop_gorder | NULL       | ref  | ix_gorder_ptype                  | ix_gorder_ptype    | 12      | lp.tb_shop_order.product_type | 10333 |   100.00 | Using index; FirstMatch(tb_shop_order) |
+----+-------------+----------------+------------+------+----------------------------------+--------------------+---------+-------------------------------+-------+----------+----------------------------------------+
2 rows in set, 1 warning (0.00 sec)

改造后:

mysql> explain select t1.* from tb_shop_order t1 INNER JOIN tb_shop_gorder t2 on t1.product_type = t2.product_type  where t1.buy_account_id  ='190000000039541381';
+----+-------------+-------+------------+------+----------------------------------+--------------------+---------+--------------------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys                    | key                | key_len | ref                | rows  | filtered | Extra       |
+----+-------------+-------+------------+------+----------------------------------+--------------------+---------+--------------------+-------+----------+-------------+
|  1 | SIMPLE      | t1    | NULL       | ref  | ix_order_accountid,ix_order_prod | ix_order_accountid | 182     | const              |     6 |   100.00 | NULL        |
|  1 | SIMPLE      | t2    | NULL       | ref  | ix_gorder_ptype                  | ix_gorder_ptype    | 12      | lp.t1.product_type | 10333 |   100.00 | Using index |
+----+-------------+-------+------------+------+----------------------------------+--------------------+---------+--------------------+-------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
mysql> explain select * from tb_shop_order where buy_account_id  ='190000000039541381' and product_type not in (select product_type from tb_shop_gorder );+----+--------------------+----------------+------------+----------------+--------------------+--------------------+---------+-------+-------+----------+-------------+
| id | select_type        | table          | partitions | type           | possible_keys      | key                | key_len | ref   | rows  | filtered | Extra       |
+----+--------------------+----------------+------------+----------------+--------------------+--------------------+---------+-------+-------+----------+-------------+
|  1 | PRIMARY            | tb_shop_order  | NULL       | ref            | ix_order_accountid | ix_order_accountid | 182     | const |     6 |   100.00 | Using where |
|  2 | DEPENDENT SUBQUERY | tb_shop_gorder | NULL       | index_subquery | ix_gorder_ptype    | ix_gorder_ptype    | 12      | func  | 10333 |   100.00 | Using index |
+----+--------------------+----------------+------------+----------------+--------------------+--------------------+---------+-------+-------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

改造后:

mysql> explain select t1.* from tb_shop_order t1 LEFT  JOIN tb_shop_gorder t2 on t1.product_type = t2.product_type  where t1.buy_account_id  ='190000000039541381' and t2.product_type IS NULL;
+----+-------------+-------+------------+------+--------------------+--------------------+---------+--------------------+-------+----------+--------------------------------------+
| id | select_type | table | partitions | type | possible_keys      | key                | key_len | ref                | rows  | filtered | Extra                                |
+----+-------------+-------+------------+------+--------------------+--------------------+---------+--------------------+-------+----------+--------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | ref  | ix_order_accountid | ix_order_accountid | 182     | const              |     6 |   100.00 | NULL                                 |
|  1 | SIMPLE      | t2    | NULL       | ref  | ix_gorder_ptype    | ix_gorder_ptype    | 12      | lp.t1.product_type | 10333 |   100.00 | Using where; Not exists; Using index |
+----+-------------+-------+------------+------+--------------------+--------------------+---------+--------------------+-------+----------+--------------------------------------+
2 rows in set, 1 warning (0.00 sec)
原文地址:https://www.cnblogs.com/200911/p/6057976.html