场景描述
在我们实际开发中,随着业务的不断增加,数据量也在不断的攀升,这样就离不开一个问题:数据查询效率优化
根据自己的以往实际项目工作经验和学习所知,现在对SQL查询优化做一个简单的梳理总结,总结的不好之处,望多多指点交流学习
主要通过以下几个点来进行总结分析:索引、语句本身、分区存储、分库分表
索引
在实际工作中,sql优化第一想到的应该就是索引,因为添加索引能够很直观的提升查询效率,但是在添加索引的时也不是越多多好,下面简单总结一下索引的实际使用
索引简介
关于索引的定义,在此不详细说明,网上的资料很多。索引简单的理解就是数据的目录,就好比一个字典的目录,其目的是提高查询效率
索引分类
SQL索引根据存储关系,分为两类:聚合索引和非聚合索引
聚集索引和非聚集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致,聚合索引的索引排序与表记录的排序是一致的,非聚合索引正好相反。
在一个表中,只会存在一个聚合索引,主键默认就是聚合索引,聚合索引的关键词为:clustered
创建聚合索引的SQL语句:
---- 根据数据表的字段1、字段2创建一个组合的聚合索引
use 库名
create clustered index 索引名称 on 表名(字段1,字段2)
SQL索引根据使用关系,分为四类:主键索引、唯一索引、普通索引(组合索引)、全文索引
主键索引:
表的主键自动为主键索引,每条数据的唯一标识,一个表只有一个主键索引
唯一索引:
唯一索引也是确保数据的唯一性,一个表可以多有多个唯一索引,这也是和主键索引的区别所在
创建唯一索引sql语句:
create UNIQUE index 索引名称 on 表名(字段1,字段2)
普通索引:
普通索引可以对任意字段或者多个字段添加索引
----创建普通索引sql语句:
create index 索引名称 on 表名(字段1,字段2)
索引创建技巧
动作描述 |
使用聚集索引 |
使用非聚集索引 |
外键列 |
应 |
应 |
主键列 |
应 |
应 |
列经常被分组排序(order by) |
应 |
应 |
返回某范围内的数据 |
应 |
不应 |
小数目的不同值 |
应 |
不应 |
大数目的不同值 |
不应 |
应 |
频繁更新的列 |
不应 |
应 |
频繁修改索引列 |
不应 |
应 |
一个或极少不同值 |
不应 |
不应 |
建立索引的原则
- 定义主键的数据列一定要建立索引。
- 定义有外键的数据列一定要建立索引。
- 对于经常查询的数据列最好建立索引。
- 对于需要在指定范围内的快速或频繁查询的数据列;
- 经常用在WHERE子句中的数据列。
- 经常出现在关键字order by、group by、distinct后面的字段,建立索引。如果建立的是复合索引,索引的字段顺序要和这些关键字后面的字段顺序一致,否则索引不会被使用。
- 对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。
- 对于定义为text、image和bit的数据类型的列不要建立索引。
- 对于经常存取的列避免建立索引
- 限制表上的索引数目。对一个存在大量更新操作的表,所建索引的数目一般不要超过3个,最多不要超过5个。索引虽说提高了访问速度,但太多索引会影响数据的更新操作。
- 对复合索引,按照字段在查询条件中出现的频度建立索引。在复合索引中,记录首先按照第一个字段排序。对于在第一个字段上取值相同的记录,系统再按照第二个字段的取值排序,以此类推。因此只有复合索引的第一个字段出现在查询条件中,该索引才可能被使用,因此将应用频度高的字段,放置在复合索引的前面,会使系统最大可能地使用此索引,发挥索引的作用。
索引碎片化处理(重构索引)
关于索引的定义,在此不详细说明,网上的资料很多。索引简单的理解就是数据的目录,就好比一个字典的目录,其目的是提高查询效率
索引简介
在实际开发中,有时候会发现新增了索引,但是效率还是没有明显提升,这时候需要考虑是否由于数据的更新编辑产生了索引碎片化,并处理
如果检查是否有索引碎片:
---- 检查一个表索引碎片化
use 库名
DBCC ShowContig(待查询的表)
---- 执行结果实例:
DBCC SHOWCONTIG 正在扫描 'SYS_Confige' 表... 表: 'SYS_Confige' (37575172);索引 ID: 1, 数据库 ID: 7 已执行 TABLE 级别的扫描。 - 扫描页数................................: 7885 - 扫描区数..............................: 986 - 区切换次数..............................: 985 - 每个区的平均页数........................: 8.0 - 扫描密度 [最佳计数:实际计数].......: 100.00% [986:986] - 逻辑扫描碎片 ..................: 0.01% - 区扫描碎片 ..................: 1.12% - 每页的平均可用字节数.....................: 23.0 - 平均页密度(满).....................: 99.72% DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
----数据结构分析:处理
Logical Scan Fragmentation-逻辑扫描碎片:无序页的百分比。该百分比应该在0%到10%之间,高了则说明有外部碎片。
解决方式:
解决方式有两种方式:整理索引碎片、重建索引,在实际操过程中建议采用:重建索引。
重建索引的SQL语句:
use 库名
DBCC DBREINDEX(待重建索引的表名称)
查询语句优化
在处理好索引后,接下来就是分析查询语句,查询语句可以借助专业的分析工具来分析,一个好的语句和不好的语句也会很影响效率,现在简单总结一下在查询语句的优化方向:
1、查询字段禁止出现 selete *
2、where 及 order by 涉及的列上建立索引。
3、where避免出现非空判断:比如:select from table where num is null
此时可以给num赋一个默认值0,语句修改为:select from table where num=0
4、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描
5、应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
-----查询value值为1 或者 4 的数据集合
select Id from SYS_Confige where Value=1 or Value=4
---- 可以这样查询:
select * from SYS_Confige where Value=1
union all
select * from SYS_Confige where Value=4
6、in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from SYS_Configet where Value in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from SYS_Configet where num Value 1 and 3
7、查询时避免使用like '%待查询关键字%' 查询
8、在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,
否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致
9、能够用关联查询的不要用exists
10、避免频繁创建和删除临时表,以减少系统表资源的消耗。
11、尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理
分区存储
当单表的数量达到一定量时,为了提高查询效率,数据表分区存储也是一个不错的优化方案。
分区呢就是把一张表的数据分成N多个区块,这些区块可以在同一个磁盘上,也可以在不同的磁盘上,通过提高减少文件大小,提高IO处理效率,间接的提高查询效率
分区存储,只是在数据存储上采用分区,但是在表现上还是一张表。
表分区有以下优点:
1、改善查询性能:对分区对象的查询可以仅搜索自己关心的分区,提高检索速度。
2、增强可用性:如果表的某个分区出现故障,表在其他分区的数据仍然可用;
3、维护方便:如果表的某个分区出现故障,需要修复数据,只修复该分区即可;
4、均衡I/O:可以把不同的分区映射到磁盘以平衡I/O,改善整个系统性能。
缺点:
分区表相关:已经存在的表没有方法可以直接转化为分区表
分库分表
分库分表其实原理也是将一个大表拆分不同的小表,在拆分上有两种拆分方式:
横向拆分:主要针对一个表的字段比较多,可以根据字段的查询频率、更新频率进行分割存储,可以理解为表扩展
纵向拆分:纵向拆分主要是根据数据量,将数据存储在不同的表,常用的拆分方式有:按照时间、按照哈希等等
分库分表和分区存储两者看上去是有点矛盾,实际上两者的出发点不一样。分区:是降低大单表数据分区存储,分库分表:直接将单表拆分为多表
同时分库分表不仅仅会增加数据维护难度,同时也会需要投入大量的开发工作,所以分库分表一般是要系统有一定的规模,公司有一定的资源支持
分库分表两种可以配合使用,比如在分表后,还可以对表进行分区存储。
总结
在数据优化过程中,索引是第一出发点,语句优化必不可少,分区、分库、分表也得考虑。