Adr*_*rie 5 data-warehouse database-design sql-server partitioning
SQL Cat 有一个技巧列表,标题为“构建大型关系数据仓库的 10 大最佳实践”。
在部分下,4 - Design dimension tables appropriately
他们指出:
避免对维度表进行分区。
他们没有提到为什么不应该这样做,我也没有在网上找到任何明确指出为什么要避免这样做的内容。
为什么要避免对维度表进行分区?
下面提供了一个更具体的例子来帮助回答,并讨论为什么不应该在大型关系数据仓库中进行分区。我不是在寻求有关改进特定于具体示例的数据模型的建议。如果该示例没有帮助提供有关为什么不应该进行分区维度的任何额外见解,那么请忽略它。
在我们的环境中,我们有一个Account
维度,它被分区DateEffective
并每月加载。我们的一些查询涉及WHERE DateEffective >= @ReportDate
,这似乎是分区消除的一个很好的候选者。此外,如果我们需要重新加载当月的数据,我们将删除整个月的数据,这似乎也将从表分区中受益。
自发布问题以来更新我们的环境......
上面提到的表具有非对齐的非聚集索引(使用以下 Brent Ozar 代码进行调查)。
select
[db_name] = isnull(db_name(s.database_id),db_name())
,[schema_name] = object_schema_name(i.object_id,db_id())
,[object_name] = o.name
,index_name = i.name
,index_type_desc = i.type_desc
,data_space_name = ds.name
,data_space_type_desc = ds.type_desc
,s.user_seeks
,s.user_scans
,s.user_lookups
,s.user_updates
,s.last_user_seek
,s.last_user_update
from
sys.objects as o
inner join sys.indexes as i
on o.object_id = i.object_id
inner join sys.data_spaces as ds
on ds.data_space_id = i.data_space_id
left join sys.dm_db_index_usage_stats as s
on i.object_id = s.object_id
and i.index_id = s.index_id
and s.database_id = db_id()
where
o.type = 'u'
and i.type in (1, 2)
and o.object_id in
(
select filter.object_id
from
(
select ob.object_id, ds.type_desc
from
sys.objects ob
inner join sys.indexes ind on ind.object_id = ob.object_id
inner join sys.data_spaces ds on ds.data_space_id = ind.data_space_id
group by ob.object_id, ds.type_desc
) as filter
group by filter.object_id
having count(*) > 1
)
order by
[object_name] desc
;
Run Code Online (Sandbox Code Playgroud)
clustered
该分区上的方案索引non-clustered
分区方案的 8 个索引中的5 个non-clustered
索引中的3 个primary
,rows_filegroup
unique, non-clustered
索引(为了完整起见:primary key non-clustered
在源代码管理中的创建表脚本中定义为 a )另一个更新
我找到了 Remus Rusanu 的这个答案,它阐明了与维度相关的分区表的复杂性。
他的陈述在我上面的例子中被我的解释所引用
未对齐的索引阻止了高效的分区切换操作
因此,当表被分区时,我们应该尝试对齐索引。在我的示例中,甚至不使用分区切换(?可能阻止?)来加载表,因为存在未对齐的索引。
使用对齐索引解决了这些问题,但也带来了一系列问题,因为这种物理、存储设计、选项会影响数据模型
我提供的示例肯定就是这种情况,并且需要进行一些更改才能实现对齐索引。
由于通常使用代理键作为primary key
(a unique clustered index
) 的维度,这提供了不断增加的窄键(即磁盘上的小数据大小)。这很重要,因为当维度和事实之间的连接可以更快地发生时发生的 B 树搜索。此外,这clustered index
将是non-clustered index
创建的任何es 的一部分,这也可以防止非聚集索引膨胀,在这里也创建更有效的索引查找/扫描。
为什么这很重要?
对齐的索引意味着无法再创建/强制执行唯一约束(分区列除外)
和
所有引用分区表的外键都必须包含分区键
和
这反过来要求所有引用分区表的表都包含分区键列值……以便正确声明外键约束。
影响是...
DateEffective
需要向引用帐户维度的每个表添加一列。DateEffective
在我们拥有的事实表上实现一列是多余的,因为该查找由加载正确AccountID
键值的ETL 过程负责。此外,某些事实以比date
数据类型更具选择性的粒度声明,其中DateEffective
显然是这样,使得将此列包含在事实表中更加荒谬(数据模型涟漪效应)。non-clustered index
es 需要更改以包含该DateEffective
列然而 ...
foreign key
实施限制。关于 SO 的一个很好的答案涵盖了这一点。parallel bitmap filtered hash-joins
可以优化星型连接(请参阅:通过位图过滤优化数据仓库查询性能),并且此优化不需要外键。小智 1
我怀疑这个建议是基于对维度表进行分区的可能实用性。在数据仓库中,事实表是这句格言的好例子:大数据是中等数据加上时间。维度表没有时间(不是真的),并且通常没有有用的分区属性。
你的似乎是一个很好的例子。为什么要Accounts
分区DateEffective
?“因为有些报告选择该列”并不是一个充分的答案。该列上的索引将是传统的解决方案,并且具有不偏向物理数据结构的优点。
无论您拥有多少帐户,您的事实表至少要大 1-3 个数量级。您的服务器将按照该比例进行缩放。查找账户是一个相对简单的操作。从表面上看,它似乎不适合分区。
归档时间: |
|
查看次数: |
2230 次 |
最近记录: |