标签:
在数据库部分,对数据库应用性能改进来说,需要重点关注应用程序,在查询设计和索引策略等方面进行优化,甚至可以把数据库查询效率提高数百倍,在其他方面的优化努力,其效果就没有这么明显(见下图)。本文重点描述在应用程序中进行数据库查询时,在设计和使用索引、设计查询语句等方面的注意事项,以取得良好的数据库查询性能。
一、索引设计和使用策略
使用索引是数据库减少磁盘I/O最有效的方法。除了在表中数据量非常少和需要返回表中大部分行的情况,正确使用索引的查询性能比全表扫描要高得多,有时简单新建一个合适的索引,就能把查询性能提高几十倍,在查询语句出现性能问题时,通常第一时间都会检查是否正确设计和使用了索引。
但是,事物都是有两面性的,索引的建立和维护需要消耗额外的CPU时间、额外的内存和存储空间,在对数据库表进行insert、update、delete操作时,都需要同时维护该表的所有索引,如果索引设计不当,不但不会提高性能,反而会降低性能。
遵循以下原则,可以让我们充分利用索引带来的性能方面的提升。
序号 |
索引设计和使用原则 |
解释说明 |
1 |
提高索引的区别能力,在数据基数大、重复值少的列上建索引,只要有可能,就使用create unique Index语句定义主键和唯一键 |
区分度越高,查找越精确,效率越高,尽量避免在有很多重复值(包括空值)的列上建索引 |
2 |
尽量选择唯一、最小、不可Null、容易获得、不常变更的列作为主键 |
|
3 |
在where子句中最频繁用到的列上建索引 |
|
4 |
在有distinct、min、max、order by、group by操作的列和join连接列上建索引 |
可降低查找成本和排序成本 |
5 |
避免创建冗余的类似索引,如存在a,b,c三列上的索引,那么a,b两列上的索引就是冗余的 |
冗余索引没有作用,只会增加额外的开销 |
6 |
索引中列的排列顺序需要考虑列唯一性,列宽度,列数据类型和查询使用频度。具有唯一性、列宽度小的、整型的、查询中引用最多的列排在前面 |
唯一性提高区分度,列宽小减少空间占用,列数据类型影响排序效率 |
7 |
尽量避免在一个索引中使用多于5个的列 |
|
8 |
对一个表,不要创建太多索引。通常,对联机事务处理环境(OLTP),不超过3个;对联机分析处理(OLAP)只读查询环境,可超过5个;混合查询和OLTP环境,2~5个为宜 |
|
9 |
尽量使用完全索引操作,即查询的所有列都在索引列中,如果需要的字段与排序无关,但查询时经常用到,可在create index时使用include子句将该字段包含在索引页中,但不作为索引字段使用 |
完全索引操作只访问索引,不需要访问数据表,可大幅度降低查询时间 |
10 |
在外键上建索引可提高父表update和delete操作的效率 |
|
11 |
在需要批量处理一定范围的数据或读取事先排序好的数据时,可使用聚簇索引(Cluster Index) |
批量处理操作时比较适用 |
12 |
不要在频繁更新的列、宽度较大的列上建立聚簇索引 |
所有非聚簇索引将以聚簇键作为定位器,这两种情况将导致非聚簇索引更新效率下降 |
13 |
不要在有大量并行顺序插入操作的情况下使用聚簇索引,或使用其他列上的索引打乱插入顺序,将插入操作分布到整个表 |
如果按聚簇键批量插入,插入操作集中在表的最后一个页面,将形成“热点”,I/O效率非常低 |
14 |
首先创建聚簇索引,再创建非聚簇索引,避免因建立聚簇索引导致所有非聚簇索引重建的情况 |
|
15 |
重建聚簇索引时使用Drop_Existing选项,避免重建聚簇索引导致所有非聚簇索引重建的情况 |
适用于SQL Server、MySQL |
16 |
在对超大字段进行查找,或对大量数据使用like ‘%...%’进行模糊查询时,使用全文索引 |
|
二、SQL语句编写的注意事项
数据库系统中,索引、碎片、统计信息和锁等很多因素都会影响性能,对这些特性进行优化都可能提高查询语句的执行效率。但是,如果SQL语句编写不合理,就可能大幅度增加本可避免的开销。
以下是编写SQL语句需要注意的一些事项:
² 不要使用“select * from ……”返回所有列,只检索需要的列,可避免后续因表结构变化导致的不必要的程序修改,还可降低额外消耗的资源
² 不要检索已知的列,如select cust_no, cust_name from CustInfo where cust_no = ‘10000050’
² 使用高选择性的where子句,让数据库返回必须的数据集,而不是返回大量数据给应用程序,再由应用程序进行筛选,返回大数据集的代价很高
² Where子句中列的顺序与需使用的索引顺序保持一致,不是所有数据库的优化器都能对此顺序进行优化,保持良好编程习惯
² 使用可参数化的搜索条件,如=, >, >=, <, <=, between, in, is null以及like ‘<literal>%’
² 尽量不要使用非参数化的负向查询,这将导致无法使用索引,如<>, !=, !>, !<, not in, not like, not exists, not between, is not null, like ‘%<literal>’
² 不要在where子句中对字段进行运算,如where amount / 2 > 100,即使amount字段有索引,也无法使用,改成where amount > 100 * 2就可使用amount列上的索引
² 不要在where子句中对字段使用函数,如where substring( Lastname, 1, 1) = ‘F’就无法使用Lastname列上的索引,而where Lastname like ‘F%’或者where Lastname >= ‘F’ and Lastname < ‘G’就可以
² 小心使用or操作,and操作中任何一个子句可使用索引都会提高查询性能,但是or条件中任何一个不能使用索引,都将导致查询性能下降,如where member_no = 1 or provider_no = 1,在member_no或provider_no任何一个字段上没有索引,都将导致表扫描或聚簇索引扫描
² Between一般比in/or高效得多,如果能在between和in/or条件中选择,那么始终选择between条件,并用>=和<=条件组合替代between子句,因为不是所有数据库的优化器都能把between子句改写为>=和<=条件组合,如果不能改写将导致无法使用索引
² 尽量用相同数据类型进行比较,避免发生数据类型转换,如select ‘5678’+3
² 在有min、max、distinct、order by、group by操作的列上建索引,避免额外的排序开销
² 当需要验证是否有符合条件的记录时,使用exists,不要使用count(*),前者在第一个匹配记录处返回,后者需要遍历所有匹配记录
² 如果参与union子句的select结果集互斥或者允许结果集中有重复记录,可使用union all代替union,可避免检测和删除重复记录的开销
² 尽量使用单条语句完成操作,如多条单笔insert语句可以合并为一条多笔insert语句,使用case子句等
² 当不需要更新数据时,使用select……for read only或fetch read only,可以提高查询性能
² 使用明确的事务控制语句来控制事务的开始和commit/rollback,将原子性扩展到整个事务,否则,每个语句都是一个原子操作,日志开销大
² 及时commit,如果有insert、update和delete操作没有commit,数据上的锁会一直保持,及时commit有利于尽快释放锁,可提高并发能力,尤其是在批量操作时,分段commit还有利于出错时的重新处理,可从最后一次commit操作继续处理,如果一直没有commit,就必须从头开始了
² 尽量保持短事务,尽量不要在事务中处理与数据库操作无关的工作,如群发邮件等
² 使用insert into……select …… from ……等块操作方式,可大幅度降低日志开销
² 根据业务逻辑使用最低级别的隔离级别,降低锁开销
Join c on a.col4 = c.col6
操作顺序是a和b进行left join,其结果再与c进行join,如果a与c进行join的结果集比较小,则应调整上述语句顺序,先进行a与c的join操作,再与b进行left join,可提高查询性能
三、其他注意事项
除了有效使用索引,编写高效SQL语句外,在提高查询性能方面,从应用程序编写角度还有以下内容需要注意:
实际上,要想得到一个高性能的数据库应用,不是一蹴而就、一劳永逸的,除了在数据库设计之初就要充分考虑业务特性和数据特性,做好物理设计(硬件选择,尤其是存储系统的选择和设计)和逻辑设计,编制高效的应用程序外,还需要与数据库管理员充分配合,在运行过程中,持续跟踪数据库状况,不定期对索引和数据碎片整理(ReOrg),对统计信息进行更新,对应用进行持续优化,才能让数据库应用系统一直保持良好的性能。
标签:
原文地址:http://www.cnblogs.com/barrywxx/p/4779913.html