首页 > 代码库 > mysql优化总结

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)

 

mysql优化总结