整数范围分区
描述
整数范围分区是一种基于整数列将大表分割成更小、更易管理的块的技术。 对于包含大量行的表,这特别有用,因为它可以显著提高查询性能、减少存储需求并简化维护任务。 要使这种分区方式有效运行,大多数查询必须访问特定整数范围内的数据。
让我们更详细地看看这一点,想象一个简化的 merge_request_diff_files 架构:
CREATE TABLE merge_request_diff_files (
merge_request_diff_id INT NOT NULL,
relative_order INT NOT NULL,
PRIMARY KEY (merge_request_diff_id, relative_order));现在想象 UI 中的典型查询会显示特定整数范围内的数据:
SELECT *
FROM merge_request_diff_files
WHERE merge_request_diff_id > 1 AND merge_request_diff_id < 10
LIMIT 100如果表按 merge_request_diff_id 列进行分区,基础表将如下所示:
CREATE TABLE merge_request_diff_files (
merge_request_diff_id INT NOT NULL,
relative_order INT NOT NULL,
PRIMARY KEY (merge_request_diff_id, relative_order))
PARTITION BY RANGE(merge_request_diff_id);分区表的主键必须将分区键作为主键定义的一部分。
我们可能有一个表的分区列表,例如:
merge_request_diff_files_1 FOR VALUES FROM (1) TO (20)
merge_request_diff_files_20 FOR VALUES FROM (20) TO (40)
merge_request_diff_files_40 FOR VALUES FROM (40) TO (60)每个分区都是一个独立的物理表,与基础 merge_request_diff_files 表具有相同的结构,但只包含分区键落在指定范围内的行数据。例如,分区 merge_request_diff_files_1 包含 merge_request_diff_id 列大于或等于 1 且小于 20 的行。
现在,如果我们再次查看之前的示例查询,数据库可以使用 WHERE 子句识别出所有匹配的行都在 merge_request_diff_files_1 分区中,而不是搜索所有分区中的所有数据。在大表中,这可以显著减少数据库需要访问的数据量。
示例
第 1 步:创建分区副本(版本 N)
第一步是添加一个迁移来创建原始表的分区副本。此迁移基于原始表中的数据创建适当的分区,并安装一个触发器,将原始表的写入同步到分区副本中。
例如,按 merge_request_diff_id 列对 merge_request_diff_commits 表进行分区的迁移示例如下:
class PartitionMergeRequestDiffCommits < Gitlab::Database::Migration[2.1]
include Gitlab::Database::PartitioningMigrationHelpers
disable_ddl_transaction!
def up
partition_table_by_int_range(
'merge_request_diff_commits',
'merge_request_diff_id',
partition_size: 10_000_000,
primary_key: %w[merge_request_diff_id relative_order]
)
end
def down
drop_partitioned_table_for('merge_request_diff_commits')
end
end执行此迁移后,原始表中的任何插入、更新或删除操作也会在新表中复制。对于更新和删除操作,只有当相应行存在于分区表中时,操作才会生效。
第 2 步:回填分区副本(版本 N)
第二步是添加一个部署后迁移,安排将现有数据从原始表回填到分区副本的后台任务。
继续上面的示例,迁移将如下所示:
class BackfillPartitionMergeRequestDiffCommits < Gitlab::Database::Migration[2.2]
include Gitlab::Database::PartitioningMigrationHelpers
milestone '16.10'
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
def up
enqueue_partitioning_data_migration :merge_request_diff_commits
end
def down
cleanup_partitioning_data_migration :merge_request_diff_commits
end
end此步骤内部会排队批量后台迁移,其中 BATCH_SIZE 和 SUB_BATCH_SIZE 分别为 50,000 和 2,500。有关更多详细信息,请参阅批量后台迁移指南。
第 3 步:回填后清理(版本 N+1)
此步骤必须在包含步骤 (2) 的版本之后至少一个版本中执行。这为后台迁移在 GitLab 自托管实例中正确执行提供了时间。在此步骤中,添加另一个部署后迁移来清理后台迁移后的内容。这包括强制执行任何剩余的任务,并复制可能因任务被丢弃或失败而遗漏的数据。
步骤 2 和步骤 3 之间必须有一个必要的停顿,以确保步骤 2 的后台迁移能够成功完成。
再次继续示例,此迁移将如下所示:
class CleanupPartitionMergeRequestDiffCommitsBackfill < Gitlab::Database::Migration[2.1]
include Gitlab::Database::PartitioningMigrationHelpers
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
def up
finalize_backfilling_partitioned_table :merge_request_diff_commits
end
def down
# no op
end
end此迁移完成后,原始表和分区表应包含相同的数据。安装在原始表上的触发器确保数据保持同步。
第 4 步:交换分区表和非分区表(版本 N+1)
此步骤用分区副本替换非分区表,这应该在所有其他迁移步骤成功完成后使用。
此方法的一些限制必须在交换迁移之前或期间处理:
- 分区表上不会自动重新创建二级索引和外键。
- 依赖于索引的某些类型的约束(UNIQUE 和 EXCLUDE)不会在分区表上自动重新创建,因为底层索引将不存在。
- 引用原始非分区表的外键应更新为引用分区表。PostgreSQL 11 不支持此功能。
- 引用原始表的视图不会自动更新为引用分区表。
# frozen_string_literal: true
class SwapPartitionMergeRequestDiffCommits < ActiveRecord::Migration[6.0]
include Gitlab::Database::PartitioningMigrationHelpers
def up
replace_with_partitioned_table :audit_events
end
def down
rollback_replace_with_partitioned_table :audit_events
end
end此迁移完成后:
- 分区表替换非分区(原始)表。
- 之前创建的同步触发器被删除。
分区表现在已准备好供应用程序使用。