魅力峰值

导航

软件的优化(三)数据库的优化(Mysql)

数据库技术的优化,根据优化分类:

在这里插入图片描述 

 

根据使用的层面分为:

  • 数据库语句具体sql层面
  • 数据表层面
  • 数据库层面

 

一、sql

 

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

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

如:SELECT idcard,score FROM csf_log_detail WHERE idcard = "" or score = 800,idcard有索引,score无索引,最终Mysql引擎没有使用索引

可以改为:SELECT idcard,score FROM csf_log_detail WHERE idcard = "" union all SELECT idcard,score FROM csf_log_detail WHERE  score = 800

3、in和not in、exists和not exists慎重使用(尽量代替)(应当理解与exists和 not exists的区别)

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

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

select id from t where num between 1 and 3

备注:

in和exists  
in 是把外表和内表作hash 连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询。一直以来认为exists比in效率高的说法是不准确的。  
如果查询的两个表大小相当,那么用in和exists差别不大。  
如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in:  
例如:表A(小表),表B(大表)1:select * from A where cc in (select cc from B)  
效率低,用到了A表上cc列的索引;select * from A where exists(select cc from B where cc=A.cc)  
效率高,用到了B表上cc列的索引。  
相反的2:select * from B where cc in (select cc from A)  
效率高,用到了B表上cc列的索引;select * from B where exists(select cc from A where cc=B.cc)  
效率低,用到了A表上cc列的索引。  
  
  
not in 和not exists  
  
如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。  
  
   
  
not in 逻辑上不完全等同于not exists,如果你误用了not in,小心你的程序存在致命的BUG:  
  
请看下面的例子:  
create table t1 (c1 number,c2 number);  
create table t2 (c1 number,c2 number);  
  
insert into t1 values (1,2);  
insert into t1 values (1,3);  
insert into t2 values (1,2);  
insert into t2 values (1,null);  
  
select * from t1 where c2 not in (select c2 from t2);  
no rows found  
select * from t1 where not exists (select 1 from t2 where t1.c2=t2.c2);  
c1 c2  
1 3  
  
正如所看到的,not in 出现了不期望的结果集,存在逻辑错误。如果看一下上述两个select语句的执行计划,也会不同。后者使用了hash_aj。  
因此,请尽量不要使用not in(它会调用子查询),而尽量使用not exists(它会调用关联子查询)。如果子查询中返回的任意一条记录含有空值,则查询将不返回任何记录,正如上面例子所示。  
除非子查询字段有非空限制,这时可以使用not in ,并且也可以通过提示让它使用hasg_aj或merge_aj连接 

  

 

 

4、应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描

select id from t where substring(name,1,3’abc’       -–nameabc开头的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'

5、模糊查询尽量把‘“%”放在匹配字符串的后边,如果“%”字符串之前,这将导致引擎放弃使用索引而进行全表扫描

 如:select id from t where name like ‘%abc%

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

6、如果在 where 子句中使用参数,也会导致全表扫描。

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

select id from t where num @num

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

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

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

如:

select id from t where num/100

应改为:

select id from t where num 100*2

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

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

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

一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要

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

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

13、查询时候,尽量使用具体字段代替“*”

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

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

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

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

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

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

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

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

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

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

 

23.扩展:创建长字符串索引时候,用索引前缀来优化

https://blog.csdn.net/weixin_30412847/article/details/113213382

 

二、表

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

2、如果表字段结果过多,某些不常用的字段可以拆分出来,作为一个新表

3、将具有大量连接的表,可以根据连接关系创建中间表

4、增加表的冗余字段,增加冗余字段也是为了减少连接查询

5、在MySQL中,尽量使用JOIN来代替子查询.

因为子查询需要嵌套查询,嵌套查询时会建立一张临时表,临时表的建立和删除都会有较大的系统开销,而连接查询不会创建临时表,因此效率比嵌套子查询高.

6、适当的索引。

索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert  update 的效率,因为 insert  update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定

三、数据库层面

1、进行数据库硬件优化

  硬件三件套:高速缓存、cpu、硬盘

      cpu:配置多核更高频率的cpu

  高度缓存:配置大内存,提高内存,即可提高缓存区容量,因此能减少磁盘I/O时间,从而提高响应速度

  硬盘:配置高速磁盘或合理分布磁盘:高速磁盘提高I/O,分布磁盘能提高并行操作的能力.

2、优化数据库的配置

  优化数据库参数可以提高资源利用率,从而提高MySQL服务器性能.MySQL服务的配置参数都在my.cnf或my.ini,下面列出性能影响较大的几个参数.

  • key_buffer_size:索引缓冲区大小
  • table_cache:能同时打开表的个数
  • query_cache_size和query_cache_type:前者是查询缓冲区大小,后者是前面参数的开关,0表示不使用缓冲区,1表示使用缓冲区,但可以在查询中使用SQL_NO_CACHE表示不要使用缓冲区,2表示在查询中明确指出使用缓冲区才用缓冲区,即SQL_CACHE.
  • sort_buffer_size:排序缓冲区

 

 

3、读写分离

 先完成主从备份,用从库来进行读,主库来进行写入 

4、分表,分为横向分表或纵向分表

 

数据量的日剧增加,数据库中某个表有几百万条数据,导致查询和插入耗时太长,怎么能解决单表压力呢?你就该考虑是否把这个表拆分成多个小表,来减轻单个表的压力,提高处理效率,此方式称为分表。

 

  分表技术比较麻烦,要修改程序代码里的SQL语句,还要手动去创建其他表,也可以用merge存储引擎实现分表,相对简单许多。分表后,程序是对一个总表进行操作,这个总表不存放数据,只有一些分表的关系,以及更新数据的方式,总表会根据不同的查询,将压力分到不同的小表上,因此提高并发能力和磁盘I/O性能。

 

  分表分为垂直拆分和水平拆分:

 

  垂直拆分:把原来的一个很多字段的表拆分多个表,解决表的宽度问题。你可以把不常用的字段单独放到一个表中,也可以把大字段独立放一个表中,或者把关联密切的字段放一个表中。

 

  水平拆分:把原来一个表拆分成多个表,每个表的结构都一样,解决单表数据量大的问题。

 

5、表分区

分区就是把一张表的数据根据表结构中的字段(如range、list、hash等)分成多个区块,这些区块可以在一个磁盘上,也可以在不同的磁盘上,分区后,表面上还是一张表,但数据散列在多个位置,这样一来,多块硬盘同时处理不同的请求,从而提高磁盘I/O读写性能,实现比较简单

6、分库,根据实际生产的业务,把数据分散在不同的数据库

分库是根据业务不同把相关的表切分到不同的数据库中,比如web、bbs、blog等库。如果业务量很大,还可将切分后的库做主从架构,进一步避免单个库压力过大。

 

四、数据库的日常维护

 数据库维护是运维工程师或者DBA主要工作,包括性能监控、性能分析、性能调优、数据库备份和恢复等。

 

posted on 2020-07-07 14:30  魅力峰值  阅读(132)  评论(0编辑  收藏  举报