koko体育app官网入口(官方)APP下载IOS/Android通用版/手机app




    koko体育app官网入口(官方)APP下载IOS/Android通用版/手机app

    SQL语(yǔ)句优化提高数(shù)据库性能

    发布于: 2016-06-06    浏览: 10510    作(zuò)者:王佳(jiā)林

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


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

    一、问题的提出(chū)

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

    二、SQL语句编写注意问题

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

    1. 操作符优化(huà)

    (a) IN 操作符

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

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

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

     

    (b) IS NULL IS NOT NULL操作(zuò)(判断字段是否为空)

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

        例子:

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

    (c) > 及(jí) < 操作符(大于(yú)或小于操作符)

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

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

    两者(zhě)的区别在(zài)于, 前者将直接跳到第一个id等于101的记录而后(hòu)者将首先定位到(dào)id=100的(de)记录并且(qiě)向前扫描到第一个id大于(yú)100的记录。

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

    LIKE操(cāo)作符可以(yǐ)应用通配符查询,里(lǐ)面的(de)通(tōng)配(pèi)符组(zǔ)合(hé)可能达到(dào)几乎是任意的查询,但是如果用得不好则(zé)会产生性能上的问题,如like '%福瑞希(xī)%'这(zhè)种(zhǒng)查(chá)询不会引用索(suǒ)引,而like'福瑞(ruì)希%'则会引用范(fàn)围(wéi)索(suǒ)引。

    一个实际例子(zǐ):用(yòng)dt_article表中内容(róng)可来查询, content like'%福瑞希%'这个(gè)条件会产生全表扫描,如果改成contentlike '福瑞希%'则会利(lì)用(yòng)content的(de)索引进行范围的查询,性能肯(kěn)定大(dà)大提高。

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

    (e) UNION操作符

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

    (f) NOT

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

    (g) OR

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

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

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

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

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

    (h) DISTINCT

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

    2. SQL书写的影响

     (a) WHERE后面的条件(jiàn)顺(shùn)序影(yǐng)响(xiǎng)

    WHERE子(zǐ)句后面的条件顺序对大(dà)数据量表的查询会产生直(zhí)接的影响。如:

    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中category_id(电压等(děng)级(jí))及is_hot(销户(hù)标(biāo)志)两个字段(duàn)都没(méi)进行(háng)索引(yǐn),所以执行的时候都是全(quán)表(biǎo)扫描,第一条(tiáo)SQL的(de)is_hot=1在记录集内比率为(wéi)99%,而category_id=92的比率只为1%,在进行(háng)第一条(tiáo)SQL的时候99%条记录都(dōu)进行category_id及is_hot的比较,而在进行第二条SQL的时候1%条(tiáo)记录(lù)都进行category_id及(jí)is_hot的比较,以(yǐ)此可以(yǐ)得出(chū)第二条SQL的CPU占用率明显(xiǎn)比第一(yī)条(tiáo)低。

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

    3. 更(gèng)多方面SQL优化资料分(fèn)享

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

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

    (2) SELECT子(zǐ)句中避免使用 ‘ * ‘:

    ORACLE在解析(xī)的过程中, 会将'*' 依(yī)次转换成所有的列名, 这(zhè)个(gè)工作是通过查询数据字典完成的(de), 这意味着将耗费更(gèng)多的时间。

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

    ORACLE在内部执(zhí)行了许多(duō)工作: 解析SQL语句, 估算(suàn)索引(yǐn)的利用率, 绑定变量(liàng) , 读数据块等。

    (4) 整合(hé)简(jiǎn)单,无关(guān)联的数据库(kù)访问:

    如果你有几个简单的数据(jù)库查询语(yǔ)句(jù),你可以把它(tā)们整(zhěng)合到一(yī)个查询中(即使它们之(zhī)间没有关系) 。

    (5) 用TRUNCATE替代DELETE:

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

    (6) 尽量(liàng)多(duō)使用COMMIT:

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

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

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

    复杂的(de)SQL往往牺牲了执行(háng)效率. 能够掌握上面的运用函数解决问题的方法在实际工作中是非常(cháng)有意义的。

    (8) 使用(yòng)表的别名(Alias):

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

    (9) 总是(shì)使用索引的第一(yī)个列:

    如果索(suǒ)引(yǐn)是建立在多个(gè)列(liè)上, 只有在它的(de)第一个列(leading column)被where子(zǐ)句引用时,优化器才会选择使(shǐ)用该索引. 这也是一条简(jiǎn)单(dān)而重要的规则,当仅(jǐn)引用索(suǒ)引的第二个列时,优化器使用了全(quán)表扫描而忽略了索(suǒ)引。

    (10) 避(bì)免(miǎn)使用(yòng)耗(hào)费资源的(de)操作(zuò):

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

    在线客服

    售前咨询

    售后服务

    投诉/建议

    服务热(rè)线
    0731-83091505
    18874148081

    koko体育app官网入口(官方)APP下载IOS/Android通用版/手机app

    koko体育app官网入口(官方)APP下载IOS/Android通用版/手机app