乐鱼官方端网站登录入口-乐鱼leyu(中国)

乐鱼官方端网站登录入口-乐鱼leyu(中国)

SQL语句优化提高数据库性能

发布于: 2016-06-06    浏览: 10504    作者:王(wáng)佳(jiā)林

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


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

一(yī)、问题的(de)提出

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

二、SQL语句编写(xiě)注意问题

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

1. 操作符优化

(a) IN 操作符

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

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

 

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

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

    例子:

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

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

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

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

两者的区别在于, 前者将直接跳到第一个(gè)id等于101的记录而后者将首先(xiān)定位(wèi)到id=100的(de)记录并且(qiě)向前扫描到第一个id大于100的记录。

(d)LIKE操作符

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

一个实际例子:用dt_article表中(zhōng)内(nèi)容可来查询, content like'%福瑞希%'这个条(tiáo)件会产生全表扫(sǎo)描,如果改成contentlike '福瑞希%'则会利用content的(de)索引进行范(fàn)围的查询,性(xìng)能(néng)肯定大大提高。

在(zài)很多(duō)情况(kuàng)下可(kě)能无(wú)法(fǎ)避免这种情(qíng)况,但是一定(dìng)要心中有底,通配符如此使用会降低查询速度。然(rán)而当通(tōng)配符出现在字(zì)符(fú)串其他位置时,优化器就能利(lì)用索引。

(e) UNION操作符

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

(f) NOT

我们要避免在索引列上使用NOT, NOT会产生在和(hé)在索引列上使用函数相同的影响。 当(dāng)查询(xún)列碰到”NOT,他就(jiù)会(huì)停止使用(yòng)索引(yǐn)转而执行全表扫描。

(g) OR

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

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

(不推荐)select * from dt_article where title='清洗空气(qì)' or category_id=92 假如(rú)你坚持(chí)要用OR, 那就需要返(fǎn)回记录最少的索引(yǐn)列(liè)写在最前(qián)面。 
       另外在(zài)一(yī)些情况下(xià),也可(kě)以使用IN来(lái)替(tì)代OR,     这是一条简(jiǎn)单易记(jì)的规则,但是实际(jì)的执行效果还须(xū)检验(yà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í)交一个包含(hán)一对多表信息的查询时,避(bì)免在SELECT子句中(zhōng)使用DISTINCT。 一般可(kě)以考虑用(yòng)EXIST替换(huàn), EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询(xún)的条件一旦满足后,马(mǎ)上返(fǎn)回结果。 

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

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

WHERE子句后面的条(tiáo)件顺序对大数据量表的查询会产生直(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 

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

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

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

(1) 选择(zé)最(zuì)有效率(lǜ)的(de)表名顺序(只在基于规则的(de)优化器中(zhōng)有效):

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

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

ORACLE在(zài)解(jiě)析的过程中, 会(huì)将'*' 依次转换成所有的列名, 这个(gè)工作是通过查询数(shù)据字典完成的, 这(zhè)意味着将耗费更多的时间。

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

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

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

如果你有几(jǐ)个(gè)简单的数据库查询语(yǔ)句,你可(kě)以把它(tā)们整合到一个查询(xún)中(即使它们之间没有关系(xì)) 。

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

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

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

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

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

(7) 通过内部函数(shù)提高(gāo)SQL效率:

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

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

当在SQL语句中连(lián)接多个表时, 请使用(yòng)表的别名并把别(bié)名前缀(zhuì)于每个Column上.这(zhè)样一来,就可(kě)以减少解析的时间并减少那些由(yóu)Column歧义引起的语法错误。

(9) 总(zǒng)是使(shǐ)用索引的第一个列:

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

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

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

在线(xiàn)客(kè)服

售前咨询

售后服务

投诉/建议

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

乐鱼官方端网站登录入口-乐鱼leyu(中国)

乐鱼官方端网站登录入口-乐鱼leyu(中国)