- 배경 정보
- text 열을 가진 새로운 테이블 만들기
- 기존 테이블에 text 열 추가
- 기존 열에 text 제한 제약 추가
- 기존 열의 텍스트 제한 제약 조건 증가
- 대규모 테이블의 텍스트 제한 제약 조건
문자열 및 텍스트 데이터 유형
문자열이나 기타 텍스트 정보를 저장하는 새 열을 추가할 때:
-
문자열
데이터 유형 대신에 항상text
데이터 유형을 사용합니다. -
text
열은 항상create_table
에서#text ... limit: 100
헬퍼(아래 참조)를 사용하여 테이블을 만들 때 또는 기존 테이블을 변경할 때add_text_limit
를 사용하여 꼭 제한을 설정해야 합니다.
기본 Rails text
열 유형은 제한을 정의할 수 없지만, 우리는 create_table
을 확장하여 limit: 255
옵션을 추가합니다. create_table
외부에서는 add_text_limit
를 사용하여 이미 존재하는 열에 체크 제약 조건을 추가할 수 있습니다.
배경 정보
우리가 항상 문자열
대신 text
를 사용하고 싶어하는 이유는 문자열
열은 제한을 업데이트하기 원할 경우 ALTER TABLE ...
명령을 실행해야 한다는 단점이 있기 때문입니다.
제한이 추가되는 중 ALTER TABLE ...
명령은 테이블에 EXCLUSIVE LOCK
을 요구하며, 이는 열을 업데이트하고 모든 기존 레코드를 유효성 검사하는 과정 동안 유지되어야 합니다. 이 프로세스는 대형 테이블의 경우 시간이 걸릴 수 있습니다.
반면에 PostgreSQL에서 text는 거의 문자열과 동등하면서, 기존 열에 제한을 추가하거나 제한을 업데이트하는 것은 매우 비용이 많이 드는 EXCLUSIVE LOCK
이 필요하지 않다는 추가적인 이점이 있습니다. 우리는 유효한 옵션을 끈다면 제약을 업데이트하기 위해서는 EXCLUSIVE LOCK
이 필요하지만, 이는 열의 선언을 업데이트하는 것에 대해서만 적용되고 있다. 그런 후 VALIDATE CONSTRAINT
를 사용하여 나중에 유효성을 검증할 수 있습니다(SHARE UPDATE EXCLUSIVE LOCK
만 필요하며 다른 유효성과 인덱스 생성과 충돌하지만 읽고 쓰기는 가능합니다).
attr_encrypted
속성에 대해 text 열을 사용하지 마세요. 대신 :binary
열을 사용하세요.text 열을 가진 새로운 테이블 만들기
새로운 테이블을 추가할 때, text 열의 제한은 테이블 생성과 동일한 마이그레이션에 추가되어야 합니다. 이 열에 제한을 추가할 수 있도록 Rails의 #text
메서드에 limit:
속성을 추가합니다.
예를 들어, db/migrate/20200401000001_create_db_guides.rb
에서 두 개의 text 열, title
과 notes
가 있는 테이블을 생성하는 마이그레이션을 고려해보세요:
class CreateDbGuides < Gitlab::Database::Migration[2.1]
def change
create_table :db_guides do |t|
t.bigint :stars, default: 0, null: false
t.text :title, limit: 128
t.text :notes, limit: 1024
end
end
end
기존 테이블에 text 열 추가
기존 테이블에 열을 추가하려면 해당 테이블에 대해 독점적 잠금이 필요합니다. 이 잠금이 잠시동안 유지되지만, add_column
이 실행되는 시간은 테이블에 얼마나 자주 액세스되는지에 따라 달라질 수 있습니다. 예를 들어, 매우 자주 액세스되는 테이블에 대해 독점적 잠금을 획들하는 것은 GitLab.com에서 몇 분이 걸릴 수 있으며 with_lock_retries
를 사용해야 합니다.
text 제한을 추가할 때, 트랜잭션이 disable_ddl_transaction!
로 비활성화되어야 합니다. 이는 마이그레이션이 나중에 실패해도 해당 열이 롤백되지 않는다는 것을 의미합니다. 마이그레이션을 다시 실행하려고 시도하면 이미 존재하는 열 때문에 오류가 발생합니다.
이러한 이유로 기존 테이블에 text 열을 추가하는 것은 다음 중 하나로 수행될 수 있습니다:
별도의 마이그레이션에 열과 제한 추가하기
테이블 sprints
에 새로운 text 열 extended_title
을 추가하는 마이그레이션을 고려해보세요, db/migrate/20200501000001_add_extended_title_to_sprints.rb
:
class AddExtendedTitleToSprints < Gitlab::Database::Migration[2.1]
# rubocop:disable Migration/AddLimitToTextColumns
# limit is added in 20200501000002_add_text_limit_to_sprints_extended_title
def change
add_column :sprints, :extended_title, :text
end
# rubocop:enable Migration/AddLimitToTextColumns
end
두 번째 마이그레이션은 extended_title
에 제한을 추가합니다, db/migrate/20200501000002_add_text_limit_to_sprints_extended_title.rb
:
class AddTextLimitToSprintsExtendedTitle < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
add_text_limit :sprints, :extended_title, 512
end
def down
# 다운이 필요한 이유는 `add_text_limit`가 되돌릴 수 없기 때문입니다.
remove_text_limit :sprints, :extended_title
end
end
열과 제한을 확인하여 하나의 마이그레이션에 열과 제한 추가하기
테이블 sprints
에 새로운 text 열 extended_title
을 추가하는 마이그레이션을 고려해보세요, db/migrate/20200501000001_add_extended_title_to_sprints.rb
:
class AddExtendedTitleToSprints < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
with_lock_retries do
add_column :sprints, :extended_title, :text, if_not_exists: true
end
add_text_limit :sprints, :extended_title, 512
end
def down
with_lock_retries do
remove_column :sprints, :extended_title, if_exists: true
end
end
end
기존 열에 text 제한 제약 추가
기존 데이터베이스 열에 text 제한을 추가하는 것은 최소 두 가지 다른 릴리스로 나뉘어져 여러 단계로 진행해야 합니다:
-
릴리스
N.M
(현재 릴리스)-
validate: false
를 사용하여 텍스트 열에 제한을 추가하는 사후 배포 마이그레이션 실행. -
기존 레코드를 수정하기 위한 사후 배포 마이그레이션 실행.
테이블의 크기에 따라 다음 릴리스에서 정리를 위한 백그라운드 마이그레이션이 필요할 수 있습니다. 자세한 내용은 대규모 테이블의 텍스트 제한 제약을 참조하십시오. - 텍스트 제한을 검증하기 위해 다음 마일스톤에 대한 이슈를 생성합니다.
-
-
릴리스
N.M+1
(다음 릴리스)- 사후 배포 마이그레이션을 사용하여 텍스트 제한을 검증합니다.
예제
특정 릴리스 마일스톤(예: 13.0)에 issues.title_html
에 1024
제한을 추가하려고 한다고 가정해 보겠습니다.
Issues는 2500만 개 이상의 레코드를 가진 크고 바쁜 테이블이므로 이를 실행하는 동안 이 테이블에 액세스하려는 다른 프로세스를 모두 잠그고 싶지 않습니다.
또한, 우리의 프로덕션 데이터베이스를 확인한 후에 issues
에서 title
의 문자 길이가 1024보다 큰 레코드가 있는 것을 알게 되어 전체적인 목적으로 이 제약을 하나의 단계로 추가하고 유효성을 검증할 수 없습니다.
새로운 유효하지 않은 레코드를 방지하기(현재 릴리스)
우리는 먼저 NOT VALID
체크 제약 조건으로 제한을 추가하여, 새 레코드가 추가되는 경우 또는 기존 레코드가 업데이트되는 경우에 일관성을 강제합니다.
위의 예시에서 1024 글자보다 더 많은 제목이 있는 기존 이슈는 영향을 받지 않고, 여전히 issues
테이블의 레코드를 업데이트할 수 있습니다. 그러나 title_html
을 1024글자보다 많은 타이틀로 업데이트하려고 하면 제약이 데이터베이스 오류를 발생시킵니다.
기존 속성에 제약을 추가하거나 제거하려면 모든 애플리케이션 변경 사항이 먼저 배포되어야 하며, 그렇지 않으면 여전히 이전 버전의 애플리케이션 서버에서는 유효하지 않은 값으로 속성을 업데이트하려고 할 수 있습니다고 합니다. 이러한 이유로 add_text_limit
는 사후 배포 마이그레이션에서 실행해야 합니다.
이전 예제에서 현재 릴리스인 13.0 마일스톤을 위해 모델 Issue
에 다음과 같은 유효성 검사가 추가되었다고 가정해 봅시다:
validates :title_html, length: { maximum: 1024 }
또한 동일한 마일스톤에서 validate: false
로 text 제한을 추가하는 사후 배포 마이그레이션을 통해 데이터베이스를 업데이트할 수 있습니다, db/post_migrate/20200501000001_add_text_limit_migration.rb
:
class AddTextLimitMigration < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
# 이렇게 하면 유효성을 검증없이 제약을 추가합니다
add_text_limit :issues, :title_html, 1024, validate: false
end
def down
# 다운이 필요한 이유는 `add_text_limit`가 되돌릴 수 없기 때문입니다.
remove_text_limit :issues, :title_html
end
end
현재 릴리스의 기존 레코드를 수정하기 위한 데이터 이민
이 접근 방식은 데이터 양과 정리 전략에 따라 다릅니다. GitLab.com에서 수정해야 하는 레코드 수는 후속 이민을 사용할지 뒷받침 데이터 이민을 사용할지 결정하는 데 도움이 되는 좋은 지표입니다.
- 데이터 양이
1,000
건 미만이면 데이터 이민은 후속 이민 내에서 실행될 수 있습니다. - 데이터 양이
1,000
건 이상이면 백그라운드 데이터 이민을 생성하는 것이 좋습니다.
어떤 옵션을 사용할지 확신이 안 들 때는 데이터베이스 팀에 문의하십시오.
예를 들어, 이슈 테이블은 상당히 크고 빈번히 액세스되므로 현재 13.0 마일스톤에 대해 백그라운드 이민을 추가할 것입니다. db/post_migrate/20200501000002_schedule_cap_title_length_on_issues.rb
:
class ScheduleCapTitleLengthOnIssues < Gitlab::Database::Migration[2.1]
# GitLab.com에서 영향을 받는 레코드 수 등에 대한 정보
# 각 배치당 평균 실행 시간 등...
BATCH_SIZE = 5000
DELAY_INTERVAL = 2.minutes.to_i
# 백그라운드 이민은 제목이 1024 글자 제한을 초과하는 이슈를 업데이트할 것입니다.
ISSUES_BACKGROUND_MIGRATION = 'CapTitleLengthOnIssues'.freeze
disable_ddl_transaction!
def up
queue_batched_background_migration(
ISSUES_BACKGROUND_MIGRATION,
:issues,
:id,
job_interval: DELAY_INTERVAL,
batch_size: BATCH_SIZE
)
end
def down
delete_batched_background_migration(ISSUES_BACKGROUND_MIGRATION, :issues, :id, [])
end
end
이 안내서를 간결하게 유지하기 위해 백그라운드 이민의 정의는 생략하고 후속 이민을 예약하는 데 사용되는 포스트-디플로이먼트 이민의 고수준 예제만 제공했습니다. 배치된 백그라운드 이민 가이드에서 자세한 정보를 찾을 수 있습니다.
텍스트 제한 검증 (다음 릴리스)
텍스트 제한 검증은 전체 테이블을 스캔하고 각 레코드가 올바른지 확인합니다.
여전히 예시로, 다음인 13.1 마일스톤에서 validate_text_limit
이민 헬퍼를 최종 포스트-디플로이먼트 이민에서 실행할 것입니다. db/post_migrate/20200601000001_validate_text_limit_migration.rb
:
class ValidateTextLimitMigration < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
validate_text_limit :issues, :title_html
end
def down
# no-op
end
end
기존 열의 텍스트 제한 제약 조건 증가
기존 데이터베이스 열의 텍스트 제한을 높이는 것은 먼저 새로운 제한(다른 이름으로)을 추가한 후 이전 제한을 삭제함으로써 안전하게 수행할 수 있습니다.
class ChangeMaintainerNoteLimitInCiRunner < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
def up
add_text_limit :ci_runners, :maintainer_note, 1024, constraint_name: check_constraint_name(:ci_runners, :maintainer_note, 'max_length_1K')
remove_text_limit :ci_runners, :maintainer_note, constraint_name: check_constraint_name(:ci_runners, :maintainer_note, 'max_length')
end
def down
# no-op: 레코드의 길이(maintainer_note)가 255보다 큰 경우 실패 가능성 있음
end
end
대규모 테이블의 텍스트 제한 제약 조건
실제로 매우 크기가 큰 테이블(예: ci_builds
의 artifacts
)의 텍스트 열을 정리해야 하는 경우, 백그라운드 이민이 길어지고 추가적인 배치된 백그라운드 이민 정리이 필요한 경우 데이터 이민을 추가한 후에 릴리스를 통해 이어지는 3 단계가 필요합니다:
-
N.M
릴리스 - 텍스트 제한과 기존 레코드를 수정하기 위한 백그라운드 이민을 추가합니다. -
N.M+1
릴리스 - 백그라운드 이민을 정리합니다. -
N.M+2
릴리스 - 텍스트 제한을 검증합니다.