球速体育平台-球速(中国)



球速体育平台-球速(中国)

SQL语句(jù)优(yōu)化提(tí)高(gāo)数据库性能(néng)

发(fā)布于: 2016-06-06    浏览: 10503    作者:王佳林

为(wéi)了获(huò)得稳定的执行(háng)性能,SQL语句越简单越好。对复杂的SQL语句,要设法对(duì)之进行(háng)简(jiǎn)化,本文给大家(jiā)介(jiè)绍优(yōu)化SQL语句提高数据库(kù)性能。


现在数据越来越(yuè)复杂和庞(páng)大,很多(duō)时候影响程序运行性能不理想(xiǎng)的原因中除(chú)了一部(bù)分(fèn)是因为应用(yòng)程序的负载确(què)实(shí)超过(guò)了(le)服务器的实际(jì)处(chù)理能力外,更多的是因为(wéi)系统存在大量(liàng)的(de)SQL语句需要优化(huà)。

一、问题的提(tí)出

在项目(mù)实际使用中,数据(jù)是一个长(zhǎng)期累(lèi)计的过程,随着数据库(kù)中(zhōng)数据的(de)增加,系统的响应速度(dù)就成为目前系统(tǒng)需要解决的最主要的问题之一。系(xì)统优化中一个(gè)很重要的方面就是SQL语(yǔ)句的优化。对(duì)于海量(liàng)数(shù)据,劣(liè)质SQL语句(jù)和优质SQL语句之(zhī)间的速度差别可以(yǐ)达到成千上百倍,因此高质量的(de)SQL语句(jù),更能(néng)提高系统的可(kě)用性。

二、SQL语句编写注(zhù)意问题

下面就某些(xiē)SQL语(yǔ)句的where子句编写中需要注(zhù)意(yì)的问题作详细介绍(shào)。在这些where子句中,即(jí)使某(mǒu)些列存在索引,但是由于编写了(le)劣质的SQL,系统在运行(háng)该SQL语(yǔ)句(jù)时也不能(néng)使(shǐ)用该索引,而同样使(shǐ)用全(quán)表扫描,这(zhè)就造成(chéng)了(le)响(xiǎng)应速(sù)度的极大降低。

1. 操作符优化

(a) IN 操作符

 在使用中尽量用(yòng)EXISTS替代(dài)IN、用(yòng)NOT EXISTS替代NOT IN 

在许(xǔ)多基(jī)于基础(chǔ)表的查询中,为了满足一个条件,往往需要对另(lìng)一(yī)个表进行联接(jiē)。在这种情况下, 使用EXISTS(或(huò)NOT EXISTS)通常将(jiāng)提高查询的效率。。在子查询中(zhōng),NOT IN子(zǐ)句将(jiāng)执行一个内部的排序和合并。 无论在哪种情况下,NOT IN都是最低效的 (因(yīn)为它对子查询中的表执(zhí)行了一个全表遍历)。。为了避免使用(yòng)NOT IN ,我们可(kě)以把它改写成外连接(Outer Joins)或(huò)NOT EXISTS

例(lì)子: 
(推荐)select* from dt_article where exists(select id from dt_article_category wheredt_article_category。id=dt_article。category_id andtitle='公司新闻')
(不推荐)select* from dt_article where category_id in (select id from dt_article_categorywhere title='公司新闻')

 

(b) IS NULL IS NOT NULL操作(判(pàn)断字(zì)段(duàn)是否为(wéi)空)

判断字段是(shì)否为(wéi)空(kōng)一般是不(bú)会应用索引的,因为索引是不(bú)索引空值的。不能用null作索引,任(rèn)何包含null值的列都将不会被包(bāo)含(hán)在索引(yǐn)中。即(jí)使索(suǒ)引有多列这(zhè)样的情况下,只要这些列中有一列(liè)含有(yǒu)null,该列(liè)就会从索引中排(pái)除。也就是(shì)说如果某(mǒu)列存在(zài)空值,即(jí)使对该列建索引也不会提高性能。任何在where子句中使(shǐ)用(yòng)is null或is not null的语句(jù)优化(huà)器是(shì)不允许(xǔ)使用(yòng)索(suǒ)引的。 

    例子:

(推荐)select* from dt_article where title>'';
 (不推荐)select* from dt_article where title is null;

(c) > < 操作符(大于或小于操作符)

(推(tuī)荐)select * from dt_article where id>=101;

(不推荐)select * from dt_article where id>100;

两(liǎng)者的(de)区别在(zài)于, 前者将直接跳到第一个(gè)id等(děng)于101的记录而后者将(jiāng)首先定位到id=100的记录(lù)并且向(xiàng)前扫描(miáo)到第一个id大于(yú)100的(de)记录(lù)。

(d)LIKE操(cāo)作符

LIKE操作符可以应用通配符查询,里面的通配符组合可能达到(dào)几乎是任(rèn)意的查询,但是如果用得不好(hǎo)则(zé)会产生性能(néng)上(shàng)的问(wèn)题(tí),如like '%福(fú)瑞希%'这(zhè)种查询不会引用索引(yǐn),而like'福(fú)瑞希%'则(zé)会引用(yòng)范围(wéi)索引。

一个实际例子(zǐ):用dt_article表(biǎo)中内容可来查询, content like'%福瑞希%'这个条件会(huì)产(chǎn)生全表扫描,如(rú)果改成contentlike '福瑞希(xī)%'则(zé)会利用content的索(suǒ)引进行(háng)范围的(de)查询,性(xìng)能肯定大大(dà)提高。

在很多情况下可能无法避免这种情况,但是一定要(yào)心中有底,通配符如此使用会(huì)降低查(chá)询速度。然而(ér)当通配符出(chū)现在字符串其他(tā)位置(zhì)时,优化器就能利(lì)用索引(yǐn)。

(e) UNION操作符

当SQL语句(jù)需要UNION两个(gè)查(chá)询(xún)结果集合时,这两(liǎng)个结果集合会以UNION-ALL的方式被合(hé)并, 然后在(zài)输出最终结果(guǒ)前(qián)进行去(qù)重和排序。 假如用UNION ALL替代UNION, 这(zhè)样排序(xù)就不是必要了。 效(xiào)率就会因(yīn)此得(dé)到提高。 需要注重(chóng)的是(shì),UNION ALL 将重复输出两个(gè)结(jié)果集合(hé)中(zhōng)相同记录。 因(yīn)此各位还是要从业务需求分析使用UNIONALL的可行性。 UNION 将对结果集合去重排序,这(zhè)个操作会使用到SORT_AREA_SIZE这块(kuài)内存。 对于这块内存的优化也是相(xiàng)当(dāng)重要(yào)的(de)。

(f) NOT

我们要(yào)避免(miǎn)在(zài)索引列上使用(yòng)NOT, NOT会产生(shēng)在(zài)和在索引列上使用函数相同(tóng)的影响。 当查询列碰到”NOT,他就会(huì)停止使用(yòng)索引转而(ér)执行全(quán)表(biǎo)扫描

(g) OR

    通常(cháng)情况下, 用UNION替(tì)换WHERE子句(jù)中的OR将会(huì)起到较好的效果。 对索引列使(shǐ)用OR将造(zào)成全(quán)表(biǎo)扫描。 注重, 以上规则只针对多个索引列有效(xiào)。 假如有column没有被索引, 查询效率(lǜ)可能会(huì)因为你没有选择OR而降低(dī)。 在(zài)下(xià)面的例子中, title和category_id上都建有索引(yǐn)。

(推荐)select * from dt_article where title='清洗(xǐ)空(kōng)气' union all select * from dt_article where category_id=92

(不(bú)推荐)select * from dt_article where title='清洗空(kōng)气(qì)' or category_id=92 假如你(nǐ)坚持要用OR, 那就需要返回记录最(zuì)少的索引(yǐn)列写在最前面。 
       另外在(zài)一些情况下(xià),也可以使用IN来替代OR,     这是一(yī)条简单(dān)易(yì)记的规则,但是(shì)实际的执行效果还须检验。

(推荐)select * from dt_article where category_id in (89,92)

(不推荐)select * from dt_article where category_id=92 or category_id=89

(h) DISTINCT

     当提交一个包含一对多表信息的(de)查询时,避(bì)免在SELECT子句中使用(yòng)DISTINCT。 一般可以考虑用EXIST替换, EXISTS 使查询更(gèng)为迅速(sù),因(yīn)为RDBMS核(hé)心模(mó)块将在子查询的条(tiáo)件一旦满足后,马上返回结(jié)果。 

2. SQL书(shū)写的影(yǐng)响(xiǎng)

 (a) WHERE后面的条件顺序影响

WHERE子句后面(miàn)的条(tiáo)件(jiàn)顺序(xù)对大数据量表的查询会产生直接的影响。如:

select * from dt_article where category_id=92 and is_hot=1
select * from dt_article where is_hot=1 and category_id=92 

以上两个SQL中category_id(电压等级)及is_hot(销户(hù)标志(zhì))两(liǎng)个字段(duàn)都没进行索引(yǐn),所以执行的时候都是全表扫描,第一条(tiáo)SQL的is_hot=1在记(jì)录集内比率为99%,而category_id=92的比率只为1%,在进行第一(yī)条SQL的时候99%条记(jì)录都进行category_id及is_hot的比较,而在进行第(dì)二条SQL的时候1%条记录都进行(háng)category_id及is_hot的比(bǐ)较,以此(cǐ)可以得出第二条SQL的CPU占(zhàn)用率明显比第(dì)一条低。

WHERE解(jiě)析是采用自下而上的顺序解析WHERE子句(jù),根据这个(gè)原理(lǐ),表(biǎo)之间(jiān)的连接(jiē)必须写在其他WHERE条件之前(qián), 那些可以过滤(lǜ)掉最大数量(liàng)记(jì)录的条(tiáo)件(jiàn)必(bì)须写(xiě)在(zài)WHERE子句的末尾(wěi)。 

3. 更多(duō)方面SQL优化资料分享

(1) 选择最(zuì)有效(xiào)率(lǜ)的表名顺序(只在基于规(guī)则(zé)的优化器中有效(xiào)):

ORACLE 的解析器按照从右到左的顺序处理FROM子句(jù)中(zhōng)的(de)表(biǎo)名,FROM子句(jù)中写(xiě)在最后的(de)表(基础表(biǎo) driving table)将(jiāng)被最先处理,在FROM子(zǐ)句中包含多个表的情况(kuàng)下,你必须选择记录条数最少的表作(zuò)为基础表。如果有3个以上的表连接查询(xún), 那(nà)就需要选择交叉表(intersectiontable)作为基础表, 交叉表是指那个被其他表所引用的表.

(2) SELECT子句中避免使用(yòng) ‘ * ‘:

ORACLE在解析的过程中, 会将'*' 依次转换成所有的列名, 这个工作是通(tōng)过查(chá)询数据字典完(wán)成的, 这意味着(zhe)将耗费更多的时间。

(3) 减少访问数据(jù)库的(de)次数:

ORACLE在(zài)内部(bù)执(zhí)行(háng)了许多(duō)工作: 解析SQL语句, 估算索引的利用率(lǜ), 绑定变量(liàng) , 读数据块等。

(4) 整合简单,无关(guān)联的数据库访问(wèn):

如果你有几个简单的(de)数据库查询语句,你可以把它们整(zhěng)合(hé)到一个(gè)查询中(zhōng)(即使(shǐ)它们(men)之间没(méi)有(yǒu)关系) 。

(5) 用TRUNCATE替代DELETE:

当删除表中的记录时,在通常情况下, 回(huí)滚(gǔn)段(rollbacksegments ) 用来(lái)存放可以被恢复的(de)信息. 如果你没有COMMIT事务,ORACLE会将数据恢复到删除(chú)之前的状态(准(zhǔn)确地说(shuō)是恢复到执行(háng)删除命令之(zhī)前的状况) 而当运用TRUNCATE时, 回(huí)滚段不再存(cún)放(fàng)任(rèn)何可被恢复(fù)的信息.当命令运行(háng)后(hòu),数据不能(néng)被恢复.因此(cǐ)很少(shǎo)的资源被调(diào)用,执行时(shí)间也会很短. (译者按: TRUNCATE只在删除全表适用,TRUNCATE是DDL不(bú)是DML) 。

(6) 尽量多使用COMMIT:

只要有可能,在程序中尽量多使(shǐ)用(yòng)COMMIT, 这(zhè)样程序的性能得到提高,需求也会(huì)因为COMMIT所释放(fàng)的(de)资源而减少,COMMIT所释放(fàng)的资源:

a. 回滚(gǔn)段上(shàng)用于恢复数据(jù)的(de)信息(xī).
b. 被程(chéng)序语句(jù)获得的锁(suǒ)
c. redo log buffer 中的空间

(7) 通过内部函数提高SQL效率(lǜ):

复杂的SQL往(wǎng)往牺牲了执(zhí)行效(xiào)率. 能够(gòu)掌握上面的运用函数解(jiě)决问题的(de)方法在实(shí)际工作(zuò)中是非(fēi)常有意义的(de)。

(8) 使用表的(de)别(bié)名(Alias):

当在SQL语句中连接多个表(biǎo)时, 请使(shǐ)用表的别名并把(bǎ)别名(míng)前缀于每个Column上.这样一来,就可以减少(shǎo)解析的时间(jiān)并(bìng)减少那些由Column歧义引起的语法错误。

(9) 总是使用索引的第一个列:

如果索引是建立在多个列上, 只有在它(tā)的第一个(gè)列(leading column)被where子(zǐ)句引用时,优化器才会选择(zé)使用该索引. 这也是一(yī)条简单(dān)而重要(yào)的规则,当仅引用索引的第(dì)二个(gè)列时,优化器使用了全表扫描而(ér)忽略(luè)了(le)索引。

(10) 避(bì)免使用(yòng)耗费资源(yuán)的操作:

带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的(de)SQL语句会启动SQL引擎(qíng)执行耗费资源的排序(SORT)功能. DISTINCT需要一次排序(xù)操作, 而其他的至少(shǎo)需要执行两次排(pái)序. 通常(cháng), 带(dài)有UNION, MINUS , INTERSECT的SQL语句(jù)都可以(yǐ)用其他方式重写. 如果你的数据库的SORT_AREA_SIZE调配得好(hǎo), 使用UNION , MINUS, INTERSECT也(yě)是(shì)可(kě)以(yǐ)考(kǎo)虑的(de), 毕(bì)竟它们的可读性很强。

在(zài)线客服

售前咨询

售后服务

投诉/建议

服务(wù)热(rè)线(xiàn)
0731-83091505
18874148081

球速体育平台-球速(中国)

球速体育平台-球速(中国)