乐鱼手机站入口-乐鱼online(中国)





  • 乐鱼手机站入口-乐鱼online(中国)

    SQL语句优化(huà)提高数据库性(xìng)能

    发布(bù)于: 2016-06-06    浏(liú)览: 10513    作者:王(wáng)佳(jiā)林(lín)

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


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

    一、问题(tí)的提出

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

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

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

    1. 操(cāo)作符优化

    (a) IN 操作符

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

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

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

     

    (b) IS NULL IS NOT NULL操(cāo)作(判断(duàn)字(zì)段是否为空)

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

        例(lì)子:

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

    (c) > < 操(cāo)作符(大于或(huò)小于(yú)操作符(fú))

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

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

    两者的(de)区别在于, 前者将(jiāng)直接跳到(dào)第(dì)一(yī)个id等于101的记录而后者将首先(xiān)定位到(dào)id=100的记录并且向(xiàng)前(qián)扫描(miáo)到第一个id大(dà)于100的记录。

    (d)LIKE操作符

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

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

    在很多情(qíng)况下(xià)可能(néng)无法避免这种情(qíng)况,但是一定要(yào)心中有底(dǐ),通配(pèi)符如此(cǐ)使用会降低(dī)查询(xún)速度(dù)。然而当通配符出现在字符串其他位置(zhì)时,优化器就能(néng)利用索引。

    (e) UNION操作符

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

    (f) NOT

    我们(men)要避(bì)免在索引列上(shàng)使(shǐ)用NOT, NOT会产生在和在索引列上(shàng)使用函数相(xiàng)同的(de)影响。 当查询列碰到”NOT,他就会(huì)停止使(shǐ)用索引转而(ér)执行全(quán)表扫(sǎo)描

    (g) OR

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

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

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

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

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

    (h) DISTINCT

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

    2. SQL书写的影响(xiǎng)

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

    WHERE子句(jù)后面(miàn)的条件顺序对(duì)大数据量表(biǎo)的查询会产生直接的影响。如(rú):

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

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

    WHERE解(jiě)析是采用自下而(ér)上的顺序解(jiě)析WHERE子句,根据这个原理,表之间的连接必(bì)须写在其他WHERE条件之前, 那些可以过滤掉(diào)最大数量记录的条件必须(xū)写在WHERE子句的末尾。 

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

    (1) 选择(zé)最有(yǒu)效率的表名顺序(xù)(只在基于规则的优化器(qì)中有效):

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

    (2) SELECT子句(jù)中避免使用 ‘ * ‘:

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

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

    ORACLE在内部(bù)执行了许多工作: 解(jiě)析SQL语句, 估算索(suǒ)引的利(lì)用率, 绑定变量 , 读(dú)数据块等。

    (4) 整合简单,无(wú)关联的数据库(kù)访(fǎng)问:

    如果你有(yǒu)几个简(jiǎn)单的(de)数据库查询语(yǔ)句,你可以把(bǎ)它们整合到一个查询(xún)中(即使它们之间没有关系) 。

    (5) 用(yòng)TRUNCATE替(tì)代DELETE:

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

    (6) 尽(jìn)量多使用COMMIT:

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

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

    (7) 通(tōng)过内(nèi)部函(hán)数提高SQL效率:

    复(fù)杂的SQL往往牺牲了执行效率. 能够掌握上面的运用函数解(jiě)决问题的方法在实际工(gōng)作中是非常有意(yì)义的。

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

    当在SQL语句中(zhōng)连(lián)接多个表(biǎo)时, 请使用(yòng)表的别名(míng)并把别名(míng)前缀(zhuì)于每个(gè)Column上.这样一(yī)来(lái),就可(kě)以减少解(jiě)析的(de)时间(jiān)并减少(shǎo)那些(xiē)由Column歧义引起的(de)语(yǔ)法错误(wù)。

    (9) 总是使(shǐ)用(yòng)索(suǒ)引的(de)第一个列:

    如果索引是建立在(zài)多(duō)个列上, 只(zhī)有(yǒu)在它(tā)的第一个列(leading column)被where子(zǐ)句引用时,优化器才(cái)会选择使用该索引. 这(zhè)也(yě)是一条简(jiǎn)单而(ér)重要的规则,当(dāng)仅引用索引的(de)第(dì)二个列时(shí),优化器使(shǐ)用了全表扫描而忽(hū)略了索引。

    (10) 避免使用(yòng)耗费资(zī)源的操(cāo)作(zuò):

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

    在线客服

    售前咨询

    售后服务

    投诉(sù)/建议

    服(fú)务热线
    0731-83091505
    18874148081

    乐鱼手机站入口-乐鱼online(中国)

    乐鱼手机站入口-乐鱼online(中国)