代码改变世界

【原】Sql Server性能优化——Partition(管理分区)

2010-03-09 10:47  拖鞋不脱  阅读(4760)  评论(1编辑  收藏  举报

正如上一篇文章SqlServer性能优化——Partition(创建分区)中所述,分区并不是一个一劳永逸的操作,对一张表做好分区仅仅是开始,接下来可能需要频繁的变更分区,管理分区。

在企业管理器中,虽然有“管理分区”的菜单,里面的内容却可能与你的预想不同,这里并没有提供直接对分区进行操作的方法,所以一些普通的操作,比如“增加分区”、“删除分区”之类的操作就需要通过脚本实现了。

增加分区(Split Partition)

增加分区”事实上就是将现有的分区分割开,基于此,在SQL Server中应用的是Split操作。在分离分区的时候,不仅仅要在Partition Function上指定分割的分界点,同样需要在Partition Scheme上指定新分区应用的文件组:

--指定下一个分区应用文件组PRIMARY
ALTER PARTITION SCHEME [MyPartitionSchema] 
NEXT USED [PRIMARY]
--指定分区分界点为5000
ALTER PARTITION FUNCTION MyPartitionFunction()
SPLIT RANGE (5000)

需要注意的一点是,新增的分区中非聚簇索引的压缩模式会被置为None

删除分区(Merge Partition)

删除分区”同样可以认为是将原来分离的分区合并在一起,所以对应的是Merge操作,而且由于并没有新增的分区,Partition Scheme并不需要改变:

ALTER PARTITION FUNCTION MyPartitionFunction ()
MERGE RANGE (5000)

切换分区(Switch Partition)

切换分区”可能是一个比看上去会应用的更频繁的操作,它的意义在于将一个分区的数据从一张表切换到另一张表中。这里定义被切换分区的表为“源表”,被切换到的表为“目标表”,则执行切换操作的前提是:

  1. 源表和目标表拥有同样的表结构,即同样的字段、字段类型,同样的索引结构(聚簇和非聚簇),同样的压缩格式。但不要求默认值约束一致(Default Constaint),也不要求目标表设置了和源表一样的自增长列。
  2. 源表如果有索引且分区,则其索引必须对齐
  3. 基于上一条,如果源表中的索引是唯一索引(聚集或非聚集),则分区依据列必须是唯一索引键锁使用的列。这是考虑到,当对唯一索引上的唯一约束进行检测时,如果索引已进行了分区,则检测只在分区内部进行。如果索引列中不包含分区依据列,则无法保证相同的索引值落在同一分区内,也就无法进行唯一约束。http://msdn.microsoft.com/zh-cn/library/ms187526(v=sql.105).aspx
  4. 源表中被切换的分区范围必须包含于目标表或者目标表将要被切换到的分区范围。这里有如下几种情况:
    1. 将源表的源分区切换到目标表的目标分区中,则目标分区范围>=源分区;
    2. 将源表的源分区切换到目标表中(目标表未分区),则目标表没有设约束,或约束范围>=源分区;
    3. 将源表切换到目标表中(源表、目标表都未分区),则只要目标表没有设约束就可以了(虽然Switch是分区提出的操作,但一个没有分区的表同样可以被看做一个大分区,所以可以对没有分区的表进行Switch操作)。
  5. 目标表或目标分区不能含有数据

下面的操作将源表的第二个分区切换到目标表的第二个分区中。

ALTER TABLE [STable] SWITCH PARTITION 2 TO [DTable] PARTITION 2

分区管理操作的性能

分割、合并以及切换分区是元数据上的操作而不是对数据的移动,所以操作的效率要比直接操作数据高很多。

  1. 对于分割分区,操作时间和被分割分区的数据量相关,数据越大则分割花费的时间会越长。
  2. 对于合并分区,如果将两个空的分区合并,自然不会耗什么时间;如果两个分区都有数据,则和分割分区一样,数据越大花费的时间越长;如果两个分区中有一个没有数据,笔者的经验是如果有大数据量的分区在右(>分界值),则消耗的时间较短,如果有大数据量的分区在左(<分界值),则会消耗较多的时间。
  3. 对于切换分区,即使是上千万级别的数据,也可以在不到1秒的时间完成分区的切换。所以虽然从表面上看,切换分区和调用Select或者Insert语句移动数据的结果是一样的,但效率却是不可同日而语的。

查看分区信息

除了利用上文提到的通过“管理压缩”的方式查看某张表的分区信息之外,SQL Server还提供了一张系统表查看数据库中的分区情况:

  1. SYS.PARTITION_SCHEMES,数据库中所有分区方案的信息,包括对应的分区函数的ID。
  2. SYS.PARTITION_FUNCTIONS,数据库中所有分区函数的信息,包括分区数等信息。
  3. SYS.PARTITION_RANGE_VALUES,每个分区范围的信息,可以和SYS.PARTITION_FUNCTIONS联查。

比如可以通过如下的脚本,查出分区函数MyPartitionFunc的第一个分区的右边界:

SELECT value FROM sys.partition_range_values, sys.partition_functions 
WHERE sys.partition_functions.function_id = sys.partition_range_values.function_id AND sys.partition_functions.name = 'MyPartitionFunc' AND boundary_id = 1

还可以通过如下脚本,获取分区表中各分区的数据情况(行数,最大值,最小值):

SELECT 
    partition = $PARTITION.MyParitionFunc([ParitionDate])
    ,rows      = COUNT(*)
    ,min    = MIN([ParitionDate])
    ,max    = MAX([ParitionDate])
FROM [MyTable]
GROUP BY $PARTITION.MyParitionFunc([ParitionDate])
ORDER BY PARTITION

具体可以参照MSDN:从已分区表和索引中查询数据和元数据