怎样提高数据库的性能与优化创建语句,高并发处理

怎样提高数据库的性能

①.减少应用和数据库的交互次数、同一个sql语句的执行次数;

②.减少表之间的关联,特别对于批量数据处理,尽量单表查询数据,统一在内存中进行逻辑处理,减少数据库压力

③.对访问频繁的数据,充分利用数据库cache和应用的缓存

④.数据量比较大的,在设计过程中,为了减少其他表的关联,增加一些冗余字段,提高查询性能

⑤.Sql语句调整
(1)尽量使用索引,避免全表扫描。试比较下面两条SQL语句: 
语句A:SELECT dname, deptno FROM dept WHERE deptno NOT  IN   (SELECT deptno FROM emp); 
语句B:SELECT dname, deptno FROM dept WHERE NOT EXISTS  (SELECT deptno FROM emp WHERE dept.deptno = emp.deptno); 

查询的模糊匹配
尽量避免在一个复杂查询里面使用 LIKE '%parm1%'——百分号会导致相关列的索引无法使用,最好不要用.
根据输入条件,先查出符合条件的列表,并把相关记录保存在一个临时表里头,然后再用临时表去做复杂关联
2)多表查询,应先查表数据量大的
表 TAB1 16,384 条记录 ,表 TAB2  1 条记录
     选择TAB2作为基础表 (最好的方法)
     select count(*) from tab1,tab2   执行时间0.96秒     
    选择TAB1作为基础表 (不佳的方法)
     select count(*) from tab2,tab1   执行时间26.09秒
(3)在子查询中慎重使用IN或者NOT IN语句,使用where (NOT) exists的效果要好的多。 
(4)慎重使用视图的联合查询,尤其是比较复杂的视图之间的联合查询。一般对视图的查询最好都分解为对数据表的直接查询效果要好一些。 
(5)查询条件避免使用*
. (6)用Where子句替换HAVING子句
     避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤。 这个处理需要排序,总计等操作。 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销。
例如:     
 低效:
     SELECT REGION,AVG(LOG_SIZE)
     FROM LOCATION
     GROUP BY REGION
     HAVING REGION != ‘SYDNEY’
     AND REGION != ‘PERTH’

     高效
     SELECT REGION,AVG(LOG_SIZE)
     FROM LOCATION
     WHERE REGION != ‘SYDNEY’
     AND REGION != ‘PERTH’
     GROUP BY REGION
 
(HAVING 中的条件一般用于对一些集合函数的比较,如COUNT() 等等。 除此而外,一般的条件应该写在WHERE子句中)

(7).用EXISTS替代IN
在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接。在这种情况下, 使用EXISTS(或NOT EXISTS)通常将提高查询的效率。
 
低效:
SELECT * 
FROM EMP (基础表)
WHERE EMPNO > 0
AND DEPTNO IN (SELECT DEPTNO 
FROM DEPT 
WHERE LOC = ‘MELB’)
    
高效:
SELECT * 
FROM EMP (基础表)
WHERE EMPNO > 0
AND EXISTS (SELECT ‘X’ 
FROM DEPT 
WHERE DEPT.DEPTNO = EMP.DEPTNO
AND LOC = ‘MELB’)

(8).用NOT EXISTS替代NOT IN
在子查询中,NOT IN子句将执行一个内部的排序和合并。 无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历)。  为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS。
 
例如:
SELECT …
FROM EMP
WHERE DEPT_NO NOT IN (SELECT DEPT_NO 
               FROM DEPT 
               WHERE DEPT_CAT=’A’);


为了提高效率改写为:
SELECT …
FROM EMP E
WHERE NOT EXISTS (SELECT ‘X’ 
 FROM DEPT D
 WHERE D.DEPT_NO = E.DEPT_NO
   AND DEPT_CAT = ‘A’);
 

⑥.在性能上对索引的选择
(1)索引的创建也是需要代价的,对于删除、某些更新、插入操作,对于每个索引都要进行相应的删除、更新、插入操作。从而导致删除、某些更新、插入操作的效率变低。 因此频繁进行删除、插入操作的表不要建立过多的索引。 
(2)查询经常用到的列上建立非聚簇索引,在频繁进行范围查询、排序、分组的列上建立聚簇索引。 
(3)对于不存在重复值的列,创建唯一索引优于创建非唯一索引。 
(4)当数据库表更新大数据后, 删除并重新建立索引来提高查询速度。 
(5)当对一个表的update操作远远多于select操作时, 不应创建索引。
(6) 避免在索引列上使用NOT; 避免在索引列上使用计算; 避免在索引列上使用IS NULL和IS NOT NULL; 避免改变索引列的类型
索引问题
法则:不要在建立的索引的数据列上进行下列操作:
◆避免对索引字段进行计算操作
◆避免在索引字段上使用not,<>,!=
◆避免在索引列上使用IS NULL和IS NOT NULL
◆避免在索引列上出现数据类型转换
◆避免在索引字段上使用函数
◆避免建立索引的列中使用空值。

在WHERE 语句中,尽量避免对索引字段进行计算操作
对Where 语句的法则
避免在WHERE子句中使用in,not  in,or 或者having
不要以字符格式声明数字,要以数字格式声明字符值

 排序
避免使用耗费资源的操作,带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎 执行,耗费资源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序

(以上所有的点都只是作为参考点,并不是需要严格遵守的。当效率不高的时候,可以根据当时的数据环境通过调整这些点来看效率的变化,实现提升。)

不要在where条件中使用函数,会强制每一行都计算该函数,无法使用索引查找。

例如:select * from table1 where id+3>5和select * from table1 where id>5-3,后者效率比前者高。

 select 1 from XXXXXXX 的使用

1是何意

select 1 from mytable;

与select anycol(目的表集合中的任意一行) from mytable;

与select * from mytable 作用上来说是没有差别的,都是查看是否有记录,一般是作条件用的。select 1 from 中的1是一常量,查到的所有行的值都是它,但从效率上来说,1>anycol>*,因为不用查字典表。

查看记录条数可以用select sum(1) from mytable;等价于select sum(*) from mytable;

单表查询慢时,基本由于过滤条件太多造成,使用联合索引加速过滤,如果索引使用不上,则得先把所有数据查询出来,然后放到内存里,内存放不下,还得部分存储到磁盘里,最后再进行过滤。另外,控制单次查询数据条数,从源头上进行流量控制,和地铁限流一个套路;另外,超级大的分页也不用考虑,Google、baidu、taobao搜索结果都没有超过100页的。

1.搜索大翻页的问题,百度、淘宝、Google查询结果限定在100页内,来避免使用count(1)计算总条数;
2.雪崩效应处理:缓存扛不住将负载传递给DB,带来过载,可以降级服务,将部分用户请求频次低,价值低但是系统开销不低的功能或者数据临时阻断停止响应,确保整体系统的稳定性;如微博过载暂停冷门订阅,避免全局崩溃;
3.写主库、读从库的"主从库数据同步问题",提示用户延迟处理(操作完后后,提示3s自动返回),体验略有不好也不会有非常大的困扰。
解决高并发要有思维宽度,能功能、使用、设计、数据库、缓存、OS各个层面去思考及其解决方法
 

解决大型网站数据瓶颈的一个脉络了,具体如下:

     单库数据库-->数据库读写分离-->缓存技术-->搜索技术-->数据的垂直拆分-->数据的水平拆分

这里我们先回顾下数据库的垂直拆分和水平拆分的定义:

  垂直拆分:把一个数据库中不同业务单元的数据分到不同的数据库里。

  水平拆分:是根据一定的规则把同一业务单元的数据拆分到多个数据库里。

1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。


2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num is null

最好不要给数据库留NULL,尽可能的使用 NOT NULL填充数据库.

备注、描述、评论之类的可以设置为 NULL,其他的,最好不要使用NULL。

不要以为 NULL 不需要空间,比如:char(100) 型,在字段建立时,空间就固定了, 不管是否插入值(NULL也包含在内),都是占用 100个字符的空间的,如果是varchar这样的变长字段, null 不占用空间。

可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:

select id from t where num = 0

3.应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。

4.应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num=10 or Name = 'admin'

可以这样查询:

select id from t where num = 10
union all
select id from t where Name = 'admin'

5.in 和 not in 也要慎用,否则会导致全表扫描,如:

select id from t where num in(1,2,3)

对于连续的数值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3

很多时候用 exists 代替 in 是一个好的选择:

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)

6.下面的查询也将导致全表扫描:

select id from t where name like ‘%abc%’

若要提高效率,可以考虑全文检索。

7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

select id from t where num = @num

可以改为强制查询使用索引:

select id from t with(index(索引名)) where num = @num

.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

select id from t where num/2 = 100

应改为:

select id from t where num = 100*2

9.应尽量避免在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'

10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

12.不要写一些没有意义的查询,如需要生成一个空表结构:

select col1,col2 into #t from t where 1=0

这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(…)

13.Update 语句,如果只更改1、2个字段,不要Update全部字段,否则频繁调用会引起明显的性能消耗,同时带来大量日志。

14.对于多张大数据量(这里几百条就算大了)的表JOIN,要先分页再JOIN,否则逻辑读会很高,性能很差。

15.select count(*) from table;这样不带任何条件的count会引起全表扫描,并且没有任何业务意义,是一定要杜绝的。


16.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。

17.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。

18.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连 接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

19.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

20.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

21.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

22. 避免频繁创建和删除临时表,以减少系统表资源的消耗。临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件, 最好使用导出表。

23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时 间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

29.尽量避免大事务操作,提高系统并发能力。

30.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

参考:http://www.cnblogs.com/yunfeifei/p/3850440.htm

 a)SQL的使用规范: 

   i. 尽量避免大事务操作,慎用holdlock子句,提高系统并发能力。 

   ii. 尽量避免反复访问同一张或几张表,尤其是数据量较大的表,可以考虑先根据条件提取数据到临时表中,然后再做连接。 

   iii. 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该改写;如果使用了游标,就要尽量避免在游标循环中再进行表连接的操作。 

   iv. 注意where字句写法,必须考虑语句顺序,应该根据索引顺序、范围大小来确定条件子句的前后顺序,尽可能的让字段顺序与索引顺序相一致,范围从大到小。 

   v. 不要在where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。 

   vi. 尽量使用exists代替select count(1)来判断是否存在记录,count函数只有在统计表中所有行数时使用,而且count(1)比count(*)更有效率。 

   vii. 尽量使用“>=”,不要使用“>”。 

   viii. 注意一些or子句和union子句之间的替换 

   ix. 注意表之间连接的数据类型,避免不同类型数据之间的连接。 

   x. 注意存储过程中参数和数据类型的关系。 

   xi. 注意insert、update操作的数据量,防止与其他应用冲突。如果数据量超过200个数据页面(400k),那么系统将会进行锁升级,页级锁会升级成表级锁。    

  b)索引的使用规范: 

   i. 索引的创建要与应用结合考虑,建议大的OLTP表不要超过6个索引。 

   ii. 尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过index index_name来强制指定索引 

   iii. 避免对大表查询时进行table scan,必要时考虑新建索引。 

   iv. 在使用索引字段作为条件时,如果该索引是联合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用。 

   v. 要注意索引的维护,周期性重建索引,重新编译存储过程。   

  c)tempdb的使用规范: 

   i. 尽量避免使用distinct、order by、group by、having、join、cumpute,因为这些语句会加重tempdb的负担。 

   ii. 避免频繁创建和删除临时表,减少系统表资源的消耗。 

   iii. 在新建临时表时,如果一次性插入数据量很大,那么可以使用select into代替create table,避免log,提高速度;如果数据量不大,为了缓和系统表的资源,建议先create table,然后insert。 

   iv. 如果临时表的数据量较大,需要建立索引,那么应该将创建临时表和建立索引的过程放在单独一个子存储过程中,这样才能保证系统能够很好的使用到该临时表的索引。 

    v. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先truncate table,然后drop table,这样可以避免系统表的较长时间锁定。 

    vi. 慎用大的临时表与其他大表的连接查询和修改,减低系统表负担,因为这种操作会在一条语句中多次使用tempdb的系统表。   
  d)合理的算法使用:    

http://www.cnblogs.com/luluping/archive/2008/05/05/1183821.html

优化:

SELECT TOP 1 优化
SELECT  ProductName ,
        ProductId ,        
        ( SELECT    CategoryName
          FROM      Category WITH ( NOLOCK )
          WHERE     Category.CategoryId = ( SELECT TOP 1
                                                        CategoryId
                                                FROM   RelProductCategory
                                                        WITH ( NOLOCK )
                                                WHERE   RelProductCategory.ProductId = Product.ProductId
                                                        AND Disabled = 0
                                              )
        ) CategoryName  
FROM    Product WITH ( NOLOCK )
WHERE   Disabled = 0
ORDER BY ProductName DESC;
                


WITH    product
          AS ( SELECT   ProductName ,
                        ProductId
               FROM     Product WITH ( NOLOCK )
               WHERE    Disabled = 0
             )
    SELECT  ProductName ,
            p.ProductId ,           
            tmp.CategoryName
    FROM    product p
            LEFT JOIN ( SELECT  p.ProductId ,
                                MIN(c.CategoryName) AS CategoryName
                        FROM    product p
                                LEFT JOIN RelProductCategory pc
                                WITH ( NOLOCK ) ON p.ProductId = pc.ProductId AND pc.Disabled = 0
                                LEFT JOIN Category c
                                WITH ( NOLOCK ) ON pc.CategoryId = c.CategoryId AND c.Disabled = 0
                        GROUP BY p.ProductId
                      ) tmp ON p.ProductId = tmp.ProductId
    ORDER BY p.ProductName DESC
原文地址:https://www.cnblogs.com/shy1766IT/p/5010252.html