Table 分区以避免锁定
Table Partitioning to avoid locks
TLDR;我正在尝试对 table 进行分区,这样每个分区都可以在不锁定其他分区的情况下进行操作,但它不起作用。
背景:我正在创建一个数据管道进程,将数据从中央公司数据库导出到 SQL 服务器数据库,在那里它可以用于分析.我每天需要导出完整的table(目前正在删除旧的,稍后归档),它可能接近 10GB。
为了使其足够快以便可用,我根据“计划”列将 table 分成 15 个块。这是在数据管道中完成的,因此“计划”列中的 15 个可能值中的每一个都被独立处理;每个计划块都从公司数据库中提取,之后在 SQL 服务器 table 中删除该块的旧数据并上传新数据。
问题是,在 15 个块中执行此操作意味着所有进程相互阻塞。一切都必须等待其他一切,最终导致一切变得非常缓慢,并且经常因超时而失败。
为了解决这个问题,我:
- 为
中的table创建了16个SQL服务器文件组
- 向每个文件组添加了一个文件
- 在计划列上对 table 进行了分区(每个文件组的边界是一个计划的名称)
根据 the answer here 这应该允许我“在一个 gulp 中删除或添加大量数据”。
虽然这似乎没有帮助;我仍然得到块。
那么两个问题:
- 是否可以分区使得每个分区都可以处理而不会阻塞其他分区?如果是,怎么做?
- 我是否必须每天重新运行分区作业?我发现的关于分区的各种教程有时似乎暗示了这一点;我不清楚数据库是否自动维护分区。
Alternative 我还考虑过将目标 table 分成 15 个 table,并尝试将它们与视图重新组合。那会更好吗?更糟?
我过去遇到过类似的问题,并发工作人员正在处理单个物理分区中的行和不同的分区,但仍然相互阻塞。
发生这种情况的一个原因是,如果其中一名工作人员设法将他们的锁定升级到 table 级别。您需要检查阻塞期间的等待任务,看看这是否适用于您。
SQL 服务器确实支持将锁升级到分区级别,但这不是默认设置(因为在某些情况下可能会导致更大的死锁概率)。
要启用此选项,您需要设置 LOCK_ESCALATION = AUTO
。
如果您正在做的工作是替换分区中的所有行,那么您应该考虑 Charlieface's 注释并在每个分区的新空 table 中执行此操作并使用 TRUNCATE TABLE ... WITH (PARTITIONS ())
或 ALTER TABLE ... SWITCH
清除旧数据并 ALTER TABLE ... SWITCH
引入新数据(两者通常都应该是非常快速的操作)。这意味着插入是真正独立的,并且这种模式可能更有益 w.r.t。插入的最小日志记录。
TLDR;我正在尝试对 table 进行分区,这样每个分区都可以在不锁定其他分区的情况下进行操作,但它不起作用。
背景:我正在创建一个数据管道进程,将数据从中央公司数据库导出到 SQL 服务器数据库,在那里它可以用于分析.我每天需要导出完整的table(目前正在删除旧的,稍后归档),它可能接近 10GB。
为了使其足够快以便可用,我根据“计划”列将 table 分成 15 个块。这是在数据管道中完成的,因此“计划”列中的 15 个可能值中的每一个都被独立处理;每个计划块都从公司数据库中提取,之后在 SQL 服务器 table 中删除该块的旧数据并上传新数据。
问题是,在 15 个块中执行此操作意味着所有进程相互阻塞。一切都必须等待其他一切,最终导致一切变得非常缓慢,并且经常因超时而失败。
为了解决这个问题,我:
- 为 中的table创建了16个SQL服务器文件组
- 向每个文件组添加了一个文件
- 在计划列上对 table 进行了分区(每个文件组的边界是一个计划的名称)
根据 the answer here 这应该允许我“在一个 gulp 中删除或添加大量数据”。
虽然这似乎没有帮助;我仍然得到块。
那么两个问题:
- 是否可以分区使得每个分区都可以处理而不会阻塞其他分区?如果是,怎么做?
- 我是否必须每天重新运行分区作业?我发现的关于分区的各种教程有时似乎暗示了这一点;我不清楚数据库是否自动维护分区。
Alternative 我还考虑过将目标 table 分成 15 个 table,并尝试将它们与视图重新组合。那会更好吗?更糟?
我过去遇到过类似的问题,并发工作人员正在处理单个物理分区中的行和不同的分区,但仍然相互阻塞。
发生这种情况的一个原因是,如果其中一名工作人员设法将他们的锁定升级到 table 级别。您需要检查阻塞期间的等待任务,看看这是否适用于您。
SQL 服务器确实支持将锁升级到分区级别,但这不是默认设置(因为在某些情况下可能会导致更大的死锁概率)。
要启用此选项,您需要设置 LOCK_ESCALATION = AUTO
。
如果您正在做的工作是替换分区中的所有行,那么您应该考虑 Charlieface's 注释并在每个分区的新空 table 中执行此操作并使用 TRUNCATE TABLE ... WITH (PARTITIONS ())
或 ALTER TABLE ... SWITCH
清除旧数据并 ALTER TABLE ... SWITCH
引入新数据(两者通常都应该是非常快速的操作)。这意味着插入是真正独立的,并且这种模式可能更有益 w.r.t。插入的最小日志记录。