对表分区,对数据库有很多好处,通常来说,分区的好处有两个:减少产生死锁的竞争条件,和删除分区的数据。由于分区之间是相互独立的,因此,对一个分区加X锁,不会对其他分区产生竞争,这是避免死锁(dead lock)的一种方式。对大数据表进行数据删除操作时,可以使用分区切换,使用少量的IO,从分区表中删除海量的数据。
在实际的项目中,有如下的分区函数和分区架构( Partition Scheme):
CREATE PARTITION FUNCTION [funcPartition_int_DataSourceID](int) AS RANGE LEFT FOR VALUES (1, 2, 3) CREATE PARTITION SCHEME [schePartition_int_DataSourceID] AS PARTITION [funcPartition_DataSourceID] TO ([PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY]) create table dbo.dt_test ( ...More column definition DataSourceID int ) on [schePartition_int_DataSourceID](DataSourceID)
一,减少产生死锁的竞争条件
查看ETL的执行日志,日志中记录Package执行的消息,某些Package会因为发送死锁,作为死锁牺牲品(victim)而失败。查看这些Package,发现产生死锁的根本原因是:不同的Package同时更新同一张表,导致竞争,产生死循环而发生死锁。深入分析发生死锁的TSQL脚本,两个Package都使用 DataSourceID 作为过滤条件。推测,可能的原因是这两个DataSourceID位于同一个分区:
1,验证边界值
分区函数设置的边界值是执行分区的依据,验证边界值,能够确定分区列是否处于相同的分区中:
select prv.function_id,pf.name,pf.boundary_value_on_right,prv.value as BoundaryValue from sys.partition_range_values prv inner join sys.partition_functions pf on prv.function_id=pf.function_id where pf.name='funcPartition_int_DataSourceID'
边界值小于当前 DataSourceID的最大值,因此,产生竞争的两个DataSourceID 都是在最右边的分区中。缺少分区的管理计划,这会导致额外增加的分区列(DataSourceID)都被分配到同一个分区中。
2,管理分区计划
对表表去,不仅要有效地避免死锁,减少产生死锁的竞争条件,还要在删除大表数据时,使用少量IO实现海量数据的移动,通过分区切换(switch)和数据表截断(truncate),快速删除数据。分区切换是把整个分区切换到零时表中,这就要求,该分区列必须足够小,能够全部删除。管理分区计划的最佳实践,合理选择分区列,并预留空分区。
如果一个分区是非空的,那么分区拆分(split range)会导致数据的移动,这可能是一个非常耗费IO的一个操作,为了避免密集的数据移动,最好是预留一个空的分区,一般是最右边的分区,该分区号最大,每次都把最右边的分区拆分成两个分区:
declare @CurrentMaxBoundaryValue int declare @ExistingMaxDataSourceID int declare @BoudaryValue int declare @sql_prefix nvarchar(max) declare @sql nvarchar(max) set @sql_prefix=N' alter partition scheme [schePartition_int_DataSourceID] next used [primary] alter partition function [funcPartition_int_DataSourceID]() split range (' select @ExistingMaxDataSourceID = max(ds.DataSourceID) from dbo.dt_DataSource ds with(nolock) select @CurrentMaxBoundaryValue= max(cast(prv.value as int)) from sys.partition_functions pf inner join sys.partition_range_values prv on pf.function_id=prv.function_id where pf.name='funcPartition_int_DataSourceID' -- add new boundary value if @CurrentMaxBoundaryValue<@ExistingMaxDataSourceID+1 begin set @BoudaryValue=@CurrentMaxBoundaryValue+1 while @BoudaryValue<=@ExistingMaxDataSourceID+1 begin select @sql = @sql_prefix+ cast(@BoudaryValue as varchar(10))+ N')' exec(@sql) set @BoudaryValue=@BoudaryValue+1 end end
二,可能出现的错误
1,SET选项错误
在执行分区脚本对索引进行分区的,可能会遇到SET选项错误,根据错误消息,启用想用的SET选项:
UNKNOWN TOKEN failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations. [SQLSTATE 42000] (Error 1934).
上述消息表面当前数据库的QUOTED_IDENTIFIER设置错误,使用SET命令启用QUOTED_IDENTIFIER选项:
SET QUOTED_IDENTIFIER ON
参考文档: