乐鱼网页版在线登录-leyu(中国)





乐鱼网页版在线登录-leyu(中国)

SQL语句优化提高数据库性能(néng)

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

为了获得稳定的(de)执行性能,SQL语句越简单越好。对复(fù)杂的(de)SQL语句,要设法对之进行简化,本文给(gěi)大家介绍(shào)优化(huà)SQL语句提高数据库性能。


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

一、问题的提出

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

二、SQL语句编(biān)写注意问(wèn)题

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

1. 操作(zuò)符优化

(a) IN 操作(zuò)符

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

在(zài)许多基于基础(chǔ)表(biǎo)的查询中,为了满足一(yī)个条件,往往需要对(duì)另一(yī)个表进行联接。在这(zhè)种情况下, 使用EXISTS(NOT EXISTS)通常(cháng)将提高查询的效(xiào)率。。在子查询中,NOT IN子(zǐ)句(jù)将执行一个内部的排序和合并。 无论在哪(nǎ)种(zhǒng)情况下,NOT IN都(dōu)是最低效(xiào)的(de) (因为它对子查询中的表执(zhí)行了一个全表遍(biàn)历)。。为了避免使用NOT IN ,我们可以把(bǎ)它改写成外连接(jiē)(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ī)新闻')
(不推荐(jiàn))select* from dt_article where category_id in (select id from dt_article_categorywhere title='公司新闻')

 

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

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

    例子:

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

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

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

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

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

(d)LIKE操作符

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

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

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

(e) UNION操作符

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

(f) NOT

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

(g) OR

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

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

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

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

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

(h) DISTINCT

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

2. SQL书写(xiě)的影响

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

WHERE子句(jù)后(hòu)面的条件(jiàn)顺序对大数据量表的查询会产(chǎn)生直接的(de)影响。如:

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

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

3. 更多方面SQL优化资料分享

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

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

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

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

(3) 减少访问(wèn)数据(jù)库的次数:

ORACLE在(zài)内部执行了(le)许多工作: 解析SQL语句(jù), 估算索引的利(lì)用率(lǜ), 绑定(dìng)变量 , 读(dú)数据(jù)块等。

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

如果(guǒ)你有几个简单的数据库(kù)查询语(yǔ)句,你可(kě)以把它们(men)整合到(dào)一个查询(xún)中(即(jí)使它们之间没有(yǒu)关系) 。

(5) 用TRUNCATE替(tì)代(dài)DELETE:

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

(6) 尽量多使用(yòng)COMMIT:

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

a. 回滚(gǔn)段上用于恢复数据的信息(xī).
b. 被程序(xù)语(yǔ)句获得的锁
c. redo log buffer 中(zhōng)的(de)空(kōng)间

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

复杂的SQL往往牺(xī)牲了执行效率(lǜ). 能够掌握上面的运用函数解决问题(tí)的方法在(zài)实际工作中是非(fēi)常有意义的。

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

当在SQL语(yǔ)句中连接多个表时, 请使用表的别名并把(bǎ)别名前(qián)缀于每个Column上.这样一来,就可(kě)以减少解析的(de)时间并(bìng)减少(shǎo)那(nà)些(xiē)由Column歧义引起的语(yǔ)法错(cuò)误。

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

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

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

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

在线客服

售前咨询

售后服务

投诉/建(jiàn)议

服务(wù)热线
0731-83091505
18874148081

乐鱼网页版在线登录-leyu(中国)

乐鱼网页版在线登录-leyu(中国)