InfoGrab DocsInfoGrab Docs

리스트 파티션

요약

파티셔닝할 테이블에 파티셔닝 키 칼럼을 추가합니다. 파티셔닝할 테이블을 참조하는 모든 외래 키(foreign key). 파티셔닝 키 칼럼을 추가합니다. 파티셔닝 키 칼럼을 포함하는 인덱스를 추가합니다. 기본 키 인덱스를 포함하여 모든 유니크 인덱스에 파티셔닝 키 칼럼을 포함하도록 변경합니다.

History

설명#

파티셔닝할 테이블에 파티셔닝 키 칼럼을 추가합니다. 파티셔닝 키를 다음 제약 조건에 포함해야 합니다:

  • 기본 키(primary key).

  • 파티셔닝할 테이블을 참조하는 모든 외래 키(foreign key).

  • 모든 유니크 제약 조건.

예시#

Step 1 - 파티션 키 추가#

파티셔닝 키 칼럼을 추가합니다. 예를 들어, Rails 마이그레이션에서:

class AddPartitionNumberForPartitioning < Gitlab::Database::Migration[2.1]
  TABLE_NAME = :table_name
  COLUMN_NAME = :partition_id
  DEFAULT_VALUE = 100

  def change
    add_column(TABLE_NAME, COLUMN_NAME, :bigint, default: 100)
  end
end

Step 2 - 필요한 인덱스 생성#

파티셔닝 키 칼럼을 포함하는 인덱스를 추가합니다. 예를 들어, Rails 마이그레이션에서:

class PrepareIndexesForPartitioning < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  TABLE_NAME = :table_name
  INDEX_NAME = :index_name

  def up
    add_concurrent_index(TABLE_NAME, [:id, :partition_id], unique: true, name: INDEX_NAME)
  end

  def down
    remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME)
  end
end

Step 3 - 유니크 제약 조건 적용#

기본 키 인덱스를 포함하여 모든 유니크 인덱스에 파티셔닝 키 칼럼을 포함하도록 변경합니다. [primary_key_column, :partition_id]에 대한 유니크 인덱스를 먼저 추가하는 것으로 시작할 수 있으며, 이는 다음 두 단계에 필요합니다. 예를 들어, Rails 마이그레이션에서:

class PrepareUniqueContraintForPartitioning < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  TABLE_NAME = :table_name
  OLD_UNIQUE_INDEX_NAME = :index_name_unique
  NEW_UNIQUE_INDEX_NAME = :new_index_name

  def up
    add_concurrent_index(TABLE_NAME, [:id, :partition_id], unique: true, name: NEW_UNIQUE_INDEX_NAME)

    remove_concurrent_index_by_name(TABLE_NAME, OLD_UNIQUE_INDEX_NAME)
  end

  def down
    add_concurrent_index(TABLE_NAME, :id, unique: true, name: OLD_UNIQUE_INDEX_NAME)

    remove_concurrent_index_by_name(TABLE_NAME, NEW_UNIQUE_INDEX_NAME)
  end
end

Step 4 - 외래 키 제약 조건 적용#

파티셔닝 키 칼럼을 포함하는 외래 키를 적용합니다. 예를 들어, Rails 마이그레이션에서:

class PrepareForeignKeyForPartitioning < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  SOURCE_TABLE_NAME = :source_table_name
  TARGET_TABLE_NAME = :target_table_name
  COLUMN = :foreign_key_id
  TARGET_COLUMN = :id
  FK_NAME = :fk_365d1db505_p
  PARTITION_COLUMN = :partition_id

  def up
    add_concurrent_foreign_key(
      SOURCE_TABLE_NAME,
      TARGET_TABLE_NAME,
      column: [PARTITION_COLUMN, COLUMN],
      target_column: [PARTITION_COLUMN, TARGET_COLUMN],
      validate: false,
      on_update: :cascade,
      name: FK_NAME
    )

    # This should be done in a separate post migration when dealing with a high traffic table
    validate_foreign_key(TABLE_NAME, [PARTITION_COLUMN, COLUMN], name: FK_NAME)
  end

  def down
    with_lock_retries do
      remove_foreign_key_if_exists(SOURCE_TABLE_NAME, name: FK_NAME)
    end
  end
end

on_update: :cascade 옵션은 파티셔닝 칼럼을 업데이트하려는 경우 필수입니다. 이 옵션은 업데이트를 모든 종속 행에 캐스케이드합니다. 이 옵션을 지정하지 않으면 타깃 테이블의 파티션 칼럼을 업데이트할 때 Key is still referenced from table ... 오류가 발생하고, 소스 테이블의 파티션 칼럼을 업데이트할 때는 Key is not present in table ... 오류가 발생합니다.

Step 5 - 기본 키 교체#

파티셔닝 키 칼럼을 포함하는 기본 키로 교체합니다. 이 작업은 참조하는 모든 외래 키에 파티션 키를 포함한 후에만 수행할 수 있습니다. 예를 들어, Rails 마이그레이션에서:

class PreparePrimaryKeyForPartitioning < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  TABLE_NAME = :table_name
  PRIMARY_KEY = :primary_key
  OLD_INDEX_NAME = :old_index_name
  NEW_INDEX_NAME = :new_index_name

  def up
    swap_primary_key(TABLE_NAME, PRIMARY_KEY, NEW_INDEX_NAME)
  end

  def down
    add_concurrent_index(TABLE_NAME, :id, unique: true, name: OLD_INDEX_NAME)
    add_concurrent_index(TABLE_NAME, [:id, :partition_id], unique: true, name: NEW_INDEX_NAME)

    unswap_primary_key(TABLE_NAME, PRIMARY_KEY, OLD_INDEX_NAME)

    # We need to add back referenced FKs if any, eg: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113725/diffs
  end
end

ActiveRecord는 복합 기본 키를 지원하지 않으므로, 모델에서 기본 키를 명시적으로 설정하는 것을 잊지 마세요.

class Model < ApplicationRecord
  self.primary_key = :id
end

Step 6 - 부모 테이블 생성 및 기존 테이블을 초기 파티션으로 첨부#

이제 데이터베이스 팀이 제공하는 다음 헬퍼를 사용하여 기존 테이블을 초기 파티션으로 첨부하면서 부모 테이블을 생성할 수 있습니다.

예를 들어, Rails post migrations에서 리스트 파티셔닝을 사용하는 경우:

class PrepareTableConstraintsForListPartitioning < Gitlab::Database::Migration[2.1]
  include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers

  disable_ddl_transaction!

  TABLE_NAME = :table_name
  PARENT_TABLE_NAME = :p_table_name
  FIRST_PARTITION = 100
  PARTITION_COLUMN = :partition_id

  def up
    prepare_constraint_for_list_partitioning(
      table_name: TABLE_NAME,
      partitioning_column: PARTITION_COLUMN,
      parent_table_name: PARENT_TABLE_NAME,
      initial_partitioning_value: FIRST_PARTITION
    )
  end

  def down
    revert_preparing_constraint_for_list_partitioning(
      table_name: TABLE_NAME,
      partitioning_column: PARTITION_COLUMN,
      parent_table_name: PARENT_TABLE_NAME,
      initial_partitioning_value: FIRST_PARTITION
    )
  end
end

initial_partitioning_value는 값의 배열이 될 수 있습니다. 기존 파티션의 모든 값을 포함해야 합니다. 자세한 내용은 이 이슈를 참고하세요.

class ConvertTableToListPartitioning < Gitlab::Database::Migration[2.1]
  include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers

  disable_ddl_transaction!

  TABLE_NAME = :table_name
  PARENT_TABLE_NAME = :p_table_name
  FIRST_PARTITION = 100
  PARTITION_COLUMN = :partition_id

  def up
    convert_table_to_first_list_partition(
      table_name: TABLE_NAME,
      partitioning_column: PARTITION_COLUMN,
      parent_table_name: PARENT_TABLE_NAME,
      initial_partitioning_value: FIRST_PARTITION
    )
  end

  def down
    revert_converting_table_to_first_list_partition(
      table_name: TABLE_NAME,
      partitioning_column: PARTITION_COLUMN,
      parent_table_name: PARENT_TABLE_NAME,
      initial_partitioning_value: FIRST_PARTITION
    )
  end
end

시퀀스는 라우팅 테이블이 소유하게 되고 ActiveRecord가 이를 확인할 수 없으므로, 모델에서 시퀀스 이름을 명시적으로 설정하는 것을 잊지 마세요. 이 설정은 table_name이 라우팅 테이블로 변경된 후 정리할 수 있습니다.

class Model < ApplicationRecord
  self.sequence_name = 'model_id_seq'
end

파티셔닝 제약 조건 마이그레이션이 완료되는 데 10분 이상 소요되는 경우, 바쁜 시간대에 post-migration이 실행되는 것을 피하기 위해 비동기로 실행되도록 설정할 수 있습니다.

다음 마이그레이션 AsyncPrepareTableConstraintsForListPartitioning을 앞에 추가하고 async: true 옵션을 사용합니다. 이 변경은 파티셔닝 제약 조건을 NOT VALID로 표시하고 주말 동안 테이블의 기존 데이터를 검증하는 예약된 job을 큐에 추가합니다.

그런 다음 두 번째 post-migration PrepareTableConstraintsForListPartitioning은 이전 주말에 기존 데이터가 이미 테스트되었으므로 파티셔닝 제약 조건을 검증됨으로만 표시합니다.

예를 들어:

class AsyncPrepareTableConstraintsForListPartitioning < Gitlab::Database::Migration[2.1]
  include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers

  disable_ddl_transaction!

  TABLE_NAME = :table_name
  PARENT_TABLE_NAME = :p_table_name
  FIRST_PARTITION = 100
  PARTITION_COLUMN = :partition_id

  def up
    prepare_constraint_for_list_partitioning(
      table_name: TABLE_NAME,
      partitioning_column: PARTITION_COLUMN,
      parent_table_name: PARENT_TABLE_NAME,
      initial_partitioning_value: FIRST_PARTITION,
      async: true
    )
  end

  def down
    revert_preparing_constraint_for_list_partitioning(
      table_name: TABLE_NAME,
      partitioning_column: PARTITION_COLUMN,
      parent_table_name: PARENT_TABLE_NAME,
      initial_partitioning_value: FIRST_PARTITION
    )
  end
end

Step 7 - 외래 키를 부모 테이블로 재지정#

초기 파티션을 참조하는 테이블은 이제 부모 테이블을 가리키도록 업데이트해야 합니다. 이 변경 없이는 해당 테이블의 레코드가 초기 파티션에서만 행을 찾으려 하므로 다음 파티션에 있는 행을 찾을 수 없습니다.

단계:

  • 파티셔닝된 테이블에 외래 키를 추가하고 비동기로 검증합니다. 예시.

  • GitLab.com에서 비동기 검증이 완료된 후 동기적으로 검증합니다. 예시.

  • 기존 외래 키를 제거하고 새 외래 키의 이름을 기존 이름으로 변경합니다. 예시.

Step 8 - 파티션 간 ID 고유성 보장#

모든 유니크 제약 조건은 파티셔닝 키를 포함해야 하므로 파티션 간에 중복 ID가 존재할 수 있습니다. 이를 해결하기 위해 데이터베이스만 ID 값을 설정할 수 있도록 강제하고, 시퀀스를 사용하여 고유한 값을 생성합니다. 시퀀스는 고유한 값을 생성하는 것이 보장되기 때문입니다.

이 단계는 테이블이 ID를 외부에 노출해야 하는 경우에만 필요합니다.

예를 들어:

class EnsureIdUniquenessForPCiBuilds < Gitlab::Database::Migration[2.1]
  include Gitlab::Database::PartitioningMigrationHelpers::UniquenessHelpers

  TABLE_NAME = :p_ci_builds
  SEQ_NAME = :ci_builds_id_seq

  def up
    ensure_unique_id(TABLE_NAME, seq: SEQ_NAME)
  end

  def down
    revert_ensure_unique_id(TABLE_NAME, seq: SEQ_NAME)
  end
end

Step 9 - 파티셔닝된 테이블 분석 및 새 파티션 생성#

autovacuum 데몬은 파티셔닝된 테이블을 처리하지 않습니다. 테이블 계층 구조의 통계를 최신 상태로 유지하려면 주기적으로 수동 ANALYZE를 실행해야 합니다.

partitioned: true 옵션과 함께 Ci::Partitionable을 구현하는 모델은 기본적으로 매주 분석됩니다. 이를 활성화하고 새 파티션을 생성하려면 PostgreSQL 이니셜라이저에 모델을 등록해야 합니다.

Step 10 - 파티셔닝된 테이블을 사용하도록 애플리케이션 업데이트#

이제 부모 테이블이 준비되었으므로 애플리케이션이 이를 사용하도록 업데이트할 수 있습니다:

class Model < ApplicationRecord
  self.table_name = :partitioned_table
end

모델에 따라 change management 이슈를 사용하는 것이 더 안전할 수 있습니다.

또한 다음 항목도 업데이트합니다:

  • config/gitlab_loose_foreign_keys.yml: 관련된 모든 테이블을 파티셔닝된 테이블로 재지정합니다.

  • GitLab exporter: 원래 테이블에 대한 모든 참조를 파티셔닝된 테이블로 교체합니다.

Step 11 - 첫 번째 파티션을 동적 스키마로 이동#

첫 번째 파티션은 public 스키마에 있습니다. 모든 파티션이 일관되게 관리되도록 gitlab_partitions_dynamic 스키마로 이동합니다.

예를 들어, Rails post-deployment 마이그레이션에서:

class MoveCiBuildNeedsToDynamicSchema < Gitlab::Database::Migration[2.3]
  include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum

  milestone '19.0'
  skip_require_disable_ddl_transactions!

  TABLE_NAME = :ci_build_needs
  PARENT_TABLE_NAME = :p_ci_build_needs
  PARTITION_VALUES = (100..111)

  def up
    return unless can_execute_on?(TABLE_NAME)

    move_table_to_dynamic_schema(TABLE_NAME)
  end

  def down
    return unless can_execute_on?(TABLE_NAME)

    move_table_from_dynamic_schema(
      TABLE_NAME, partition_values: PARTITION_VALUES, parent_table_name: PARENT_TABLE_NAME
    )
  end
end

리스트 파티션

GitLab v19.1
원문 보기
요약

파티셔닝할 테이블에 파티셔닝 키 칼럼을 추가합니다. 파티셔닝할 테이블을 참조하는 모든 외래 키(foreign key). 파티셔닝 키 칼럼을 추가합니다. 파티셔닝 키 칼럼을 포함하는 인덱스를 추가합니다. 기본 키 인덱스를 포함하여 모든 유니크 인덱스에 파티셔닝 키 칼럼을 포함하도록 변경합니다.

History

설명#

파티셔닝할 테이블에 파티셔닝 키 칼럼을 추가합니다. 파티셔닝 키를 다음 제약 조건에 포함해야 합니다:

  • 기본 키(primary key).

  • 파티셔닝할 테이블을 참조하는 모든 외래 키(foreign key).

  • 모든 유니크 제약 조건.

예시#

Step 1 - 파티션 키 추가#

파티셔닝 키 칼럼을 추가합니다. 예를 들어, Rails 마이그레이션에서:

class AddPartitionNumberForPartitioning < Gitlab::Database::Migration[2.1]
  TABLE_NAME = :table_name
  COLUMN_NAME = :partition_id
  DEFAULT_VALUE = 100

  def change
    add_column(TABLE_NAME, COLUMN_NAME, :bigint, default: 100)
  end
end

Step 2 - 필요한 인덱스 생성#

파티셔닝 키 칼럼을 포함하는 인덱스를 추가합니다. 예를 들어, Rails 마이그레이션에서:

class PrepareIndexesForPartitioning < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  TABLE_NAME = :table_name
  INDEX_NAME = :index_name

  def up
    add_concurrent_index(TABLE_NAME, [:id, :partition_id], unique: true, name: INDEX_NAME)
  end

  def down
    remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME)
  end
end

Step 3 - 유니크 제약 조건 적용#

기본 키 인덱스를 포함하여 모든 유니크 인덱스에 파티셔닝 키 칼럼을 포함하도록 변경합니다. [primary_key_column, :partition_id]에 대한 유니크 인덱스를 먼저 추가하는 것으로 시작할 수 있으며, 이는 다음 두 단계에 필요합니다. 예를 들어, Rails 마이그레이션에서:

class PrepareUniqueContraintForPartitioning < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  TABLE_NAME = :table_name
  OLD_UNIQUE_INDEX_NAME = :index_name_unique
  NEW_UNIQUE_INDEX_NAME = :new_index_name

  def up
    add_concurrent_index(TABLE_NAME, [:id, :partition_id], unique: true, name: NEW_UNIQUE_INDEX_NAME)

    remove_concurrent_index_by_name(TABLE_NAME, OLD_UNIQUE_INDEX_NAME)
  end

  def down
    add_concurrent_index(TABLE_NAME, :id, unique: true, name: OLD_UNIQUE_INDEX_NAME)

    remove_concurrent_index_by_name(TABLE_NAME, NEW_UNIQUE_INDEX_NAME)
  end
end

Step 4 - 외래 키 제약 조건 적용#

파티셔닝 키 칼럼을 포함하는 외래 키를 적용합니다. 예를 들어, Rails 마이그레이션에서:

class PrepareForeignKeyForPartitioning < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  SOURCE_TABLE_NAME = :source_table_name
  TARGET_TABLE_NAME = :target_table_name
  COLUMN = :foreign_key_id
  TARGET_COLUMN = :id
  FK_NAME = :fk_365d1db505_p
  PARTITION_COLUMN = :partition_id

  def up
    add_concurrent_foreign_key(
      SOURCE_TABLE_NAME,
      TARGET_TABLE_NAME,
      column: [PARTITION_COLUMN, COLUMN],
      target_column: [PARTITION_COLUMN, TARGET_COLUMN],
      validate: false,
      on_update: :cascade,
      name: FK_NAME
    )

    # This should be done in a separate post migration when dealing with a high traffic table
    validate_foreign_key(TABLE_NAME, [PARTITION_COLUMN, COLUMN], name: FK_NAME)
  end

  def down
    with_lock_retries do
      remove_foreign_key_if_exists(SOURCE_TABLE_NAME, name: FK_NAME)
    end
  end
end

on_update: :cascade 옵션은 파티셔닝 칼럼을 업데이트하려는 경우 필수입니다. 이 옵션은 업데이트를 모든 종속 행에 캐스케이드합니다. 이 옵션을 지정하지 않으면 타깃 테이블의 파티션 칼럼을 업데이트할 때 Key is still referenced from table ... 오류가 발생하고, 소스 테이블의 파티션 칼럼을 업데이트할 때는 Key is not present in table ... 오류가 발생합니다.

Step 5 - 기본 키 교체#

파티셔닝 키 칼럼을 포함하는 기본 키로 교체합니다. 이 작업은 참조하는 모든 외래 키에 파티션 키를 포함한 후에만 수행할 수 있습니다. 예를 들어, Rails 마이그레이션에서:

class PreparePrimaryKeyForPartitioning < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  TABLE_NAME = :table_name
  PRIMARY_KEY = :primary_key
  OLD_INDEX_NAME = :old_index_name
  NEW_INDEX_NAME = :new_index_name

  def up
    swap_primary_key(TABLE_NAME, PRIMARY_KEY, NEW_INDEX_NAME)
  end

  def down
    add_concurrent_index(TABLE_NAME, :id, unique: true, name: OLD_INDEX_NAME)
    add_concurrent_index(TABLE_NAME, [:id, :partition_id], unique: true, name: NEW_INDEX_NAME)

    unswap_primary_key(TABLE_NAME, PRIMARY_KEY, OLD_INDEX_NAME)

    # We need to add back referenced FKs if any, eg: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113725/diffs
  end
end

ActiveRecord는 복합 기본 키를 지원하지 않으므로, 모델에서 기본 키를 명시적으로 설정하는 것을 잊지 마세요.

class Model < ApplicationRecord
  self.primary_key = :id
end

Step 6 - 부모 테이블 생성 및 기존 테이블을 초기 파티션으로 첨부#

이제 데이터베이스 팀이 제공하는 다음 헬퍼를 사용하여 기존 테이블을 초기 파티션으로 첨부하면서 부모 테이블을 생성할 수 있습니다.

예를 들어, Rails post migrations에서 리스트 파티셔닝을 사용하는 경우:

class PrepareTableConstraintsForListPartitioning < Gitlab::Database::Migration[2.1]
  include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers

  disable_ddl_transaction!

  TABLE_NAME = :table_name
  PARENT_TABLE_NAME = :p_table_name
  FIRST_PARTITION = 100
  PARTITION_COLUMN = :partition_id

  def up
    prepare_constraint_for_list_partitioning(
      table_name: TABLE_NAME,
      partitioning_column: PARTITION_COLUMN,
      parent_table_name: PARENT_TABLE_NAME,
      initial_partitioning_value: FIRST_PARTITION
    )
  end

  def down
    revert_preparing_constraint_for_list_partitioning(
      table_name: TABLE_NAME,
      partitioning_column: PARTITION_COLUMN,
      parent_table_name: PARENT_TABLE_NAME,
      initial_partitioning_value: FIRST_PARTITION
    )
  end
end

initial_partitioning_value는 값의 배열이 될 수 있습니다. 기존 파티션의 모든 값을 포함해야 합니다. 자세한 내용은 이 이슈를 참고하세요.

class ConvertTableToListPartitioning < Gitlab::Database::Migration[2.1]
  include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers

  disable_ddl_transaction!

  TABLE_NAME = :table_name
  PARENT_TABLE_NAME = :p_table_name
  FIRST_PARTITION = 100
  PARTITION_COLUMN = :partition_id

  def up
    convert_table_to_first_list_partition(
      table_name: TABLE_NAME,
      partitioning_column: PARTITION_COLUMN,
      parent_table_name: PARENT_TABLE_NAME,
      initial_partitioning_value: FIRST_PARTITION
    )
  end

  def down
    revert_converting_table_to_first_list_partition(
      table_name: TABLE_NAME,
      partitioning_column: PARTITION_COLUMN,
      parent_table_name: PARENT_TABLE_NAME,
      initial_partitioning_value: FIRST_PARTITION
    )
  end
end

시퀀스는 라우팅 테이블이 소유하게 되고 ActiveRecord가 이를 확인할 수 없으므로, 모델에서 시퀀스 이름을 명시적으로 설정하는 것을 잊지 마세요. 이 설정은 table_name이 라우팅 테이블로 변경된 후 정리할 수 있습니다.

class Model < ApplicationRecord
  self.sequence_name = 'model_id_seq'
end

파티셔닝 제약 조건 마이그레이션이 완료되는 데 10분 이상 소요되는 경우, 바쁜 시간대에 post-migration이 실행되는 것을 피하기 위해 비동기로 실행되도록 설정할 수 있습니다.

다음 마이그레이션 AsyncPrepareTableConstraintsForListPartitioning을 앞에 추가하고 async: true 옵션을 사용합니다. 이 변경은 파티셔닝 제약 조건을 NOT VALID로 표시하고 주말 동안 테이블의 기존 데이터를 검증하는 예약된 job을 큐에 추가합니다.

그런 다음 두 번째 post-migration PrepareTableConstraintsForListPartitioning은 이전 주말에 기존 데이터가 이미 테스트되었으므로 파티셔닝 제약 조건을 검증됨으로만 표시합니다.

예를 들어:

class AsyncPrepareTableConstraintsForListPartitioning < Gitlab::Database::Migration[2.1]
  include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers

  disable_ddl_transaction!

  TABLE_NAME = :table_name
  PARENT_TABLE_NAME = :p_table_name
  FIRST_PARTITION = 100
  PARTITION_COLUMN = :partition_id

  def up
    prepare_constraint_for_list_partitioning(
      table_name: TABLE_NAME,
      partitioning_column: PARTITION_COLUMN,
      parent_table_name: PARENT_TABLE_NAME,
      initial_partitioning_value: FIRST_PARTITION,
      async: true
    )
  end

  def down
    revert_preparing_constraint_for_list_partitioning(
      table_name: TABLE_NAME,
      partitioning_column: PARTITION_COLUMN,
      parent_table_name: PARENT_TABLE_NAME,
      initial_partitioning_value: FIRST_PARTITION
    )
  end
end

Step 7 - 외래 키를 부모 테이블로 재지정#

초기 파티션을 참조하는 테이블은 이제 부모 테이블을 가리키도록 업데이트해야 합니다. 이 변경 없이는 해당 테이블의 레코드가 초기 파티션에서만 행을 찾으려 하므로 다음 파티션에 있는 행을 찾을 수 없습니다.

단계:

  • 파티셔닝된 테이블에 외래 키를 추가하고 비동기로 검증합니다. 예시.

  • GitLab.com에서 비동기 검증이 완료된 후 동기적으로 검증합니다. 예시.

  • 기존 외래 키를 제거하고 새 외래 키의 이름을 기존 이름으로 변경합니다. 예시.

Step 8 - 파티션 간 ID 고유성 보장#

모든 유니크 제약 조건은 파티셔닝 키를 포함해야 하므로 파티션 간에 중복 ID가 존재할 수 있습니다. 이를 해결하기 위해 데이터베이스만 ID 값을 설정할 수 있도록 강제하고, 시퀀스를 사용하여 고유한 값을 생성합니다. 시퀀스는 고유한 값을 생성하는 것이 보장되기 때문입니다.

이 단계는 테이블이 ID를 외부에 노출해야 하는 경우에만 필요합니다.

예를 들어:

class EnsureIdUniquenessForPCiBuilds < Gitlab::Database::Migration[2.1]
  include Gitlab::Database::PartitioningMigrationHelpers::UniquenessHelpers

  TABLE_NAME = :p_ci_builds
  SEQ_NAME = :ci_builds_id_seq

  def up
    ensure_unique_id(TABLE_NAME, seq: SEQ_NAME)
  end

  def down
    revert_ensure_unique_id(TABLE_NAME, seq: SEQ_NAME)
  end
end

Step 9 - 파티셔닝된 테이블 분석 및 새 파티션 생성#

autovacuum 데몬은 파티셔닝된 테이블을 처리하지 않습니다. 테이블 계층 구조의 통계를 최신 상태로 유지하려면 주기적으로 수동 ANALYZE를 실행해야 합니다.

partitioned: true 옵션과 함께 Ci::Partitionable을 구현하는 모델은 기본적으로 매주 분석됩니다. 이를 활성화하고 새 파티션을 생성하려면 PostgreSQL 이니셜라이저에 모델을 등록해야 합니다.

Step 10 - 파티셔닝된 테이블을 사용하도록 애플리케이션 업데이트#

이제 부모 테이블이 준비되었으므로 애플리케이션이 이를 사용하도록 업데이트할 수 있습니다:

class Model < ApplicationRecord
  self.table_name = :partitioned_table
end

모델에 따라 change management 이슈를 사용하는 것이 더 안전할 수 있습니다.

또한 다음 항목도 업데이트합니다:

  • config/gitlab_loose_foreign_keys.yml: 관련된 모든 테이블을 파티셔닝된 테이블로 재지정합니다.

  • GitLab exporter: 원래 테이블에 대한 모든 참조를 파티셔닝된 테이블로 교체합니다.

Step 11 - 첫 번째 파티션을 동적 스키마로 이동#

첫 번째 파티션은 public 스키마에 있습니다. 모든 파티션이 일관되게 관리되도록 gitlab_partitions_dynamic 스키마로 이동합니다.

예를 들어, Rails post-deployment 마이그레이션에서:

class MoveCiBuildNeedsToDynamicSchema < Gitlab::Database::Migration[2.3]
  include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum

  milestone '19.0'
  skip_require_disable_ddl_transactions!

  TABLE_NAME = :ci_build_needs
  PARENT_TABLE_NAME = :p_ci_build_needs
  PARTITION_VALUES = (100..111)

  def up
    return unless can_execute_on?(TABLE_NAME)

    move_table_to_dynamic_schema(TABLE_NAME)
  end

  def down
    return unless can_execute_on?(TABLE_NAME)

    move_table_from_dynamic_schema(
      TABLE_NAME, partition_values: PARTITION_VALUES, parent_table_name: PARENT_TABLE_NAME
    )
  end
end