陈奇网络工作室

MySQL数据库的8种常见误用介绍

本文的内容

显示

1.

1.极限陈述

2.

2.隐式转换

3.

3.关联更新和删除

4.

4.混合排序

5.

5.EXISTS语句

6.

6.条件下推

7.

7.提前缩小范围

8.

8.下推中间结果集

9.

摘要

10.

推荐阅读

如今,越来越多的客户在MySQL数据库上构建他们的应用,甚至从Oracle迁移到MySQL。但是有些客户在使用MySQL数据库时会遇到一些情况,比如响应时间慢,CPU满。阿里云RDS专家服务团队已经帮助云上的客户解决了很多急需解决的问题。《ApsaraDB专家诊断报告》中一些常见的SQL问题总结如下,供大家参考。

MySQL数据库的八种常见错误用法:

1.极限陈述

分页查询是最常用的场景之一,但通常也是最容易出现问题的。例如,对于下面的简单语句,一般DBA会考虑向type、name和create _ time字段添加一个复合索引。这样,条件排序可以有效地使用索引,性能得到快速提高。

选择*

from操作

其中type=\'SQLStats\ '

ANDname=\'SlowLog\ '

ORDERBYcreate_time

极限1000,10;

嗯,可能90%以上的DBA都会止步于此。但是当LIMIT子句变成“LIMIT 1000000,10”的时候,程序员还是会抱怨:为什么我只取10条记录还是很慢?

要知道,数据库不知道第1000000条记录从哪里开始,就算有索引也需要从头开始计算。在大多数情况下,当这种性能问题出现时,程序员是懒惰的。在前端数据浏览和翻页,或者大数据批量导出的场景下,可以将上一页的最大值作为参数作为查询条件。SQL重新设计如下:

选择*

from操作

其中type=\'SQLStats\ '

ANDname=\'SlowLog\ '

and create _ time \ ' 2017-03-16 14:00:00 \ '

ORDERBYcreate _ timelimit10

在新的设计下,查询时间基本是固定的,不会随着数据量的增加而改变。

2.隐式转换

SQL语句中查询变量和字段定义类型不匹配是另一个常见错误。例如,下面的语句:

mysql解释扩展选择*

从我的_余额b

其中b.bpn=14000000123

并且b.isverified为空;

mysql显示警告;

|警告|1739|无法使用ref accessonindex \ ' bpn \ '因为类型或集合转换在字段上进行\ '

其中,字段bpn的定义是varchar(20),MySQL的策略是将字符串转换成数字,然后进行比较。函数作用于表字段,索引无效。

以上情况可能是应用框架自动填充的参数,并非程序员的本意。目前有很多复杂的应用框架,要小心它在方便使用的同时可能给自己挖坑。

3.关联更新和删除

MySQL5.6虽然引入了物化,但需要特别注意的是,目前只是针对查询语句的优化。对于更新或删除,您需要手动将其重写为JOIN。

比如下面的UPDATE语句,MySQL实际执行的是循环/嵌套子查询,其执行时间可想而知。

更新操作o

SETstatus=\ '正在应用\ '

其中标识号(选择号

来自(SELECTo.id,

o .状态

从操作o

其中,组=123

ANDo.statusNOTIN(\'done\ ')

ORDERBYo.parent,

外径

极限1)t);

实施计划:

- - - - - - - - - -

| id | select _ type | table | type | possible _ keys | key | key _ len | ref | rows | Extra |

- - - - - - - - - -

| 1 | PRIMARY | o | index | PRIMARY | 8 | | 24 | Using where;使用临时|

| 2 |从属子查询| || || ||在读取常量表后发现不可能|

|3| DERIVED |o| ref |idx_2,idx _ 5 | idx _ 5 | 8 | const | 1 | Using where;使用文件排序|

- - - - - - - - - -

重写为JOIN后,子查询的选择方式从依赖子查询变为派生,执行速度大大加快,从7秒提高到2毫秒。

更新操作o

加入(SELECTo.id,

哦。状态

从操作o

其中,组=123

ANDo.statusNOTIN(\'done\ ')

ORDERBYo.parent,

外径

极限1) t

ONo.id=t.id

SETstatus=\ '正在应用\ '

执行计划简化为:

- - - - - - - - - -

| id | select _ type | table | type | possible _ keys | key | key _ len | ref | rows | Extra |

- - - - - - - - - -

|1|主|| || ||在读取常量表后发现不可能|

| 2 |DERIVED| o |ref| idx_2,idx _ 5 | idx _ 5 | 8 | const | 1 | Using where;使用文件排序|

- - - - - - - - - -

4.混合排序

关系型数据库不能利用索引进行混合排序。但在某些场景,还是有机会使用特殊方法提升性能的。

选择*

从我的订单o

INNERJOINmy _ evaluate aona。订单id=o . id

ORDERBYa.is_replyASC,

答。评价_时间描述

限制0,20

执行计划显示为全表扫描:

- - - - - - - - - -

| id | select _ type | table | type | possible _ key | key | key _ len | ref | rows | Extra

- - - - - - - - - -

| 1 | SIMPLE | a | ALL | idx _ orderid | NULL | NULL | NULL | 1967647 |使用文件排序|

|1|简单| o | eq _ ref | PRIMARY | PRIMARY | 122 | a . orderid | 1 | NULL |

- - - - - - - - - -

由于是_回复只有0和一两种状态,我们按照下面的方法重写后,执行时间从1.58秒降低到2毫秒。

选择?*?

从哪里?((选择?*

??从哪里?我的订单?

??内心?加入?我的_评价答?

???开?a.orderid=o.id?

????然后呢. is_reply=?0?

??订单?被谁?估_时间?DESC?

??极限?0,20)?

?工会?全部?

?(选择?*

??从哪里?我的订单?

??内心?加入?我的_评价答?

???开?a.orderid=o.id?

????然后呢. is_reply=?1?

??订单?被谁?估_时间?DESC?

??极限?0,20)) t?

订单?被谁?是_回复?ASC,

??考核时间?DESC?

极限?20;

5.存在语句

关系型数据库对待存在子句时,仍然采用嵌套子查询的执行方式。如下面的结构化查询语言语句:

选择*

来自我的邻居

LEFTJOINmy_neighbor_apply sra

ONn.id=sra.neighbor_id

ANDsra.user_id=\'xxx\ '

主题状态四

和现有者(选择一

发件人信息

其中n.id=m.neighbor_id

ANDm.inuser=\'xxx\ ')

ANDn.topic_type 5

执行计划为:

- - - - -

| id |?select_type?|表|?类型?| possible_keys |?钥匙?| key_len |?裁判?|行|?额外?|

- - - - - - - - - -

|?1?|主|?n?|全部|?| NULL |?NULL?| NULL |?1086041?|使用位置|

| 1 |?初级?| sra |?裁判?| |?idx_user_id?| 123 |?const?| 1 |?用在哪里?|

|?2?|从属子查询|?m?| ref |?| idx_message_info |?122?| const |?1?|使用索引条件;使用哪里|

- - - - - - - - - -

去掉存在更改为加入,能够避免嵌套子查询,将执行时间从1.93秒降低为一毫秒。

选择*

来自我的邻居

INNERJOINmessage_info m

ONn.id=m.neighbor_id

ANDm.inuser=\'xxx\ '

LEFTJOINmy_neighbor_apply sra

ONn.id=sra.neighbor_id

ANDsra.user_id=\'xxx\ '

主题状态四

ANDn.topic_type 5

新的执行计划:

- - - - - - - - - -

| id | select _ type | table | type | possible _ keys | key | key _ len | ref | rows | Extra |

---- ------------- ------------ -------- --------------- --------- --------- ------- -------- ----------------------------------------------------

| id |select_type| table |type| possible_keys |key| key_len |ref| rows |Extra|

---- ------------- ------------ -------- --------------- --------- --------- ------- -------- ----------------------------------------------------

|1| PRIMARY |derived2| ALL |NULL| NULL |NULL| NULL |15| Using temporary; Using filesort |

| 1 |PRIMARY| u |eq_ref| PRIMARY |PRIMARY| 4 |o.uid| 1 |NULL|

|1| PRIMARY |p| ALL |PRIMARY| NULL |NULL| NULL |6| Using where; Using join buffer (Block Nested Loop) |

| 2 |DERIVED| o |index| NULL |idx_1| 5 |NULL| 909112 |Using where|

---- ------------- ------------ -------- --------------- --------- --------- ------- ---

8. 中间结果集下推

再来看下面这个已经初步优化过的例子(左连接中的主表优先作用查询条件):

SELECTa.*,

c.allocated

FROM(

SELECTresourceid

FROMmy_distribute d

WHEREisdelete=0

ANDcusmanagercode=\'1234567\'

ORDERBYsalecodelimit20) a

LEFTJOIN

(

SELECTresourcesid,sum(ifnull(allocation,0) *12345) allocated

FROMmy_resources

GROUPBYresourcesid) c

ONa.resourceid=c.resourcesid

那么该语句还存在其它问题吗?不难看出子查询c 是全表聚合查询,在表数量特别大的情况下会导致整个语句的性能下降。

其实对于子查询c,左连接最后结果集只关心能和主表resourceid能匹配的数据。因此我们可以重写语句如下,执行时间从原来的2秒下降到2毫秒。

SELECTa.*,

c.allocated

FROM(

SELECTresourceid

FROMmy_distribute d

WHEREisdelete=0

ANDcusmanagercode=\'1234567\'

ORDERBYsalecodelimit20) a

LEFTJOIN

(

SELECTresourcesid,sum(ifnull(allocation,0) *12345) allocated

FROMmy_resources r,

(

SELECTresourceid

FROMmy_distribute d

WHEREisdelete=0

ANDcusmanagercode=\'1234567\'

ORDERBYsalecodelimit20) a

WHEREr.resourcesid=a.resourcesid

GROUPBYresourcesid) c

ONa.resourceid=c.resourcesid

但是子查询a 在我们的SQL语句中出现了多次。这种写法不仅存在额外的开销,还使得整个语句显的繁杂。使用WITH语句再次重写:

WITH a AS

(

SELECTresourceid

FROMmy_distribute d

WHEREisdelete=0

ANDcusmanagercode=\'1234567\'

ORDERBYsalecodelimit20)

SELECTa.*,

c.allocated

FROMa

LEFTJOIN

(

SELECTresourcesid,sum(ifnull(allocation,0) *12345) allocated

FROMmy_resources r,

a

WHEREr.resourcesid=a.resourcesid

GROUPBYresourcesid) c

ONa.resourceid=c.resourcesid

总结

数据库编译器产生执行计划,决定着SQL的实际执行方式。但是编译器只是尽力服务,所有数据库的编译器都不是尽善尽美的。上述提到的多数场景,在其它数据库中也存在性能问题。了解数据库编译器的特性,才能避规其短处,写出高性能的SQL语句。

程序员在设计数据模型以及编写SQL语句时,要把算法的思想或意识带进来。编写复杂SQL语句要养成使用WITH语句的习惯。简洁且思路清晰的SQL语句也能减小数据库的负担^^。

更多关于云服务器,域名注册,虚拟主机的问题,请访问西部数码代理商官网:www.chenqinet.cn

后台-系统设置-扩展变量-手机广告位-内容页底部广告位3