SPEC
SQL 표준 또는 PostgreSQL 문서가 정의하는 계약
00SQL 다음의 데이터베이스
PROJECT POLICYrelation에서 page까지, query plan에서 crash recovery까지. marketplace 한 시스템을 망가뜨리고 복구하며 PostgreSQL의 실제 경계를 배운다.
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE inventory
SET reserved = reserved + 1
WHERE product_id = 4207
AND stock - reserved > 0;
-- heap tuple: xmin 842 → xmax 847
-- WAL: 0/3A7F2B8 · buffer: dirty
COMMIT;
-- ERROR 40001? retry the whole txSQL 표준 또는 PostgreSQL 문서가 정의하는 계약
pin된 PostgreSQL에서 fixture로 관찰할 동작
조건과 trade-off가 있는 운영 권고
이 lab이 재현성과 안전을 위해 강제하는 선택
CAPSTONE STAGE · Capstone 0 · 주문·상품·결제의 경계와 키 정의
관계는 같은 속성 집합을 가진 튜플의 집합이며 후보 키와 함수 종속성이 의미를 제한한다. SQL table은 중복과 NULL을 허용할 수 있으므로 수학적 관계와 완전히 같지 않다. primary key는 선택된 후보 키이고 foreign key는 참조 무결성을 표현한다.
집합, predicate, 논리곱과 SQL의 기본 DDL/DML을 알고 시작한다. marketplace에서 주문번호, 판매자 SKU, 결제사 거래 ID 중 무엇이 후보 키인지 말로 먼저 적는다.
0–3분에는 엔터티와 불변식을 적고, 3–7분에는 키와 종속성을 찾고, 7–11분에는 3NF schema를 만들고, 11–15분에는 위반 INSERT와 NULL predicate를 실행한다.
PostgreSQL은 UNIQUE/PRIMARY KEY를 unique B-tree로 뒷받침하고, foreign key를 trigger로 검사하며, CHECK는 행이 삽입·변경될 때 평가한다. CHECK 결과가 TRUE 또는 UNKNOWN이면 통과하므로 NULL 허용 여부는 NOT NULL로 별도 표현해야 한다.
결제의 자연 후보 키는 provider와 provider_ref의 조합이다. 금액은 통화 최소 단위 정수로 저장하고 양수 불변식을 DB에 둔다.
CREATE TABLE payment (
payment_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
order_id bigint NOT NULL REFERENCES orders(order_id),
provider text NOT NULL,
provider_ref text NOT NULL,
amount_minor bigint NOT NULL CHECK (amount_minor > 0),
currency char(3) NOT NULL,
UNIQUE (provider, provider_ref)
);반복 그룹을 order_item으로 분리하고 product 사실과 주문 당시 가격 snapshot을 구분한다. 정규화는 갱신 이상을 줄인다. 보고 성능을 위한 비정규화는 원본과 재생성 절차, 일관성 지연을 함께 문서화할 때만 도입한다.
PRIMARY/UNIQUE/FK는 의미 제약이며 동시에 일부 접근 경로를 만든다. PostgreSQL은 foreign key의 참조하는 열에 index를 자동 생성하지 않는다. 조인 plan이 느리다면 의미 모델과 물리 index를 별도로 검토한다.
EXPLAIN SELECT o.order_id
FROM orders o JOIN payment p USING (order_id)
WHERE p.provider = 'stripe' AND p.provider_ref = 'pi_demo';INSERT는 heap tuple을 만들고 관련 index와 constraint를 갱신한다. 같은 transaction 안의 FK 검사는 기본적으로 statement 끝에 완료되며 DEFERRABLE로 선언한 제약은 transaction 끝까지 미룰 수 있다. 실패하면 transaction 전체가 abort 상태가 된다.
CHECK를 application validation으로만 옮긴 분기를 만들고 두 세션이 음수 결제와 없는 주문 참조를 쓰게 한다. `discount = NULL`과 `discount IS NULL` 결과도 비교한다. 실패가 재현되지 않으면 이 실습은 통과가 아니다.
정상 주문 graph가 commit되는지, 중복 provider_ref·음수 금액·orphan item이 SQLSTATE와 함께 거부되는지, ON DELETE 정책이 의도대로 동작하는지 transaction test로 확인한다.
키 폭은 모든 참조 index와 join 비용에 전파된다. surrogate key를 쓰더라도 자연 후보 키의 UNIQUE를 버리지 않는다. 비정규화의 읽기 절감과 write fan-out, 재구축 시간, 추가 저장량을 함께 측정한다.
tenant_id를 데이터 불변식과 권한 경계 양쪽에 포함하고 composite FK로 교차 tenant 참조를 막는다. 숨겨야 할 값에 NULL을 쓰는 것은 접근 제어가 아니다.
constraint 이름과 SQLSTATE를 관찰 가능하게 남기고 위반률을 배포 회귀 신호로 본다. NOT VALID 제약은 검증 상태를 운영 항목으로 추적한다.
산출물은 ERD, 불변식 표, 후보 키·함수 종속성 목록, 실행 가능한 schema와 위반 fixture다.
각 불변식의 DB 표현 또는 의도적 application-only 사유가 있고, 모든 constraint 실패 test가 통과하며, NULL의 UNKNOWN 사례를 설명할 때 완료다.
답을 보기 전 설명하라: 후보 키와 primary key는 왜 다르며, `CHECK (amount > 0)`만으로 NULL을 막을 수 없는 이유는 무엇인가?
16 / CHECK
정답다른 writer, race, 운영 SQL이 우회할 수 있다. DB constraint는 모든 writer의 commit 경계에 불변식을 둔다.
정답선택되지 않는다. WHERE는 TRUE인 행만 남긴다.
02 / EXECUTE
PROJECT POLICY읽기만 하는 문서가 아니다. 동일한 marketplace를 migration하고, 부하를 만들고, 충돌시키고, plan과 복구 결과를 증거로 남긴다.
Docker가 기본 기준선이며 macOS와 Windows는 같은 SQL fixture를 호출한다.
PostgreSQL 18.4 primary + replica → migration → seed
(cd lab && ./scripts/lab.sh init)schema·constraint·RLS·reporting SQL test
(cd lab && ./scripts/lab.sh test)대표 plan과 estimate 오차 기록
(cd lab && ./scripts/lab.sh explain)deadlock·lost update·write skew·long transaction 재현
(cd lab && ./scripts/lab.sh transactions)깨진 archive 거부 후 격리 복구·fingerprint 검증
(cd lab && ./scripts/lab.sh backup-restore)workload·monitoring·모든 비파괴 fault 통합 실행
(cd lab && LAB_WORKLOAD_SECONDS=5 ./scripts/lab.sh all)Windows PowerShell 동등 실행 경로
Push-Location lab; .\scripts\lab.ps1 all; Pop-Location03 / BREAK
PROJECT POLICY각 장애는 격리된 fixture다. disk-full과 role 우회는 안전 장치와 명시적 opt-in 없이는 실행되지 않는다.
fixturelab/fixtures/plans/cardinality_before.sql
fixturelab/fixtures/plans/missing_index_before.sql
fixturelab/fixtures/plans/excess_indexes.sql
fixturelab/scripts/container/transactions.sh
fixturelab/scripts/container/transactions.sh
fixturelab/scripts/container/transactions.sh
fixturelab/scripts/container/transactions.sh
fixturelab/harness/pool-exhaustion.mjs
fixturelab/scripts/container/replica-stale-read.sh
fixturelab/scripts/container/replica-stale-read.sh
fixturelab/scripts/container/migration-lock.sh
fixturelab/scripts/container/disk-full.sh
fixturelab/scripts/container/backup-restore.sh
fixturelab/fixtures/security/rls_bypass.sql
04 / PROVE
PROJECT POLICY통과한 명령과 실제 학습 효과를 같은 말로 부르지 않는다.
build, migration, seed, SQL test, anomaly, EXPLAIN, restore 명령이 종료 코드를 남긴다.
12×16 섹션·14개 장애·공식 출처·KO/EN은 정적 검사했다. 실제 브라우저·screen reader·Windows UX는 별도 검증 전까지 unverified다.
실제 workload 성능과 학습자가 더 빨리 진단하는지는 별도 사용자 검증 전까지 unverified다.
05 / SOURCES
SPEC설명보다 원문을 우선한다. 링크는 PostgreSQL 18 공식 문서와 표준·원 논문으로 제한한다.