乐动网页版-乐动(中国)

  • 乐动网页版-乐动(中国)

    SQL语句优化提高(gāo)数据库性能

    发布于(yú): 2016-06-06    浏(liú)览: 10509    作者:王佳林

    为了获得稳定的执行性能(néng),SQL语句越简单越(yuè)好(hǎo)。对复杂的SQL语句(jù),要设(shè)法对之进(jìn)行简化,本文给大家介绍优化SQL语(yǔ)句提高(gāo)数(shù)据库性能。


    现在数据越来越复(fù)杂和(hé)庞大,很多时候影(yǐng)响程序(xù)运(yùn)行性能不理想(xiǎng)的原因中除了一部(bù)分是因为应用程序的负载确实超过了服务(wù)器的实际处理能力(lì)外,更多的是因为系统存在大量的SQL语(yǔ)句需要优(yōu)化(huà)。

    一、问题的提出

    在项目实际使用中,数据是一个长期累计的过(guò)程,随(suí)着(zhe)数据(jù)库中数据(jù)的增(zēng)加,系统的响应速度就成为目(mù)前系统需要解决的最主要的问题之一(yī)。系统优化中一个很重(chóng)要的方面就是SQL语(yǔ)句的优化。对(duì)于(yú)海量数据(jù),劣(liè)质SQL语句和优质SQL语句之间的速(sù)度(dù)差别可以达到成千上百倍,因此高(gāo)质量(liàng)的SQL语句,更能提高系统的可用性。

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

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

    1. 操作符优化

    (a) IN 操作符

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

    在许多基(jī)于基础表的查(chá)询(xún)中,为(wéi)了满足一个条件(jiàn),往(wǎng)往需要对另一个表进行联(lián)接。在这(zhè)种情况下, 使用EXISTS(NOT EXISTS)通常将提(tí)高(gāo)查(chá)询的效(xiào)率。。在子查询中,NOT IN子句将执行一个内部的排序和合并。 无论在哪种情(qíng)况下,NOT IN都是最低(dī)效(xiào)的(de) (因为它(tā)对子查询(xún)中的表执(zhí)行了(le)一个全表遍历)。。为了避免使用NOT IN ,我们可以把它改(gǎi)写(xiě)成外连接(Outer Joins)或(huò)NOT EXISTS

    例子: 
    (推荐)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='公司新闻(wén)')

     

    (b) IS NULL 或(huò)IS NOT NULL操作(判(pàn)断(duàn)字段是否为空)

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

        例子:

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

    (c) > < 操(cāo)作符(fú)(大于或小(xiǎo)于操作符)

    (推荐(jiàn))select * from dt_article where id>=101;

    (不(bú)推(tuī)荐)select * from dt_article where id>100;

    两(liǎng)者的区别(bié)在于, 前(qián)者(zhě)将直接跳到第(dì)一个id等于101的记录而后者将首先定位到id=100的记录并(bìng)且向前扫(sǎo)描到第一个id大于100的记(jì)录。

    (d)LIKE操作符(fú)

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

    一个实际例子:用dt_article表中内容可来(lái)查(chá)询, content like'%福瑞希%'这个条件(jiàn)会产生(shēng)全表扫描,如果改成contentlike '福(fú)瑞希%'则(zé)会利用(yòng)content的索引进(jìn)行(háng)范围的(de)查询,性(xìng)能肯定(dìng)大大提高。

    在很多情况下可能无法避免这种情况(kuàng),但是一定要心中有(yǒu)底,通配符如此使(shǐ)用会(huì)降(jiàng)低查询速度。然而当通配符出现在字符串其他位(wèi)置时,优化(huà)器就能(néng)利用索引(yǐn)。

    (e) UNION操作符

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

    (f) NOT

    我们要(yào)避免在索引列上使用(yòng)NOT, NOT会产生在和在索引列上使用函数相(xiàng)同的影响。 当查询列碰到”NOT,他就会停止使用索引转而执行(háng)全表扫描

    (g) OR

        通常情况下, 用UNION替换WHERE子句中的OR将(jiāng)会起到(dào)较(jiào)好的(de)效果。 对索引(yǐn)列使用(yòng)OR将造(zào)成全表扫描。 注(zhù)重, 以上规则只针对多(duō)个索引列有效。 假如(rú)有column没有被索引, 查询(xún)效(xiào)率可能(néng)会因(yīn)为你没有选择OR而降低(dī)。 在下(xià)面的例子中, title和(hé)category_id上都建有(yǒu)索引。

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

    (不推荐)select * from dt_article where title='清洗空气' or category_id=92 假如你坚持要用OR, 那就需要(yào)返回记(jì)录(lù)最少的索(suǒ)引(yǐn)列写在(zài)最前(qián)面。 
           另外在一些情况下,也可以使用IN来替(tì)代(dài)OR,     这(zhè)是一条简单易记的(de)规则,但是实际的执行效果还(hái)须检(jiǎn)验。

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

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

    (h) DISTINCT

         当提交一个包含一对多表信息的查询(xún)时,避免在(zài)SELECT子(zǐ)句中使用(yòng)DISTINCT。 一般(bān)可以考虑用EXIST替换(huàn), EXISTS 使查询更为(wéi)迅速,因为RDBMS核(hé)心模块将在子(zǐ)查询的条件一旦满(mǎn)足(zú)后,马上返(fǎn)回结果。 

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

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

    WHERE子句后(hòu)面的条(tiáo)件顺序对大数据量(liàng)表的查询会(huì)产生直接的影响。如:

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

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

    WHERE解析是采用自下而上的顺序解析WHERE子句,根据这个原理,表之间(jiān)的连接必须写(xiě)在其他WHERE条(tiáo)件(jiàn)之前, 那些可以过滤掉最(zuì)大数量记录(lù)的(de)条件(jiàn)必须写在WHERE子句的末尾。 

    3. 更多方(fāng)面(miàn)SQL优化资料分享

    (1) 选择最有效率的表名顺序(只在基于规则的优化器(qì)中有效):

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

    (2) SELECT子句中避免(miǎn)使用 ‘ * ‘:

    ORACLE在解析(xī)的过(guò)程中, 会(huì)将'*' 依次转换成所有的列名(míng), 这个工作是(shì)通过查询数据字典完(wán)成(chéng)的(de), 这意味(wèi)着(zhe)将耗(hào)费更(gèng)多的(de)时(shí)间。

    (3) 减少访(fǎng)问(wèn)数据库的(de)次数:

    ORACLE在内部执行了许多工作: 解析SQL语句, 估算索引的(de)利用率, 绑定变量(liàng) , 读数(shù)据块等。

    (4) 整合简单,无关联的数据库访问:

    如果你有(yǒu)几个简(jiǎn)单的数据库查询(xún)语句,你可以把它们整合到(dào)一个查询中(zhōng)(即(jí)使它们之间没(méi)有关系) 。

    (5) 用(yòng)TRUNCATE替代(dài)DELETE:

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

    (6) 尽量多使用COMMIT:

    只要有可(kě)能,在程序中尽量多使用COMMIT, 这样程序的性能得(dé)到提高,需求也会因为COMMIT所释放的资源而减少,COMMIT所释(shì)放的资源:

    a. 回滚段上用(yòng)于(yú)恢复数据的信息.
    b. 被程序语句(jù)获(huò)得的锁
    c. redo log buffer 中的空间

    (7) 通过内部(bù)函数提高SQL效率:

    复杂的SQL往往牺(xī)牲了执行效率(lǜ). 能(néng)够掌(zhǎng)握上面的(de)运用函数解(jiě)决问题(tí)的方法在(zài)实际(jì)工(gōng)作中是非常有(yǒu)意义的。

    (8) 使用表的别名(Alias):

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

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

    如果索引是建立在多个列上, 只有在它的第(dì)一(yī)个列(leading column)被where子句引用时,优化(huà)器才会选择使用该(gāi)索引. 这也是(shì)一条简(jiǎn)单而重(chóng)要的规则,当仅引用(yòng)索引的第二个列时(shí),优(yōu)化器(qì)使用了(le)全表扫描而忽略了索引。

    (10) 避免使用耗费资源的操(cāo)作:

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

    在线客服(fú)

    售前咨询(xún)

    售后服务

    投诉/建议(yì)

    服务热线
    0731-83091505
    18874148081

    乐动网页版-乐动(中国)

    乐动网页版-乐动(中国)