무중단 스키마 변경과 대량 작업
운영 중인 테이블을 건드릴 때는 잠금 시간과 잠금 범위를 먼저 계산한다
무엇이 위험한가
| 작업 | 위험 | 이유 |
|---|
| NOT NULL 컬럼 추가 | 높다 | 기본값을 채우며 전체 행을 다시 쓴다 |
| 컬럼 타입 변경 | 높다 | 전체 행을 다시 쓴다 |
| 인덱스 추가 | 중간 | 기본 문법은 쓰기를 막는다 |
| NULL 허용 컬럼 추가 | 낮다 | 최신 버전은 메타데이터만 바꾼다 |
| 컬럼 삭제 | 낮다 | 표시만 하고 공간은 나중에 정리 |
| 컬럼 이름 변경 | 낮지만 위험 | 빠르지만 배포 중 옛 코드가 깨진다 |
핵심은 잠금을 얼마나 오래 쥐는가입니다. 전체 행을 다시 쓰는 작업은 행수에 비례해서, 1억 행이면 수십 분입니다. 그동안 그 테이블에 대한 읽기와 쓰기가 막힙니다.
NOT NULL 컬럼을 안전하게 추가하는 순서
| 순서 | 작업 |
|---|
| 1 | NULL 허용으로 컬럼을 추가한다 |
| 2 | 애플리케이션이 새 값을 함께 쓰게 배포한다 |
| 3 | 기존 행의 값을 나눠서 채운다 |
| 4 | NOT NULL 제약을 건다 |
3번을 한 문장으로 하지 않는 것이 중요합니다. 1억 행을 한 번에 갱신하면 트랜잭션이 커지고 잠금이 길어집니다. 1만 건씩 나눠 돌리면서 사이에 짧은 휴식을 둡니다.
컬럼 이름을 바꿀 때
빠르지만 위험합니다. 배포 중에는 옛 코드와 새 코드가 함께 도는데, 옛 코드는 옛 이름을 찾습니다.
| 순서 | 작업 |
|---|
| 1 | 새 이름의 컬럼을 추가한다 |
| 2 | 두 컬럼에 모두 쓰도록 배포한다 |
| 3 | 기존 데이터를 복사한다 |
| 4 | 읽기를 새 컬럼으로 바꿔 배포한다 |
| 5 | 옛 컬럼 쓰기를 멈추고 배포한다 |
| 6 | 옛 컬럼을 지운다 |
번거롭지만 각 단계에서 옛 코드와 새 코드가 함께 동작합니다.
대량 삽입
한 건씩 넣으면 왕복과 트랜잭션 비용이 건수만큼 듭니다.
| 방법 | 100만 건 기준 |
|---|
| 한 건씩 삽입 | 왕복 100만 번. 매우 느리다 |
| 묶음 삽입 | 1,000건씩 묶으면 왕복 1,000번 |
| 대량 적재 명령 | 파일에서 직접 읽어 가장 빠르다 |
| 인덱스 나중에 만들기 | 적재 후 인덱스를 만들면 빠르다 |
주의할 점도 있습니다.
| 항목 | 내용 |
|---|
| 트랜잭션 크기 | 100만 건을 한 트랜잭션에 넣으면 로그가 커지고 되돌리기가 비싸다 |
| 잠금 | 큰 삽입이 다른 쓰기를 방해할 수 있다 |
| 복제 지연 | 대량 변경이 복제본에 전파되며 지연을 만든다 |
| 실패 처리 | 중간에 실패하면 어디까지 됐는지 알 수 있게 한다 |
대량 삭제
DELETE 는 행을 하나씩 지우고 로그를 남깁니다. 전체를 지운다면 다른 방법이 낫습니다.
| 방법 | 특징 |
|---|
| DELETE | 조건부 삭제. 느리고 로그가 크다. 되돌릴 수 있다 |
| TRUNCATE | 전체 삭제. 빠르지만 조건을 걸 수 없다 |
| 파티션 삭제 | 오래된 파티션을 떼어낸다. 가장 빠르다 |
| 나눠서 삭제 | 조건부로 조금씩. 운영 중에는 이 방법 |
Q.운영 중인 테이블에 NOT NULL 컬럼을 추가하려면 어떻게 하나요?
한 번에 하지 않고 NULL 허용으로 추가한 뒤 채우고 제약을 거는 순서로 나눕니다.
| 순서 | 작업 | 이유 |
|---|
| 1 | NULL 허용으로 컬럼 추가 | 메타데이터만 바뀌어 빠르다 |
| 2 | 새 값을 함께 쓰도록 배포 | 이후 들어오는 행은 값이 있다 |
| 3 | 기존 행을 나눠서 채운다 | 한 번에 하면 잠금이 길다 |
| 4 | NOT NULL 제약을 건다 | 이미 값이 다 있어 검사가 빠르다 |
한 번에 하면 무엇이 문제인지 보면 이렇습니다. 기본값이 있는 NOT NULL 컬럼을 추가하면 DB 가 모든 행에 그 값을 채워야 하고, 그동안 테이블 전체가 잠깁니다. 1억 행이면 수십 분이고 서비스가 멈춥니다.
3번을 나누는 방법입니다.
| 항목 | 내용 |
|---|
| 묶음 크기 | 1만 건 정도. 한 트랜잭션이 짧게 끝나도록 |
| 사이 휴식 | 짧게 쉬어 다른 쿼리가 끼어들 틈을 준다 |
| 진행 추적 | 마지막 처리 id 를 기록해 재시작할 수 있게 |
흔한 실수: 최신 DB 버전은 기본값 추가가 빠르다는 것만 알고 그대로 실행하는 것. 버전과 조건에 따라 다르고, 조건을 벗어나면 전체를 다시 씁니다. 실행 전에 그 버전의 동작을 확인해야 합니다.
Q.100만 건을 DB 에 넣어야 합니다. 어떻게 하나요?
한 건씩 넣지 않고 묶어서 보냅니다. 왕복 횟수가 그대로 시간이 됩니다.
| 방법 | 왕복 | 상대적 속도 |
|---|
| 한 건씩 | 100만 번 | 가장 느리다 |
| 1,000건씩 묶음 | 1,000번 | 크게 빨라진다 |
| 대량 적재 명령 | 1번 | 가장 빠르다 |
함께 볼 것들입니다.
| 항목 | 판단 |
|---|
| 트랜잭션 크기 | 전체를 한 트랜잭션으로 묶지 않는다. 로그가 커지고 실패 시 전체를 되돌린다 |
| 인덱스 | 적재 후에 만드는 편이 빠르다. 운영 중이면 불가 |
| 잠금과 복제 | 대량 변경이 다른 쓰기를 방해하고 복제 지연을 만든다 |
| 재시작 | 어디까지 넣었는지 기록해 중단 후 이어갈 수 있게 |
| 유입 속도 조절 | 운영 중이면 초당 건수에 상한을 둔다 |
운영 중과 초기 적재는 판단이 다릅니다. 초기 적재라면 인덱스를 나중에 만들고 최대 속도로 넣습니다. 운영 중이라면 속도를 낮추고 나눠서 넣는 편이 안전합니다.
흔한 실수: 반복문 안에서 커밋을 매번 하는 것. 트랜잭션 시작과 커밋이 100만 번 일어나 그 비용이 삽입보다 커집니다.
Q.컬럼 이름을 바꾸는 것이 왜 위험한가요?
DB 작업 자체는 빠르지만, 배포 중에 옛 코드와 새 코드가 함께 돌기 때문입니다.
| 시점 | 옛 코드 | 새 코드 |
|---|
| 이름 변경 직후 | 옛 이름을 찾아 오류 | 정상 |
| 배포 완료 후 | 없음 | 정상 |
배포는 순간이 아닙니다. 서버를 하나씩 교체하는 동안 두 버전이 공존하고, 그 사이 옛 버전의 요청이 전부 실패합니다. 되돌리기도 어렵습니다. 이전 버전으로 롤백하면 그것도 옛 이름을 찾습니다.
안전한 순서입니다.
| 순서 | 작업 |
|---|
| 1 | 새 이름의 컬럼을 추가한다 |
| 2 | 두 곳에 모두 쓰도록 배포한다 |
| 3 | 기존 데이터를 복사한다 |
| 4 | 읽기를 새 컬럼으로 바꿔 배포한다 |
| 5 | 옛 컬럼 쓰기를 멈추고 배포한다 |
| 6 | 옛 컬럼을 지운다 |
번거롭지만 각 단계에서 두 버전이 함께 동작합니다. 이 원칙은 컬럼 삭제와 타입 변경에도 같이 적용됩니다.
흔한 실수: 배포 시간을 짧게 잡아 문제가 없다고 보는 것. 짧아도 그 사이 요청은 실패하고, 롤백 경로가 막히는 것은 시간과 무관합니다.
Q.대량 삭제를 어떻게 하나요?
목적에 따라 방법이 다릅니다. 전체인지 조건부인지, 운영 중인지가 갈림길입니다.
| 방법 | 조건 | 속도 | 되돌리기 |
|---|
| DELETE | 걸 수 있다 | 느리다. 행마다 로그 | 트랜잭션 안에서 가능 |
| TRUNCATE | 없다. 전체 | 매우 빠르다 | 어렵다 |
| 파티션 떼어내기 | 파티션 단위 | 가장 빠르다 | 파티션을 보관하면 가능 |
| 나눠서 DELETE | 걸 수 있다 | 느리지만 안전 | 가능 |
운영 중에는 네 번째를 씁니다. 1억 건을 한 문장으로 지우면 잠금이 길고 로그가 폭증하며 복제 지연이 커집니다. 1만 건씩 나눠 지우면서 사이에 쉬어야 합니다.
정기적으로 오래된 데이터를 지우는 요구라면 애초에 파티션으로 설계하는 것이 답입니다. 월별 파티션을 두면 지난달 데이터를 지우는 것이 파티션 하나를 떼는 일이 됩니다.
| 함께 볼 것 | 내용 |
|---|
| 공간 회수 | DELETE 는 공간을 즉시 돌려주지 않는다. 정리 작업이 필요하다 |
| 인덱스 | 삭제도 인덱스를 갱신하므로 인덱스가 많으면 느리다 |
| 외래 키 | 참조하는 테이블이 있으면 검사 비용이 붙는다 |
흔한 실수: 조건부 삭제에 TRUNCATE 를 쓰려 하는 것. 조건을 걸 수 없어 전체가 사라집니다.
먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.
읽었으면 문제로 확인해보세요
데이터베이스 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.