- 大数据量高并发的数据库优化
- 1、数据库结构的设计
-
- 若是不能设计一个合理的数据库模型,不只会增长客户端和服务器段程序的编程和维护的难度,并且将会影响系统实际运行的性能。因此,在一个系统开始实施以前,完备的数据库模型的设计是必须的。
-
- 在一个系统分析、设计阶段,由于数据量较小,负荷较低。咱们每每只注意到功能的实现,而很难注意到性能的薄弱之处,等到系统投入实际运行一段时间后,才发现系统的性能在下降,这时再来考虑提升系统性能则要花费更多的人力物力,而整个系统也不可避免的造成了一个打补丁工程。
-
- 因此在考虑整个系统的流程的时候,咱们必需要考虑,在高并发大数据量的访问状况下,咱们的系统会不会出现极端的状况。(例如:对外统计系统在7月16日出现的数据异常的状况,并发大数据量的的访问形成,数据库的响应时间不能跟上数据刷新的速度形成。具体状况是:在日期临界时(00:00:00),判断数据库中是否有当前日期的记录,没有则插入一条当前日期的记录。在低并发访问的状况下,不会发生问题,可是当日期临界时的访问量至关大的时候,在作这一判断的时候,会出现屡次条件成立,则数据库里会被插入多条当前日期的记录,从而形成数据错误。),数据库的模型肯定下来以后,咱们有必要作一个系统内数据流向图,分析可能出现的瓶颈。
-
- 为了保证数据库的一致性和完整性,在逻辑设计的时候每每会设计过多的表间关联,尽量的下降数据的冗余。(例如用户表的地区,咱们能够把地区另外存放到一个地区表中)若是数据冗余低,数据的完整性容易获得保证,提升了数据吞吐速度,保证了数据的完整性,清楚地表达数据元素之间的关系。而对于多表之间的关联查询(尤为是大数据表)时,其性能将会下降,同时也提升了客户端程序的编程难度,所以,物理设计需折衷考虑,根据业务规则,肯定对关联表的数据量大小、数据项的访问频度,对此类数据表频繁的关联查询应适当提升数据冗余设计但增长了表间链接查询的操做,也使得程序的变得复杂,为了提升系统的响应时间,合理的数据冗余也是必要的。设计人员在设计阶段应根据系统操做的类型、频度加以均衡考虑。
- 另外,最好不要用自增属性字段做为主键与子表关联。不便于系统的迁移和数据恢复。对外统计系统映射关系丢失(******************)。
-
- 原来的表格必须能够经过由它分离出去的表格从新构建。使用这个规定的好处是,你能够确保不会在分离的表格中引入多余的列,全部你建立的表格结构都与它们的实际须要同样大。应用这条规定是一个好习惯,不过除非你要处理一个很是大型的数据,不然你将不须要用到它。(例如一个通行证系统,我能够将USERID,USERNAME,USERPASSWORD,单独出来做个表,再把USERID做为其余表的外键)
-
- 表的设计具体注意的问题:
-
- 一、数据行的长度不要超过8020字节,若是超过这个长度的话在物理页中这条数据会占用两行从而形成存储碎片,下降查询效率。
- 二、可以用数字类型的字段尽可能选择数字类型而不用字符串类型的(电话号码),这会下降查询和链接的性能,并会增长存储开销。这是由于引擎在处理查询和链接回逐个比较字符串中每个字符,而对于数字型而言只须要比较一次就够了。
-
- 三、对于不可变字符类型char和可变字符类型varchar 都是8000字节,char查询快,可是耗存储空间,varchar查询相对慢一些可是节省存储空间。在设计字段的时候能够灵活选择,例如用户名、密码等长度变化不大的字段能够选择CHAR,对于评论等长度变化大的字段能够选择VARCHAR。
-
- 四、字段的长度在最大限度的知足可能的须要的前提下,应该尽量的设得短一些,这样能够提升查询的效率,并且在创建索引的时候也能够减小资源的消耗。
-
-
- 2、查询的优化
-
- 保证在实现功能的基础上,尽可能减小对数据库的访问次数;经过搜索参数,尽可能减小对表的访问行数,最小化结果集,从而减轻网络负担;可以分开的操做尽可能分开处理,提升每次的响应速度;在数据窗口使用SQL时,尽可能把使用的索引放在选择的首列;算法的结构尽可能简单;在查询时,不要过多地使用通配符如SELECT * FROM T1语句,要用到几列就选择几列如:SELECT COL1,COL2 FROM T1;在可能的状况下尽可能限制尽可能结果集行数如:SELECT TOP 300 COL1,COL2,COL3 FROM T1,由于某些状况下用户是不须要那么多的数据的。
- 在没有建索引的状况下,数据库查找某一条数据,就必须进行全表扫描了,对全部数据进行一次遍历,查找出符合条件的记录。在数据量比较小的状况下,也许看不出明显的差异,可是当数据量大的状况下,这种状况就是极为糟糕的了。
- SQL语句在SQL SERVER中是如何执行的,他们担忧本身所写的SQL语句会被SQL SERVER误解。好比:
- select * from table1 where name='zhangsan' and tID > 10000
- 和执行:
- select * from table1 where tID > 10000 and name='zhangsan'
- 一些人不知道以上两条语句的执行效率是否同样,由于若是简单的从语句前后上看,这两个语句的确是不同,若是tID是一个聚合索引,那么后一句仅仅从表的10000条之后的记录中查找就好了;而前一句则要先从全表中查找看有几个name='zhangsan'的,然后再根据限制条件条件tID>10000来提出查询结果。
- 事实上,这样的担忧是没必要要的。SQL SERVER中有一个“查询分析优化器”,它能够计算出where子句中的搜索条件并肯定哪一个索引能缩小表扫描的搜索空间,也就是说,它能实现自动优化。虽然查询优化器能够根据where子句自动的进行查询优化,但有时查询优化器就会不按照您的本意进行快速查询。
- 在查询分析阶段,查询优化器查看查询的每一个阶段并决定限制须要扫描的数据量是否有用。若是一个阶段能够被用做一个扫描参数(SARG),那么就称之为可优化的,而且能够利用索引快速得到所需数据。
- SARG的定义:用于限制搜索的一个操做,由于它一般是指一个特定的匹配,一个值的范围内的匹配或者两个以上条件的AND链接。形式以下:
- 列名 操做符 <常数 或 变量> 或 <常数 或 变量> 操做符 列名
- 列名能够出如今操做符的一边,而常数或变量出如今操做符的另外一边。如:
- Name=’张三’
- 价格>5000
- 5000<价格
- Name=’张三’ and 价格>5000
- 若是一个表达式不能知足SARG的形式,那它就没法限制搜索的范围了,也就是SQL SERVER必须对每一行都判断它是否知足WHERE子句中的全部条件。因此一个索引对于不知足SARG形式的表达式来讲是无用的。
- 因此,优化查询最重要的就是,尽可能使语句符合查询优化器的规则避免全表扫描而使用索引查询。
-
- 具体要注意的:
-
- 1.应尽可能避免在 where 子句中对字段进行 null 值判断,不然将致使引擎放弃使用索引而进行全表扫描,如:
- select id from t where num is null
- 能够在num上设置默认值0,确保表中num列没有null值,而后这样查询:
- select id from t where num=0
-
- 2.应尽可能避免在 where 子句中使用!=或<>操做符,不然将引擎放弃使用索引而进行全表扫描。优化器将没法经过索引来肯定将要命中的行数,所以须要搜索该表的全部行。
-
- 3.应尽可能避免在 where 子句中使用 or 来链接条件,不然将致使引擎放弃使用索引而进行全表扫描,如:
- select id from t where num=10 or num=20
- 能够这样查询:
- select id from t where num=10
- union all
- select id from t where num=20
-
- 4.in 和 not in 也要慎用,由于IN会使系统没法使用索引,而只能直接搜索表中的数据。如:
- select id from t where num in(1,2,3)
- 对于连续的数值,能用 between 就不要用 in 了:
- select id from t where num between 1 and 3
-
- 5.尽可能避免在索引过的字符数据中,使用非打头字母搜索。这也使得引擎没法利用索引。
- 见以下例子:
- SELECT * FROM T1 WHERE NAME LIKE ‘%L%’
- SELECT * FROM T1 WHERE SUBSTING(NAME,2,1)=’L’
- SELECT * FROM T1 WHERE NAME LIKE ‘L%’
- 即便NAME字段建有索引,前两个查询依然没法利用索引完成加快操做,引擎不得不对全表全部数据逐条操做来完成任务。而第三个查询可以使用索引来加快操做。
-
- 6.必要时强制查询优化器使用某个索引,如在 where 子句中使用参数,也会致使全表扫描。由于SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,若是在编译时创建访问计划,变量的值仍是未知的,于是没法做为索引选择的输入项。以下面语句将进行全表扫描:
- select id from t where num=@num
- 能够改成强制查询使用索引:
- select id from t with(index(索引名)) where num=@num
-
- 7.应尽可能避免在 where 子句中对字段进行表达式操做,这将致使引擎放弃使用索引而进行全表扫描。如:
- SELECT * FROM T1 WHERE F1/2=100
- 应改成:
- SELECT * FROM T1 WHERE F1=100*2
-
- SELECT * FROM RECORD WHERE SUBSTRING(CARD_NO,1,4)=’5378’
- 应改成:
- SELECT * FROM RECORD WHERE CARD_NO LIKE ‘5378%’
-
- SELECT member_number, first_name, last_name FROM members
- WHERE DATEDIFF(yy,datofbirth,GETDATE()) > 21
- 应改成:
- SELECT member_number, first_name, last_name FROM members
- WHERE dateofbirth < DATEADD(yy,-21,GETDATE())
- 即:任何对列的操做都将致使表扫描,它包括数据库函数、计算表达式等等,查询时要尽量将操做移至等号右边。
-
- 8.应尽可能避免在where子句中对字段进行函数操做,这将致使引擎放弃使用索引而进行全表扫描。如:
- select id from t where substring(name,1,3)='abc'--name以abc开头的id
- select id from t where datediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的id
- 应改成:
- select id from t where name like 'abc%'
- select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
-
- 9.不要在 where 子句中的“=”左边进行函数、算术运算或其余表达式运算,不然系统将可能没法正确使用索引。
-
- 10.在使用索引字段做为条件时,若是该索引是复合索引,那么必须使用到该索引中的第一个字段做为条件时才能保证系统使用该索引,不然该索引将不会被使用,而且应尽量的让字段顺序与索引顺序相一致。
-
- 11.不少时候用 exists是一个好的选择:
- select num from a where num in(select num from b)
- 用下面的语句替换:
- select num from a where exists(select 1 from b where num=a.num)
-
- SELECT SUM(T1.C1)FROM T1 WHERE(
- (SELECT COUNT(*)FROM T2 WHERE T2.C2=T1.C2>0)
- SELECT SUM(T1.C1) FROM T1WHERE EXISTS(
- SELECT * FROM T2 WHERE T2.C2=T1.C2)
- 二者产生相同的结果,可是后者的效率显然要高于前者。由于后者不会产生大量锁定的表扫描或是索引扫描。
-
- 若是你想校验表里是否存在某条纪录,不要用count(*)那样效率很低,并且浪费服务器资源。能够用EXISTS代替。如:
- IF (SELECT COUNT(*) FROM table_name WHERE column_name = 'xxx')
- 能够写成:
- IF EXISTS (SELECT * FROM table_name WHERE column_name = 'xxx')
-
- 常常须要写一个T_SQL语句比较一个父结果集和子结果集,从而找到是否存在在父结果集中有而在子结果集中没有的记录,如:
- SELECT a.hdr_key FROM hdr_tbl a---- tbl a 表示tbl用别名a代替
- WHERE NOT EXISTS (SELECT * FROM dtl_tbl b WHERE a.hdr_key = b.hdr_key)
- SELECT a.hdr_key FROM hdr_tbl a
- LEFT JOIN dtl_tbl b ON a.hdr_key = b.hdr_key WHERE b.hdr_key IS NULL
- SELECT hdr_key FROM hdr_tbl
- WHERE hdr_key NOT IN (SELECT hdr_key FROM dtl_tbl)
- 三种写法均可以获得一样正确的结果,可是效率依次下降。
-
- 12.尽可能使用表变量来代替临时表。若是表变量包含大量数据,请注意索引很是有限(只有主键索引)。
-
- 13.避免频繁建立和删除临时表,以减小系统表资源的消耗。
-
- 14.临时表并非不可以使用,适当地使用它们可使某些例程更有效,例如,当须要重复引用大型表或经常使用表中的某个数据集时。可是,对于一次性事件,最好使用导出表。
-
- 15.在新建临时表时,若是一次性插入数据量很大,那么可使用 select into 代替 create table,避免形成大量 log ,以提升速度;若是数据量不大,为了缓和系统表的资源,应先create table,而后insert。
-
- 16.若是使用到了临时表,在存储过程的最后务必将全部的临时表显式删除,先 truncate table ,而后 drop table ,这样能够避免系统表的较长时间锁定。
-
- 17.在全部的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每一个语句后向客户端发送 DONE_IN_PROC 消息。
-
- 18.尽可能避免大事务操做,提升系统并发能力。
-
- 19.尽可能避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
-
- 20. 避免使用不兼容的数据类型。例如float和int、char和varchar、binary和varbinary是不兼容的。数据类型的不兼容可能使优化器没法执行一些原本能够进行的优化操做。例如:
- SELECT name FROM employee WHERE salary > 60000
- 在这条语句中,如salary字段是money型的,则优化器很难对其进行优化,由于60000是个整型数。咱们应当在编程时将整型转化成为钱币型,而不要等到运行时转化。
-
- 21.充分利用链接条件,在某种状况下,两个表之间可能不仅一个的链接条件,这时在 WHERE 子句中将链接条件完整的写上,有可能大大提升查询速度。
- 例:
- SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO
- SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO AND A.ACCOUNT_NO=B.ACCOUNT_NO
- 第二句将比第一句执行快得多。
-
- 2二、使用视图加速查询
- 把表的一个子集进行排序并建立视图,有时能加速查询。它有助于避免多重排序 操做,并且在其余方面还能简化优化器的工做。例如:
- SELECT cust.name,rcvbles.balance,……other columns
- FROM cust,rcvbles
- WHERE cust.customer_id = rcvlbes.customer_id
- AND rcvblls.balance>0
- AND cust.postcode>“98000”
- ORDER BY cust.name
-
- 若是这个查询要被执行屡次而不止一次,能够把全部未付款的客户找出来放在一个视图中,并按客户的名字进行排序:
- CREATE VIEW DBO.V_CUST_RCVLBES
- AS
- SELECT cust.name,rcvbles.balance,……other columns
- FROM cust,rcvbles
- WHERE cust.customer_id = rcvlbes.customer_id
- AND rcvblls.balance>0
- ORDER BY cust.name
- 而后如下面的方式在视图中查询:
- SELECT * FROM V_CUST_RCVLBES
- WHERE postcode>“98000”
- 视图中的行要比主表中的行少,并且物理顺序就是所要求的顺序,减小了磁盘I/O,因此查询工做量能够获得大幅减小。
-
- 2三、能用DISTINCT的就不用GROUP BY
- SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BY OrderID
- 可改成:
- SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10
-
- 24.能用UNION ALL就不要用UNION
- UNION ALL不执行SELECT DISTINCT函数,这样就会减小不少没必要要的资源
-
- 35.尽可能不要用SELECT INTO语句。
- SELECT INOT 语句会致使表锁定,阻止其余用户访问该表。
-
- 上面咱们提到的是一些基本的提升查询速度的注意事项,可是在更多的状况下,每每须要反复试验比较不一样的语句以获得最佳方案。最好的方法固然是测试,看实现相同功能的SQL语句哪一个执行时间最少,可是数据库中若是数据量不多,是比较不出来的,这时能够用查看执行计划,即:把实现相同功能的多条SQL语句考到查询分析器,按CTRL+L看查所利用的索引,表扫描次数(这两个对性能影响最大),整体上看询成本百分比便可。
-
- 3、算法的优化
-
- 尽可能避免使用游标,由于游标的效率较差,若是游标操做的数据超过1万行,那么就应该考虑改写。.使用基于游标的方法或临时表方法以前,应先寻找基于集的解决方案来解决问题,基于集的方法一般更有效。与临时表同样,游标并非不可以使用。对小型数据集使用 FAST_FORWARD 游标一般要优于其余逐行处理方法,尤为是在必须引用几个表才能得到所需的数据时。在结果集中包括“合计”的例程一般要比使用游标执行的速度快。若是开发时间容许,基于游标的方法和基于集的方法均可以尝试一下,看哪种方法的效果更好。
- 游标提供了对特定集合中逐行扫描的手段,通常使用游标逐行遍历数据,根据取出的数据不一样条件进行不一样的操做。尤为对多表和大表定义的游标(大的数据集合)循环很容易使程序进入一个漫长的等特甚至死机。
- 在有些场合,有时也非得使用游标,此时也可考虑将符合条件的数据行转入临时表中,再对临时表定义游标进行操做,可时性能获得明显提升。
- (例如:对内统计初版)
- 封装存储过程
-
- 4、创建高效的索引
-
- 建立索引通常有如下两个目的:维护被索引列的惟一性和提供快速访问表中数据的策略。大型数据库有两种索引即簇索引和非簇索引,一个没有簇索引的表是按堆结构存储数据,全部的数据均添加在表的尾部,而创建了簇索引的表,其数据在物理上会按照簇索引键的顺序存储,一个表只容许有一个簇索引,所以,根据B树结构,能够理解添加任何一种索引均能提升按索引列查询的速度,但会下降插入、更新、删除操做的性能,尤为是当填充因子(Fill Factor)较大时。因此对索引较多的表进行频繁的插入、更新、删除操做,建表和索引时因设置较小的填充因子,以便在各数据页中留下较多的自由空间,减小页分割及从新组织的工做。
- 索引是从数据库中获取数据的最高效方式之一。95% 的数据库性能问题均可以采用索引技术获得解决。做为一条规则,我一般对逻辑主键使用惟一的成组索引,对系统键(做为存储过程)采用惟一的非成组索引,对任何外键列[字段]采用非成组索引。不过,索引就象是盐,太多了菜就咸了。你得考虑数据库的空间有多大,表如何进行访问,还有这些访问是否主要用做读写。
- 实际上,您能够把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引:汇集索引(clustered index,也称聚类索引、簇集索引)和非汇集索引(nonclustered index,也称非聚类索引、非簇集索引)。下面,咱们举例来讲明一下汇集索引和非汇集索引的区别:
- 其实,咱们的汉语字典的正文自己就是一个汇集索引。好比,咱们要查“安”字,就会很天然地翻开字典的前几页,由于“安”的拼音是“an”,而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的,那么“安”字就天然地排在字典的前部。若是您翻完了全部以“a”开头的部分仍然找不到这个字,那么就说明您的字典中没有这个字;一样的,若是查“张”字,那您也会将您的字典翻到最后部分,由于“张”的拼音是“zhang”。也就是说,字典的正文部分自己就是一个目录,您不须要再去查其余目录来找到您须要找的内容。
- 咱们把这种正文内容自己就是一种按照必定规则排列的目录称为“汇集索引”。
- 若是您认识某个字,您能够快速地从自动中查到这个字。但您也可能会遇到您不认识的字,不知道它的发音,这时候,您就不能按照刚才的方法找到您要查的字,而须要去根据“偏旁部首”查到您要找的字,而后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并非真正的正文的排序方法,好比您查“张”字,咱们能够看到在查部首以后的检字表中“张”的页码是672页,检字表中“张”的上面是“驰”字,但页码倒是63页,“张”的下面是“弩”字,页面是390页。很显然,这些字并非真正的分别位于“张”字的上下方,如今您看到的连续的“驰、张、弩”三字实际上就是他们在非汇集索引中的排序,是字典正文中的字在非汇集索引中的映射。咱们能够经过这种方式来找到您所须要的字,但它须要两个过程,先找到目录中的结果,而后再翻到您所须要的页码。
- 咱们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为“非汇集索引”。
- 进一步引伸一下,咱们能够很容易的理解:每一个表只能有一个汇集索引,由于目录只能按照一种方法进行排序。
-
- (一)什么时候使用汇集索引或非汇集索引
- 下面的表总结了什么时候使用汇集索引或非汇集索引(很重要)。
- 动做描述 使用汇集索引 使用非汇集索引
- 列常常被分组排序 应 应
- 返回某范围内的数据 应 不该
- 一个或极少不一样值 不该 不该
- 小数目的不一样值 应 不该
- 大数目的不一样值 不该 应
- 频繁更新的列 不该 应
- 外键列 应 应
- 主键列 应 应
- 频繁修改索引列 不该 应
-
-
- 事实上,咱们能够经过前面汇集索引和非汇集索引的定义的例子来理解上表。如:返回某范围内的数据一项。好比您的某个表有一个时间列,刚好您把聚合索引创建在了该列,这时您查询2004年1月1日至2004年10月1日之间的所有数据时,这个速度就将是很快的,由于您的这本字典正文是按日期进行排序的,聚类索引只须要找到要检索的全部数据中的开头和结尾数据便可;而不像非汇集索引,必须先查到目录中查到每一项数据对应的页码,而后再根据页码查到具体内容。
-
-
- (二)结合实际,谈索引使用的误区
-
- 理论的目的是应用。虽然咱们刚才列出了什么时候应使用汇集索引或非汇集索引,但在实践中以上规则却很容易被忽视或不能根据实际状况进行综合分析。下面咱们将根据在实践中遇到的实际问题来谈一下索引使用的误区,以便于你们掌握索引创建的方法。
- 一、主键就是汇集索引
- 这种想法笔者认为是极端错误的,是对汇集索引的一种浪费。虽然SQL SERVER默认是在主键上创建汇集索引的。
- 一般,咱们会在每一个表中都创建一个ID列,以区分每条数据,而且这个ID列是自动增大的,步长通常为1。咱们的这个办公自动化的实例中的列Gid就是如此。此时,若是咱们将这个列设为主键,SQL SERVER会将此列默认为汇集索引。这样作有好处,就是可让您的数据在数据库中按照ID进行物理排序,但笔者认为这样作意义不大。
- 显而易见,汇集索引的优点是很明显的,而每一个表中只能有一个汇集索引的规则,这使得汇集索引变得更加珍贵。
- 从咱们前面谈到的汇集索引的定义咱们能够看出,使用汇集索引的最大好处就是可以根据查询要求,迅速缩小查询范围,避免全表扫描。在实际应用中,由于ID号是自动生成的,咱们并不知道每条记录的ID号,因此咱们很难在实践中用ID号来进行查询。这就使让ID号这个主键做为汇集索引成为一种资源浪费。其次,让每一个ID号都不一样的字段做为汇集索引也不符合“大数目的不一样值状况下不该创建聚合索引”规则;固然,这种状况只是针对用户常常修改记录内容,特别是索引项的时候会负做用,但对于查询速度并无影响。
- 在办公自动化系统中,不管是系统首页显示的须要用户签收的文件、会议仍是用户进行文件查询等任何状况下进行数据查询都离不开字段的是“日期”还有用户自己的“用户名”。
- 一般,办公自动化的首页会显示每一个用户还没有签收的文件或会议。虽然咱们的where语句能够仅仅限制当前用户还没有签收的状况,但若是您的系统已创建了很长时间,而且数据量很大,那么,每次每一个用户打开首页的时候都进行一次全表扫描,这样作意义是不大的,绝大多数的用户1个月前的文件都已经浏览过了,这样作只能徒增数据库的开销而已。事实上,咱们彻底可让用户打开系统首页时,数据库仅仅查询这个用户近3个月来未阅览的文件,经过“日期”这个字段来限制表扫描,提升查询速度。若是您的办公自动化系统已经创建的2年,那么您的首页显示速度理论上将是原来速度8倍,甚至更快。
-
- 二、只要创建索引就能显著提升查询速度
- 事实上,咱们能够发现上面的例子中,第二、3条语句彻底相同,且创建索引的字段也相同;不一样的仅是前者在fariqi字段上创建的是非聚合索引,后者在此字段上创建的是聚合索引,但查询速度却有着天壤之别。因此,并不是是在任何字段上简单地创建索引就能提升查询速度。
- 从建表的语句中,咱们能够看到这个有着1000万数据的表中fariqi字段有5003个不一样记录。在此字段上创建聚合索引是再合适不过了。在现实中,咱们天天都会发几个文件,这几个文件的发文日期就相同,这彻底符合创建汇集索引要求的:“既不能绝大多数都相同,又不能只有极少数相同”的规则。由此看来,咱们创建“适当”的聚合索引对于咱们提升查询速度是很是重要的。
-
- 三、把全部须要提升查询速度的字段都加进汇集索引,以提升查询速度
- 上面已经谈到:在进行数据查询时都离不开字段的是“日期”还有用户自己的“用户名”。既然这两个字段都是如此的重要,咱们能够把他们合并起来,创建一个复合索引(compound index)。
- 不少人认为只要把任何字段加进汇集索引,就能提升查询速度,也有人感到迷惑:若是把复合的汇集索引字段分开查询,那么查询速度会减慢吗?带着这个问题,咱们来看一下如下的查询速度(结果集都是25万条数据):(日期列fariqi首先排在复合汇集索引的起始列,用户名neibuyonghu排在后列)
- 咱们能够看到若是仅用汇集索引的起始列做为查询条件和同时用到复合汇集索引的所有列的查询速度是几乎同样的,甚至比用上所有的复合索引列还要略快(在查询结果集数目同样的状况下);而若是仅用复合汇集索引的非起始列做为查询条件的话,这个索引是不起任何做用的。固然,语句一、2的查询速度同样是由于查询的条目数同样,若是复合索引的全部列都用上,并且查询结果少的话,这样就会造成“索引覆盖”,于是性能能够达到最优。同时,请记住:不管您是否常用聚合索引的其余列,但其前导列必定要是使用最频繁的列。
-
- (三)其余注意事项
- “水可载舟,亦可覆舟”,索引也同样。索引有助于提升检索性能,但过多或不当的索引也会致使系统低效。由于用户在表中每加进一个索引,数据库就要作更多的工做。过多的索引甚至会致使索引碎片。
- 因此说,咱们要创建一个“适当”的索引体系,特别是对聚合索引的建立,更应精益求精,以使您的数据库能获得高性能的发挥
-
- 解决方案:创建中间表,经过DTS调度天天共步数据。
- 中间表设计原则
- 记录数同原表同样,减小多表链接,保存运算好的值,若是记录修改,根据修改日志,从新计算中间值
-
-
- 增量同步数据(DTS)
- 直接从天天的数据库更改日志读取记录,更新中间表,根据服务器空间程度合理调度DTS,减小数据同步时间。
-
- 对中间数据进行运算
- 查询不做字段运行,因此运算在生成中间表的过程当中已经计算
-
- 根据查询,优化索引设计
- 根据数据查询特性,对where ,GROUP BY等操做字段进行索引设计,提升查询速度
-
- 优化数据类型
- 大量采用Int提升查询、统计速度
-
- 优化中间表关键字
- 采用Int,提升插入速度
-
-
- 数据文件优化设计,一个主要业务,一个数据文件,建数据文件时,估计数据量,一次建一个比较大的文件,这样所分配的文件就是一个连续文件块。
咱们要作到不但会写SQL,还要作到写出性能优良的SQL,如下为笔者学习、摘录、并汇总部分资料与你们分享!
(1) 选择最有效率的表名顺序(只在基于规则的优化器中有效):
ORACLE 的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)将被最早处理,在FROM子句中包含多个表的状况下,你必须选择记录条数最少的表做为基础表。若是有3个以上的表链接查询, 那就须要选择交叉表(intersection table)做为基础表, 交叉表是指那个被其余表所引用的表.
(2) WHERE子句中的链接顺序.:
ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的链接必须写在其余WHERE条件以前, 那些能够过滤掉最大数量记录的条件必须写在WHERE子句的末尾.
(3) SELECT子句中避免使用 ‘ * ‘:
ORACLE在解析的过程当中, 会将'*' 依次转换成全部的列名, 这个工做是经过查询数据字典完成的, 这意味着将耗费更多的时间
(4) 减小访问数据库的次数:
ORACLE在内部执行了许多工做: 解析SQL语句, 估算索引的利用率, 绑定变量 , 读数据块等;
(5) 在SQL*Plus , SQL*Forms和Pro*C中从新设置ARRAYSIZE参数, 能够增长每次数据库访问的检索数据量 ,建议值为200
(6) 使用DECODE函数来减小处理时间:
使用DECODE函数能够避免重复扫描相同记录或重复链接相同的表.
(7) 整合简单,无关联的数据库访问:
若是你有几个简单的数据库查询语句,你能够把它们整合到一个查询中(即便它们之间没有关系)
(8) 删除重复记录:
最高效的删除重复记录方法 ( 由于使用了ROWID)例子:
DELETE FROM EMP E WHERE E.ROWID > (SELECT MIN(X.ROWID)
FROM EMP X WHERE X.EMP_NO = E.EMP_NO);
(9) 用TRUNCATE替代DELETE:
当删除表中的记录时,在一般状况下, 回滚段(rollback segments ) 用来存放能够被恢复的信息. 若是你没有COMMIT事务,ORACLE会将数据恢复到删除以前的状态(准确地说是恢复到执行删除命令以前的情况) 而当运用TRUNCATE时, 回滚段再也不存听任何可被恢复的信息.当命令运行后,数据不能被恢复.所以不多的资源被调用,执行时间也会很短. (译者按: TRUNCATE只在删除全表适用,TRUNCATE是DDL不是DML)
(10) 尽可能多使用COMMIT:
只要有可能,在程序中尽可能多使用COMMIT, 这样程序的性能获得提升,需求也会由于COMMIT所释放的资源而减小:
COMMIT所释放的资源:
a. 回滚段上用于恢复数据的信息.
b. 被程序语句得到的锁
c. redo log buffer 中的空间
d. ORACLE为管理上述3种资源中的内部花费
(11) 用Where子句替换HAVING子句:
避免使用HAVING子句, HAVING 只会在检索出全部记录以后才对结果集进行过滤. 这个处理须要排序,总计等操做. 若是能经过WHERE子句限制记录的数目,那就能减小这方面的开销. (非oracle中)on、where、having这三个均可以加条件的子句中,on是最早执行,where次之,having最后,由于on是先把不符合条件的记录过滤后才进行统计,它就能够减小中间运算要处理的数据,按理说应该速度是最快的,where也应该比having快点的,由于它过滤数据后才进行sum,在两个表联接时才用on的,因此在一个表的时候,就剩下where跟having比较了。在这单表查询统计的状况下,若是要过滤的条件没有涉及到要计算字段,那它们的结果是同样的,只是where可使用rushmore技术,而having就不能,在速度上后者要慢若是要涉及到计算的字段,就表示在没计算以前,这个字段的值是不肯定的,根据上篇写的工做流程,where的做用时间是在计算以前就完成的,而having就是在计算后才起做用的,因此在这种状况下,二者的结果会不一样。在多表联接查询时,on比where更早起做用。系统首先根据各个表之间的联接条件,把多个表合成一个临时表后,再由where进行过滤,而后再计算,计算完后再由having进行过滤。因而可知,要想过滤条件起到正确的做用,首先要明白这个条件应该在何时起做用,而后再决定放在那里
(12) 减小对表的查询:
在含有子查询的SQL语句中,要特别注意减小对表的查询.例子:
SELECT TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) = ( SELECT
TAB_NAME,DB_VER FROM TAB_COLUMNS WHERE VERSION = 604)
(13) 经过内部函数提升SQL效率.:
复杂的SQL每每牺牲了执行效率. 可以掌握上面的运用函数解决问题的方法在实际工做中是很是有意义的
(14) 使用表的别名(Alias):
当在SQL语句中链接多个表时, 请使用表的别名并把别名前缀于每一个Column上.这样一来,就能够减小解析的时间并减小那些由Column歧义引发的语法错误.
(15) 用EXISTS替代IN、用NOT EXISTS替代NOT IN:
在许多基于基础表的查询中,为了知足一个条件,每每须要对另外一个表进行联接.在这种状况下, 使用EXISTS(或NOT EXISTS)一般将提升查询的效率. 在子查询中,NOT IN子句将执行一个内部的排序和合并. 不管在哪一种状况下,NOT IN都是最低效的 (由于它对子查询中的表执行了一个全表遍历). 为了不使用NOT IN ,咱们能够把它改写成外链接(Outer Joins)或NOT EXISTS.
例子:
(高效)SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND EXISTS (SELECT ‘X' FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB')
(低效)SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC = ‘MELB')
(16) 识别'低效执行'的SQL语句:
虽然目前各类关于SQL优化的图形化工具层出不穷,可是写出本身的SQL工具来解决问题始终是一个最好的方法:
SELECT EXECUTIONS , DISK_READS, BUFFER_GETS,
ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio,
ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run,
SQL_TEXT
FROM V$SQLAREA
WHERE EXECUTIONS>0
AND BUFFER_GETS > 0
AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8
ORDER BY 4 DESC;
(17) 用索引提升效率:
索引是表的一个概念部分,用来提升检索数据的效率,ORACLE使用了一个复杂的自平衡B-tree结构. 一般,经过索引查询数据比全表扫描要快. 当ORACLE找出执行查询和Update语句的最佳路径时, ORACLE优化器将使用索引. 一样在联结多个表时使用索引也能够提升效率. 另外一个使用索引的好处是,它提供了主键(primary key)的惟一性验证.。那些LONG或LONG RAW数据类型, 你能够索引几乎全部的列. 一般, 在大型表中使用索引特别有效. 固然,你也会发现, 在扫描小表时,使用索引一样能提升效率. 虽然使用索引能获得查询效率的提升,可是咱们也必须注意到它的代价. 索引须要空间来存储,也须要按期维护, 每当有记录在表中增减或索引列被修改时, 索引自己也会被修改. 这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O . 由于索引须要额外的存储空间和处理,那些没必要要的索引反而会使查询反应时间变慢.。按期的重构索引是有必要的.:
ALTER INDEX <INDEXNAME> REBUILD <TABLESPACENAME>
18) 用EXISTS替换DISTINCT:
当提交一个包含一对多表信息(好比部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 通常能够考虑用EXIST替换, EXISTS 使查询更为迅速,由于RDBMS核心模块将在子查询的条件一旦知足后,马上返回结果. 例子:
(低效):
SELECT DISTINCT DEPT_NO,DEPT_NAME FROM DEPT D , EMP E
WHERE D.DEPT_NO = E.DEPT_NO
(高效):
SELECT DEPT_NO,DEPT_NAME FROM DEPT D WHERE EXISTS ( SELECT ‘X'
FROM EMP E WHERE E.DEPT_NO = D.DEPT_NO);
(19) sql语句用大写的;由于oracle老是先解析sql语句,把小写的字母转换成大写的再执行
(20) 在Java代码中尽可能少用链接符“+”链接字符串!
(21) 避免在索引列上使用NOT 一般,
咱们要避免在索引列上使用NOT, NOT会产生在和在索引列上使用函数相同的影响. 当ORACLE”遇到”NOT,他就会中止使用索引转而执行全表扫描.
(22) 避免在索引列上使用计算.
WHERE子句中,若是索引列是函数的一部分.优化器将不使用索引而使用全表扫描.
举例:
低效:
SELECT … FROM DEPT WHERE SAL * 12 > 25000;
高效:
SELECT … FROM DEPT WHERE SAL > 25000/12;
(23) 用>=替代>
高效:
SELECT * FROM EMP WHERE DEPTNO >=4
低效:
SELECT * FROM EMP WHERE DEPTNO >3
二者的区别在于, 前者DBMS将直接跳到第一个DEPT等于4的记录然后者将首先定位到DEPTNO=3的记录而且向前扫描到第一个DEPT大于3的记录.
(24) 用UNION替换OR (适用于索引列)
一般状况下, 用UNION替换WHERE子句中的OR将会起到较好的效果. 对索引列使用OR将形成全表扫描. 注意, 以上规则只针对多个索引列有效. 若是有column没有被索引, 查询效率可能会由于你没有选择OR而下降. 在下面的例子中, LOC_ID 和REGION上都建有索引.
高效:
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE LOC_ID = 10
UNION
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE REGION = “MELBOURNE”
低效:
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE LOC_ID = 10 OR REGION = “MELBOURNE”
若是你坚持要用OR, 那就须要返回记录最少的索引列写在最前面.
(25) 用IN来替换OR
这是一条简单易记的规则,可是实际的执行效果还须检验,在ORACLE8i下,二者的执行路径彷佛是相同的.
低效:
SELECT…. FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30
高效
SELECT… FROM LOCATION WHERE LOC_IN IN (10,20,30);
(26) 避免在索引列上使用IS NULL和IS NOT NULL
避免在索引中使用任何能够为空的列,ORACLE将没法使用该索引.对于单列索引,若是列包含空值,索引中将不存在此记录. 对于复合索引,若是每一个列都为空,索引中一样不存在此记录. 若是至少有一个列不为空,则记录存在于索引中.举例: 若是惟一性索引创建在表的A列和B列上, 而且表中存在一条记录的A,B值为(123,null) , ORACLE将不接受下一条具备相同A,B值(123,null)的记录(插入). 然而若是全部的索引列都为空,ORACLE将认为整个键值为空而空不等于空. 所以你能够插入1000 条具备相同键值的记录,固然它们都是空! 由于空值不存在于索引列中,因此WHERE子句中对索引列进行空值比较将使ORACLE停用该索引.
低效: (索引失效)
SELECT … FROM DEPARTMENT WHERE DEPT_CODE IS NOT NULL;
高效: (索引有效)
SELECT … FROM DEPARTMENT WHERE DEPT_CODE >=0;
(27) 老是使用索引的第一个列:
若是索引是创建在多个列上, 只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引. 这也是一条简单而重要的规则,当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引
28) 用UNION-ALL 替换UNION ( 若是有可能的话):
当SQL 语句须要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并, 而后在输出最终结果前进行排序. 若是用UNION ALL替代UNION, 这样排序就不是必要了. 效率就会所以获得提升. 须要注意的是,UNION ALL 将重复输出两个结果集合中相同记录. 所以各位仍是要从业务需求分析使用UNION ALL的可行性. UNION 将对结果集合排序,这个操做会使用到SORT_AREA_SIZE这块内存. 对于这块内存的优化也是至关重要的. 下面的SQL能够用来查询排序的消耗量
低效:
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = '31-DEC-95'
UNION
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = '31-DEC-95'
高效:
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = '31-DEC-95'
UNION ALL
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = '31-DEC-95'
(29) 用WHERE替代ORDER BY:
ORDER BY 子句只在两种严格的条件下使用索引.
ORDER BY中全部的列必须包含在相同的索引中并保持在索引中的排列顺序.
ORDER BY中全部的列必须定义为非空.
WHERE子句使用的索引和ORDER BY子句中所使用的索引不能并列.
例如:
表DEPT包含如下列:
DEPT_CODE PK NOT NULL
DEPT_DESC NOT NULL
DEPT_TYPE NULL
低效: (索引不被使用)
SELECT DEPT_CODE FROM DEPT ORDER BY DEPT_TYPE
高效: (使用索引)
SELECT DEPT_CODE FROM DEPT WHERE DEPT_TYPE > 0
(30) 避免改变索引列的类型.:
当比较不一样数据类型的数据时, ORACLE自动对列进行简单的类型转换.
假设 EMPNO是一个数值类型的索引列.
SELECT … FROM EMP WHERE EMPNO = ‘123'
实际上,通过ORACLE类型转换, 语句转化为:
SELECT … FROM EMP WHERE EMPNO = TO_NUMBER(‘123')
幸运的是,类型转换没有发生在索引列上,索引的用途没有被改变.
如今,假设EMP_TYPE是一个字符类型的索引列.
SELECT … FROM EMP WHERE EMP_TYPE = 123
这个语句被ORACLE转换为:
SELECT … FROM EMP WHERETO_NUMBER(EMP_TYPE)=123
由于内部发生的类型转换, 这个索引将不会被用到! 为了不ORACLE对你的SQL进行隐式的类型转换, 最好把类型转换用显式表现出来. 注意当字符和数值比较时, ORACLE会优先转换数值类型到字符类型
(31) 须要小心的WHERE子句:
某些SELECT 语句中的WHERE子句不使用索引. 这里有一些例子.
在下面的例子里, (1)‘!=' 将不使用索引. 记住, 索引只能告诉你什么存在于表中, 而不能告诉你什么不存在于表中. (2) ‘||'是字符链接函数. 就象其余函数那样, 停用了索引. (3) ‘+'是数学函数. 就象其余数学函数那样, 停用了索引. (4)相同的索引列不能互相比较,这将会启用全表扫描.
(32) a. 若是检索数据量超过30%的表中记录数.使用索引将没有显著的效率提升.
b. 在特定状况下, 使用索引也许会比全表扫描慢, 但这是同一个数量级上的区别. 而一般状况下,使用索引比全表扫描要块几倍乃至几千倍!
(33) 避免使用耗费资源的操做:
带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎
执行耗费资源的排序(SORT)功能. DISTINCT须要一次排序操做, 而其余的至少须要执行两次排序. 一般, 带有UNION, MINUS , INTERSECT的SQL语句均可以用其余方式重写. 若是你的数据库的SORT_AREA_SIZE调配得好, 使用UNION , MINUS, INTERSECT也是能够考虑的, 毕竟它们的可读性很强
(34) 优化GROUP BY:
提升GROUP BY 语句的效率, 能够经过将不须要的记录在GROUP BY 以前过滤掉.下面两个查询返回相同结果但第二个明显就快了许多.
低效:
SELECT JOB , AVG(SAL)
FROM EMP
GROUP by JOB
HAVING JOB = ‘PRESIDENT'
OR JOB = ‘MANAGER'
高效:
SELECT JOB , AVG(SAL)
FROM EMP
WHERE JOB = ‘PRESIDENT'
OR JOB = ‘MANAGER'
GROUP by JOB算法