你的位置:首页 > 数据库

[数据库]7、SQL Server索引、表压缩


索引

什么是索引?

索引是一种磁盘上的数据结构,建立在表或视图的基础上。使用索引可以使数据的获取更快更高校,也会影响其他的一些性能,如插入或更新等。

索引主要分为两种类型:聚集索引和非聚集索引。

字典的目录就是一个索引,按照拼音查询想要的字就是聚集索引(物理连续,页码与目录一一对应),偏旁部首就是一个非聚集索引(逻辑连续,页码与目录不连续)。

聚集索引存储记录是物理上连续存在的,而非聚集索引是逻辑上的连续,物理存储并不连续。

聚集索引一个表中只能有一个,而非聚集索引一个表中可以有多个。

索引的利弊

使用索引是为了避免全表扫描,因为全表扫描是从磁盘上读取表的每一个数据页,如果有索引指向数据值,则只需要读少次数的磁盘就可以。

带索引的表在数据库中占用更多的空间,同样增、删、改数据的命令所需时间会更长。

索引的存储机制

书中的目录是一个字词以及所在的页码列表,数据库中的索引是表中的值以及各值存储位置的列表。

聚集索引是在数据库中新开辟一个物理空间,用来存放他排列的值,当有新数据插入时,他会重新排列整个物理存储空间。

非聚集索引只包含原表中的非聚集索引的列和指向实际物理表的一个指针。

数据表的基本结构

当一个新的数据表创建时,系统将在磁盘中分配一段以8k为单位的连续空间。当一个8k用完的时候,数据库指针会自动分配一个8k的空间,每个8k的空间称为一个数据页,并分配从0-7的页号,每个文件的第0页记录引导信息叫页头,每8个数据页由64k组成形成扩展区。全部数据页的组合形成堆。

SQL Server规定行不能跨越数据页,所以每行记录的最大数量只能是8k,这就是为什么char和varchar这两种字符类型容量要限制在8k以内的原因,存储超过8k的数据应使用text类型,其实text类型的字段值不能直接录入和保存,它是存储一个指针,指向由若干个8k的数据页所组成的扩展区,真正的数据其实放在这些数据页中。

什么情况下设置索引

1、定义主键的数据列(sql server默认会给主键一个聚集索引)。

2、定义有外键的数据列

3、对于经常查询的数据列

4、对于需要在指定范围内频繁查询的数据列

5、经常在where子句中出现的数据列

6、经常出现在关键字 order by、group by、distinct后面的字段。

什么情况下不要设置索引

1、查询中很少涉及的列,重复值比较多的列。

2、text、image、bit数据类型的列

3、经常存取的列

4、经常更新操作的表,索引一般不要超过3个、最多不要5个。虽说提高了访问速度,但会影响更新操作。

聚集索引

1、使用SSMS创建聚集索引

展开要创建索引的表->右击索引->选择新建索引->聚集索引->新建索引点添加->选择列->选择升序或降序->输入名字->确定。

默认情况下,生成主键的同时将自动创建一个聚集索引。

2、使用T-SQL创建聚集索引

use webDBgocreate clustered index index_name /*聚集索引名*/on table_name(  id desc)with(drop_existing=on); /*如果存在则删除*/

每张表或者视图只能包含一个聚集索引,因为聚集索引改变了数据存储与排列方式。无论是聚集还是非聚集索引,都将信息存储在平衡树或B-树中,B-树识别类似数据并将他们组合在一起,正是由于B-树中的检索基于键值,因此索引可以提升数据访问的速度。B-树将具有类似键的组合起来,所以数据库引擎只需搜索少量页面即可找到目标记录。

非聚集索引

每张表上可以有多个非聚集索引,可以在某个列上创建一个索引,也可以在已经是现有索引组成部分的多列上创建索引。

SSMS创建方法同上,T-SQL创建方法如下:

use webDBgocreate nonclustered index fei /*聚集索引名*/on defualt(  hits desc)

添加索引选项

fillfactor:用于在创建索引时,每个索引页的数据占索引大小的百分比,默认100.当需要频繁修改表时,建议设置为70-80,不经常更新时建议90.

pad_index:用于索引中间级中每个页上保持开放的空间。不能设置值,他的值继承自fillfactor。

小例子

use webDBgocreate table ceshi --新建表(  id int identity(1,1) primary key,  name varchar(20),  code varchar(20),  [date] datetime)--插入10w条测试数据declare @n intset @n = 1while @n <100000beginInsert into ceshi (name,code,[date]) values ('name'+cast(@n as varchar(20)),'code'+cast(@n as varchar(20)),getutcdate())set @n=@n+1end--查看索引情况set statistics io on --查看磁盘ioset statistics time on --查看sql语句分析编译和执行时间select * from ceshi--查看索引情况exec sp_helpindex ceshiselect * from ceshi where name = 'name1'--ctrl+l 查看执行计划 聚集索引扫描开销100%,考虑优化为索引查找,在name上建立非聚集索引--建立非聚集索引create index name_index on ceshi(  name)--再次查看索引情况 多出来新建的非聚集索引exec sp_helpindex ceshi--在运行上面的语句select * from ceshi where name = 'name1'--明显发现速度变快了 , ctrl+l 发现聚集索引和非聚集索引各占50%

管理索引

exec sp_helpindex ceshi --查看该表中的索引exec sp_rename 'ceshi.name_index','new_name' --改名drop index ceshi.new_name --删除索引dbcc showcontig(ceshi,new_name) --检查碎片dbcc indexdefrag(webDB,ceshi,new_name) --整理碎片update statistics ceshi --更新表中所有索引的统计

表压缩

SQL Server的主要性能取决于磁盘I/O效率,SQL Server 2008提供了数据压缩功能来提高磁盘I/O效率。

表压缩意味着减小数据的磁盘占有量,所以压缩可以用在堆表、聚集索引的表、非聚集索引的表、索引视图、分区表上。

可压缩的数据类型

smallint、int、Bigint、decimal、numeric、real、float、money、smallmoeny、bit、datetime、datetime2、datetimeoffset、char、nchar、binary、rowversion。

SQLServer中有两种压缩类型:数据与备份

行压缩

压缩会改变数据的物理存储方式,但不需要对代码做任何修改。

行压缩流程:首先识别表中每一列的数据类型,然后转换为可变长度,最后将存储空间的请求总量减少到实际需求量。

如:固定长度的类型int、char、nchar等,在数据页中以不定长度的方式存储(存储真实数据长度)。

1、SSMS行压缩

表->右键->存储->管理压缩->对所有分区使用相同压缩类型->右侧 选择row->立即执行->完成。

在压缩堆表或聚集索引时并不同时包含非聚集索引,因此需要另外单独对非聚集索引进行操作。

索引->右键->存储->管理压缩->对所有分区使用相同压缩类型->右侧 选择row->立即执行->完成。

2、T-SQL行压缩

 

--在现有表进行压缩--聚集alter table ceshi rebuild with(data_compression=row)--非聚集alter index new_name on ceshi rebuild with(data_compression=row) --在创建表时进行压缩create table yasuo(  id int primary key,  name varchar(50),  mail varchar(50))with (data_compression=row)

 

创建时指定行压缩方式,这时并未发生改变。只要数据插入表中,该行即被压缩。

页压缩

页压缩通过执行额外的一些步骤增强了行压缩的功能。

页压缩步骤:行压缩、前缀压缩、字典压缩。

首先对于每一列将确定一个值,此值可以减少每一列中值的存储空间。一旦确定该值后,每一列的前缀值的行将被存储在页头中。所有的信息称为压缩信息,存储在页头之下。标识的值(前缀值)位于没列中,将由指向压缩信息部分中对应值的引用进行替换。

下一步字典压缩,搜索整个页面而非单个列,重复值被移动到页头的压缩信息部分,取而代之的是指向该值的引用。

在SSMS中页压缩步骤与行压缩步骤一致,只是选择压缩方式为Page。T-SQL中将row改成page即可。

需要注意

1、如果保留在内存中的数据是压缩的,一旦被选中,则必须先进行解压缩。

2、在插入新行时,数据也是行或页压缩的。

3、当更新或删除时,行压缩对象保留当前的压缩级别。但是页压缩可能需要重新计算,取决于发生变化的数据量。

用哪种压缩

需要频繁更新的对象应该使用行压缩。

只是执行读取操作的应该使用页压缩。