View
SQL 표준 표를 보면 REPEATABLE READ 칸에 phantom read "허용"이라고 명확히 적혀 있다. 학교에서도, 책에서도, 인터넷 블로그 99%가 그 표를 그대로 베껴 옮긴다. 그러나 정작 MySQL InnoDB에서 직접 돌려보면 phantom은 발생하지 않는다. 표가 거짓말을 하는 것인가 싶을 정도이다.
면접에서 "MySQL 기본 격리 수준이 무엇이며 phantom read를 막아주는가?"라는 질문이 나오면 답이 갈린다. 표를 외운 사람은 "RR이고 phantom은 막지 못한다"라고 답하고, MySQL을 실제로 다뤄본 사람은 "RR이고 phantom까지 막는다"라고 답한다. 둘 다 맞을 수 있는 미묘한 영역이지만 정확히 짚어주는 한국어 자료는 거의 없었다.
그래서 이번에 mysql 격리 수준 동작을 18셀(3 격리수준 × 2 읽기 변형 × 3 이상현상)의 매트릭스로 짜서 직접 돌려봤다. MariaDB를 띄워 두 트랜잭션을 동시에 굴리며 어디에서 막히고 어디에서 깨지는지 정량적으로 측정했다. SQL 표준 명세표와 InnoDB 실측이 어디에서 일치하고 어디에서 갈라지는지 차트로 함께 제시한다.
Phantom Read란?
같은 쿼리를 두 번 실행했을 때 첫 번째엔 없던 행이 두 번째에 보이는 현상이다.
예시)
트랜잭션 A (당신)
1) SELECT * FROM users WHERE age > 20;
결과: 3명 (이름: A, B, C)
2) [다른 트랜잭션이 age > 20인 사용자 D를 추가]
3) SELECT * FROM users WHERE age > 20;
결과: 4명 (이름: A, B, C, D) ← D가 새로 나타남!
SQL 표준이 정의한 격리 수준 4가지부터 복습한다
dirty read, non-repeatable read, phantom read, lost update란 각각 무엇인가
격리 수준은 "어떤 이상 현상을 막느냐"로 정의된다. 막아야 할 이상 현상은 네 종류이다.
dirty read는 다른 트랜잭션이 commit하지 않은 데이터를 읽어버리는 현상이다. 매우 위험하다. non-repeatable read는 같은 row를 동일 트랜잭션 안에서 두 번 읽었는데 값이 달라지는 현상이다. 그 사이에 다른 트랜잭션이 UPDATE 후 commit한 경우에 발생한다. phantom read는 같은 조건으로 SELECT를 두 번 했는데 row 개수가 달라지는 현상이다. 다른 트랜잭션이 매칭 row를 INSERT하고 commit하면 일어난다. 마지막으로 lost update는 두 트랜잭션이 같은 값을 동시에 읽고 각자 갱신해 commit하면 한쪽 갱신이 통째로 사라지는 현상이다. 잔액 +100을 두 번 했는데 +100만 반영되는 그 시나리오다.

출처: Medium (29KB)
SQL 표준 명세표 (lost update 포함 확장 버전)
ANSI SQL 1992 원문은 dirty read·non-repeatable read·phantom read 세 가지 read phenomena만 정의한다. lost update는 1995년 Berenson 외 4인의 "A Critique of ANSI SQL Isolation Levels" 논문에서 P4로 추가된 항목이다. 인터넷에 가장 많이 도는 4열짜리 매트릭스는 그 둘을 합친 확장판이다.
| 격리 수준 | dirty read | non-repeatable read | phantom read | lost update |
| READ UNCOMMITTED | 허용 | 허용 | 허용 | 허용 |
| READ COMMITTED | 차단 | 허용 | 허용 | 허용 |
| REPEATABLE READ | 차단 | 차단 | 허용 | 허용 |
| SERIALIZABLE | 차단 | 차단 | 차단 | 차단 |
여기서 RR 칸의 phantom read는 "허용"이다. 표만 보면 "RR에서 phantom이 일어난다"가 답인데, MySQL InnoDB에서 정말 그러한지가 이 글의 본론이다.
다만 MySQL InnoDB는 표대로 움직이지 않는다
InnoDB 기본 격리 수준은 RR이고 표준보다 한 단계 엄격하다
MySQL/MariaDB InnoDB의 기본 격리 수준은 REPEATABLE READ이다. PostgreSQL이 READ COMMITTED를 기본으로 두는 것과는 다르다. 그리고 InnoDB의 RR은 SQL 표준이 정의한 RR보다 한 단계 더 엄격하게 동작한다. phantom read도 차단하고, lost update 또한 일반 SELECT 기준으로는 차단해버린다.
MVCC 스냅샷과 gap lock이 함께 동작한다
InnoDB가 RR에서 phantom까지 막는 비결은 두 메커니즘이 함께 동작하기 때문이다.
첫 번째는 MVCC consistent read, 즉 스냅샷 읽기이다. 트랜잭션이 첫 SELECT를 던지는 순간 그 시점의 read view를 고정한다. 일반 SELECT는 이 스냅샷에서만 읽으므로 다른 트랜잭션이 INSERT/UPDATE/commit을 아무리 수행해도 보이지 않는다. 두 번째는 gap lock과 next-key lock이다. SELECT ... FOR UPDATE나 UPDATE처럼 잠금을 거는 읽기는 인덱스 range를 잠가버린다. 그 range 안으로 INSERT가 들어오려 하면 물리적으로 블로킹된다.
이 두 메커니즘이 같은 RR 안에서 따로 돌아가는 것이 사람들이 헷갈리는 부분이다. consistent read는 phantom을 "보이지 않게" 하고, gap lock은 phantom INSERT 자체를 "물리적으로 못 들어오게" 한다. 결과는 둘 다 phantom 차단이지만 메커니즘은 완전히 다르다.
직접 18셀 매트릭스를 돌려보았다
스키마와 두 트랜잭션 시나리오
테이블은 users(id, balance, age) 한 장이다. 초기 상태는 row 9건, 모두 age ≤ 30, 한 row의 balance = 1000이다.
각 셀마다 두 트랜잭션 Tx1·Tx2를 동시에 띄워 다음 세 시나리오를 굴렸다.
-- phantom_read 시나리오
-- Tx1
START TRANSACTION;
SELECT COUNT(*) FROM users WHERE age > 30; -- 첫 번째
-- (잠깐 대기)
SELECT COUNT(*) FROM users WHERE age > 30; -- 두 번째
COMMIT;
-- Tx2 (Tx1의 두 SELECT 사이에 끼어듦)
START TRANSACTION;
INSERT INTO users (id, balance, age) VALUES (10, 500, 35);
COMMIT;
-- non_repeatable_read 시나리오
-- Tx1
START TRANSACTION;
SELECT balance FROM users WHERE id = 1; -- 첫 번째
-- (잠깐 대기)
SELECT balance FROM users WHERE id = 1; -- 두 번째
COMMIT;
-- Tx2
START TRANSACTION;
UPDATE users SET balance = 9999 WHERE id = 1;
COMMIT;
-- lost_update 시나리오 (Tx1·Tx2가 동시에 +100)
-- Tx1과 Tx2 둘 다
START TRANSACTION;
SELECT balance FROM users WHERE id = 1;
-- (계산: balance + 100)
UPDATE users SET balance = ? WHERE id = 1;
COMMIT;
일반 SELECT vs SELECT FOR UPDATE 두 변형을 모두 측정한 이유
같은 격리 수준이라도 일반 SELECT(consistent snapshot read)와 SELECT ... FOR UPDATE(locking read)는 동작이 다르다. 갭 락이 켜지는 지점이 어디인지를 자연스럽게 드러내려면 두 변형을 한 번에 비교해야 한다. 표만 보고 "RR에서 phantom이 일어나지 않는다"라고 외우면 잠금 읽기 동작을 놓치게 된다.
환경 (MariaDB on python:3.12-slim, 68초 안에 종료)
| 항목 | 값 |
| 베이스 이미지 | python:3.12-slim |
| DB 엔진 | InnoDB (MariaDB) |
| innodb_lock_wait_timeout | 5초 |
| 메모리 한도 | 1024 MB |
| CPU 한도 | 2.0 vCPU |
| 스윕 규모 | 18 셀 (3 × 2 × 3) |
| 실측 소요 | 68,175 ms (약 68.2초) |
| 종료 코드 | 0 (PASS) |
단일 컨테이너에 MariaDB를 띄우고 PyMySQL로 두 세션을 동시 운용했다. MariaDB 기준이지만 vanilla InnoDB와 동일한 RR 동작이라고 보면 된다. Aurora나 TiDB 같은 호환 DB는 또 다를 수 있다. 이 부분은 본문 끝에서 한 줄 더 짚는다.
결과 매트릭스
18셀 한 장 차트
전체 18셀의 결과를 한 장으로 정리하면 다음과 같다. 1 = ALLOWED(현상 발생), 0 = PREVENTED(차단)이다.

| 격리 수준 | 변형 | phantom_read | non_repeatable_read | lost_update |
| READ COMMITTED | snapshot | 1 | 1 | 1 |
| READ COMMITTED | for_update | 1 | 0 | 0 |
| REPEATABLE READ | snapshot | 0 | 0 | 0 (`err_1020`) |
| REPEATABLE READ | for_update | 0 | 0 | 0 |
| SERIALIZABLE | snapshot | 0 | 0 | 0 (deadlock) |
| SERIALIZABLE | for_update | 0 | 0 | 0 (`err_1020`) |
RC + 일반 SELECT는 phantom·non-repeatable·lost 셋 모두 발생한다. 표준 표와 일치하는 결과이다. RR + 일반 SELECT는 셋 모두 발생하지 않는다. 이 지점이 표준 표와 갈리는 부분인데, 표는 phantom·lost를 허용으로 적어두었다. RR + FOR UPDATE 또한 셋 모두 발생하지 않는다. 이번에는 갭 락이 INSERT를 블로킹하여 차단된다. SERIALIZABLE은 어떤 변형이든 셋 모두 차단된다. 다만 동시성 비용이 가장 크다.
SQL 표준과 InnoDB 실측이 갈라지는 지점
SQL 표준 명세와 InnoDB 실측을 셀 단위로 비교하면 RR 줄에서 두 칸이 갈라진다.

| 격리 수준 | 이상 현상 | SQL 표준 명세 | InnoDB 실측 | 판정 |
| READ COMMITTED | phantom_read | allow | allow | MATCH |
| READ COMMITTED | non_repeatable_read | allow | allow | MATCH |
| READ COMMITTED | lost_update | allow | allow | MATCH |
| REPEATABLE READ | phantom_read | allow | block | DIVERGE |
| REPEATABLE READ | non_repeatable_read | block | block | MATCH |
| REPEATABLE READ | lost_update | allow | block | DIVERGE |
| SERIALIZABLE | phantom_read | block | block | MATCH |
| SERIALIZABLE | non_repeatable_read | block | block | MATCH |
| SERIALIZABLE | lost_update | block | block | MATCH |
stdout으로 찍은 비교 결과 또한 동일한 이야기이다.
=== Spec vs InnoDB (snapshot reads) ===
[MATCH ] READ COMMITTED phantom_read spec=allow innodb=allow
[MATCH ] READ COMMITTED non_repeatable_read spec=allow innodb=allow
[MATCH ] READ COMMITTED lost_update spec=allow innodb=allow
[DIVERGE] REPEATABLE READ phantom_read spec=allow innodb=block
[MATCH ] REPEATABLE READ non_repeatable_read spec=block innodb=block
[DIVERGE] REPEATABLE READ lost_update spec=allow innodb=block
[MATCH ] SERIALIZABLE phantom_read spec=block innodb=block
[MATCH ] SERIALIZABLE non_repeatable_read spec=block innodb=block
[MATCH ] SERIALIZABLE lost_update spec=block innodb=block
표준은 RR에서 phantom·lost를 허용한다고 적었으나 InnoDB는 둘 다 차단한다. 9개 셀 중 7개는 일치하고 RR에서 두 셀만 갈라지는 형태이다.
RC가 만드는 사고들
RC에서 일반 SELECT는 매 SELECT마다 새 read view를 잡는다. 따라서 다른 트랜잭션이 commit한 직후에는 그것이 곧바로 보인다. 결과적으로 phantom_read 시나리오에서는 Tx1의 첫 SELECT가 9, 두 번째 SELECT가 10으로 나온다. INSERT가 그대로 보이는 것이다. non_repeatable_read 시나리오에서는 Tx1이 1000을 보았다가 9999를 보게 된다. lost_update 시나리오에서는 최종 잔액이 1100으로 찍힌다. 기댓값은 1200인데 한쪽 +100이 통째로 사라진 것이다.
이는 SQL 표준이 정확히 예측한 그대로이다. RC + 일반 SELECT 조합을 실전 OLTP의 잔액·재고 관리 로직에 그대로 사용하면 망한다.
다만 RC에서도 SELECT FOR UPDATE로 바꾸면 non_repeatable·lost는 차단된다. 두 번째 SELECT 시점에 락이 잡혀 있어 Tx2 UPDATE가 1807ms 동안 블로킹되었다가 Tx1 commit 후 진행된다. 그러나 phantom은 여전히 발생한다. 갭 락이 RC에서는 켜지지 않으므로 INSERT는 그대로 들어온다.
RR에서 일반 SELECT는 phantom까지 차단한다 (표와 정반대)
RR + 일반 SELECT의 결과 셀을 보면 다음과 같다.
| 시나리오 | Tx1 첫 SELECT | Tx1 둘째 SELECT | 비고 |
| phantom_read | 9 | 9 | Tx2 INSERT는 commit되었으나 Tx1엔 보이지 않음 |
| non_repeatable_read | 1000 | 1000 | Tx2 UPDATE도 보이지 않음 |
| lost_update | 1100 / 1200 | — | 단 `err_1020`로 한쪽이 막혀 결과적으로 차단 |
phantom 사이즈가 9 → 9로 그대로 유지된 것이 묘미이다. Tx2가 분명 INSERT 후 commit까지 끝냈는데 Tx1의 두 번째 SELECT에서는 그것이 보이지 않는다. MVCC가 트랜잭션 첫 SELECT 시점의 스냅샷을 고정해두기 때문이다. SQL 표준 표는 이 칸을 "phantom 허용"으로 적어두었지만 InnoDB는 그렇게 동작하지 않는다.
lost_update 셀의 err_1020 에러는 InnoDB가 "row가 다른 트랜잭션에 의해 변경됨"을 감지하고 한 트랜잭션을 강제로 실패시킨 케이스이다. 결과적으로 lost update 자체는 발생하지 않았으며, 애플리케이션이 retry 로직을 깔아둔다면 잔액 1200을 정확히 맞출 수 있다.
RR에서 phantom이 차단되는 진짜 이유는 무엇인가
consistent read 경로
RR + 일반 SELECT가 phantom을 차단하는 메커니즘은 MVCC consistent read이다. 트랜잭션이 첫 SELECT를 던지는 순간 InnoDB가 read view, 즉 트랜잭션 ID 리스트를 만든다. 이후의 모든 SELECT는 그 시점에 commit된 row만 보고, 그 이후 commit된 row는 보지 않는다.
이 메커니즘은 Tx2의 INSERT를 막지 않는다. Tx2는 자기 갈 길을 가서 commit까지 한다. 단지 Tx1의 두 번째 SELECT는 자신의 read view에 없는 trx_id가 만든 row이므로 무시할 뿐이다. "안 보이게" 하는 것이 핵심이다. "못 들어오게" 하는 것이 아니다.
locking read 경로
RR + SELECT ... FOR UPDATE는 메커니즘이 완전히 다르다. 여기서는 gap lock과 next-key lock이 켜진다.
SELECT COUNT(*) WHERE age > 30 FOR UPDATE를 던지면 InnoDB는 age 인덱스에서 30보다 큰 값 range를 통째로 잠가버린다. Tx2가 INSERT (age=35)를 시도하면 그 range 안에 들어가므로 락 대기로 빠진다.

실측 대기 시간을 보면 RR + snapshot + phantom_read 셀에서 Tx2 wait는 1ms이다. Tx2가 자유롭게 INSERT/commit을 수행하고, 단지 Tx1엔 보이지 않을 뿐이다. 반면 RR + for_update + phantom_read 셀의 Tx2 wait는 1803ms이다. Tx2의 INSERT가 갭 락에 막혀 Tx1 commit 직전까지 기다린 것이다. 같은 RR 격리 수준이지만 변형에 따라 락 메커니즘 작동 여부가 정반대로 갈린다.
같은 RR 안에서 두 메커니즘이 동시에 작동한다
이 지점이 사람들이 자주 틀리는 부분이다. RR이라고 해서 phantom 차단 메커니즘이 한 가지로 통일된 것이 아니다. 어떤 SELECT를 사용하느냐에 따라 다른 메커니즘이 작동한다. 일반 SELECT는 MVCC consistent read 경로로 가서 "안 보이게" 차단하고, SELECT FOR UPDATE나 UPDATE/DELETE는 next-key lock으로 "못 들어오게" 차단한다.
따라서 면접에서 "RR에서 phantom을 어떻게 막는가?"라는 질문을 받으면 "MVCC로 막거나 갭 락으로 막으며, 둘 다 RR에서 작동한다" 이렇게 답해야 정확하다.
SERIALIZABLE은 어디에서 비싸지는가
모든 SELECT가 사실상 share lock으로 승격된다
SERIALIZABLE 격리 수준에서는 일반 SELECT조차도 share lock을 잡는다. Tx1이 SELECT하면 그 row(또는 range)에 LOCK IN SHARE MODE 락이 걸리고, Tx2가 UPDATE/DELETE/INSERT를 시도하면 막힌다.
실측에서도 SERIALIZABLE + snapshot + phantom_read 셀에서 Tx2 wait가 1808ms로 잡힌다. 일반 SELECT인데도 RR + FOR UPDATE와 거의 같은 대기 시간이 나온다. SERIALIZABLE은 사실상 모든 읽기를 잠금 읽기로 승격시키는 모드라고 보면 된다.
락 대기 시간 차트
전체 18셀의 Tx2 대기 시간을 막대 차트로 그리면 동시성 비용이 어디에서 폭발하는지 한눈에 보인다.

| 격리 수준 | 변형 | phantom_read | non_repeatable_read | lost_update |
| READ COMMITTED | snapshot | 2 | 2 | 251 |
| READ COMMITTED | for_update | 2 | 1,807 | 427 |
| REPEATABLE READ | snapshot | 1 | 2 | 251 |
| REPEATABLE READ | for_update | 1,803 | 1,807 | 430 |
| SERIALIZABLE | snapshot | 1,808 | 1,806 | 252 |
| SERIALIZABLE | for_update | 1,807 | 1,807 | 172 |
(단위: 밀리초)
일반 SELECT만 사용할 때 락 대기는 거의 0이다. RC와 RR snapshot phantom_read 셀이 1~2ms로 박혀 있다. 잠금이 켜지는 순간 1800ms대로 점프한다. Tx1이 sleep 후 commit하는 1.8초가 그대로 반영된 결과이다. SERIALIZABLE은 일반 SELECT인데도 1800ms 대기가 잡힌다. 이것이 "모든 읽기를 잠금으로 승격"의 비용이다.
SERIALIZABLE까지 가야 하는 경우는 거의 없다
위 차트가 답한다. RR + FOR UPDATE는 잠금 읽기 셀에서만 1800ms 대기 비용이 들고 일반 SELECT는 빠르게 통과한다. SERIALIZABLE은 모든 SELECT가 비용을 짊어진다.
한 트랜잭션 안에서 잠가야 할 row만 명시적으로 FOR UPDATE를 거는 RR이 SERIALIZABLE보다 거의 항상 더 낫다. SERIALIZABLE은 "모든 읽기가 잠금이어야 한다"가 진짜 요구사항인 회계·금융 일부 시나리오에서나 의미가 있다.
실전에서 격리 수준을 어떻게 선택해야 하는가
일반 OLTP 서비스는 RR 기본값을 그대로 사용한다
대부분의 웹 서비스는 InnoDB의 RR 기본값을 그대로 사용하면 된다. phantom·non-repeatable이 일반 SELECT에서는 차단된다는 점은 위 실험으로 검증되었으며, 명시적 잠금이 필요한 부분만 SELECT ... FOR UPDATE로 처리하면 된다. READ COMMITTED로 내릴 동기는 보통 락을 풀어주려는 것이지만, RR도 일반 SELECT는 락을 잡지 않으므로 굳이 내릴 이유가 없다.
잔액 증감 같은 lost-update 위험 코드는 RR + SELECT FOR UPDATE
잔액에 +100 더하기, 재고 -1 차감하기 같은 read-modify-write 패턴은 RR 기본값만으로는 부족할 수 있다. 위 실험에서 RR + snapshot lost_update 셀을 보면 err_1020이 떴는데, 이는 한쪽 트랜잭션이 retry 가능한 에러로 빠진 결과이다. 애플리케이션 retry 로직이 없다면 한쪽 갱신이 그대로 사라진다.
따라서 그러한 코드는 명시적으로 다음과 같이 작성하는 것이 안전하다.
START TRANSACTION;
SELECT balance FROM users WHERE id = 1 FOR UPDATE;
-- 계산
UPDATE users SET balance = ? WHERE id = 1;
COMMIT;
FOR UPDATE로 row를 잠가서 lost_update 자체를 발생하지 못하게 막는 것이다. 실측에서 RR + for_update + lost_update 셀은 최종 잔액이 정확히 1200 / 1200으로 나온다.
보고서·통계는 RC로 낮춰 락을 풀어주는 것이 맞다
긴 분석 쿼리, 즉 수십 분 도는 보고서 같은 경우는 RC로 낮추는 것이 나을 수 있다. RR 스냅샷이 트랜잭션 끝까지 유지되면 undo log가 쌓여 다른 트랜잭션이 느려질 수 있기 때문이다. 어차피 보고서는 약간의 non-repeatable이 큰 문제가 되지 않으므로 RC로 풀어주는 편이 시스템 전체에 이롭다.
SERIALIZABLE은 마지막 수단이다
위 대기 시간 차트가 보여주듯 SERIALIZABLE은 일반 SELECT까지 잠금을 걸어버린다. 이를 감수할 만한 시나리오는 거의 없다. 정합성이 절대적으로 중요한 회계 마감 작업, 금융 결제 정산처럼 동시성을 포기해도 되는 영역에서만 의미가 있다.
자주 틀리는 면접 질문
"MySQL 기본 격리 수준은 무엇인가"
REPEATABLE READ이다. PostgreSQL은 READ COMMITTED를 기본으로 둔다는 점에서 다르다. 헷갈리는 사람이 많다.
"RR에서 phantom read가 일어나는가"
SQL 표준의 답과 InnoDB의 답이 다르다. SQL 표준은 "허용"이고 InnoDB는 "차단"이다. 면접관이 어떤 답을 원하는지에 따라 갈리므로 둘 다 알고 있어야 한다. "표준 표대로라면 허용이지만 InnoDB는 MVCC + 갭 락으로 phantom까지 차단한다" 이렇게 답하는 것이 정확하다.
"갭 락이란 무엇인가"
인덱스 range를 잠그는 락이다. 특정 row가 아니라 row 사이의 빈 공간(gap)을 잠가 그 range로 INSERT가 들어오는 것을 차단한다. RR에서 phantom을 막는 핵심 메커니즘 중 하나이다. RC에서는 켜지지 않는다.
"MVCC consistent read와 locking read의 차이는 무엇인가"
consistent read는 일반 SELECT가 사용하는 경로로, 트랜잭션 시작 시점 스냅샷에서 읽는다. 락을 잡지 않는다. locking read는 SELECT FOR UPDATE나 LOCK IN SHARE MODE 같은 것으로 row(또는 range)에 락을 걸고 읽는다. 같은 RR 안에서 두 경로가 동시에 동작한다.
표를 베끼지 말고 직접 돌려보라
격리 수준을 공부할 때 표를 외우고 끝내면 면접에서 위 세 번째 질문에서 무너진다. 정확히 답하려면 SQL 표준과 InnoDB 구현이 어디에서 갈라지는지 알아야 하고, 그것을 가장 확실히 아는 방법은 직접 돌려보는 것이다.
가장 중요한 사실 하나만 가져간다면, InnoDB의 RR은 SQL 표준 RR보다 한 단계 엄격하여 phantom·lost_update까지 사실상 차단한다는 것이다. 표만 보면 잡히지 않는 차이이며, 한국어 자료가 잘 짚지 않는 부분이기도 하다. 거기 더해, 같은 RR 안에서도 일반 SELECT는 MVCC로 막고 FOR UPDATE는 갭 락으로 막는다는 점, 그리고 SERIALIZABLE은 동시성 비용이 너무 커서 RR + FOR UPDATE 조합이 거의 항상 더 낫다는 점까지 함께 기억해두면 된다.
마지막으로 한 줄 경고한다. 위 결과는 vanilla InnoDB(MySQL 8.0+, MariaDB 10+) 기준이다. Aurora는 RR 동작이 미묘하게 다를 수 있고, TiDB나 Spanner 같은 분산 DB는 또 다르다. 본인이 사용하는 DB가 진짜 InnoDB인지부터 확인하고, 의심스럽다면 위 18셀 매트릭스를 그대로 본인 환경에 돌려보면 된다. 표만 베끼는 자료보다 직접 돌린 결과가 항상 답이다.
'Database' 카테고리의 다른 글
| PgBouncer를 정말 무조건 써야 하는가 — 직접 연결과 session/transaction/statement 3가지 모드 직접 비교 (0) | 2026.05.04 |
|---|---|
| DB max_connections=100인데 pool 200을 잡으면 어떻게 되는가 — 응급처치 3가지 직접 비교 (0) | 2026.05.03 |
| DB Connection Pool은 클수록 좋다는 거짓말 — PostgreSQL 풀을 1부터 256까지 늘려본 결과 (0) | 2026.05.03 |
| Citus 샤딩이 깨졌을 때, 할 수 있는 방법과 사례들을 정리한다 (0) | 2026.04.30 |
| RocksDB란 무엇이며 왜 다들 사용하는가 (0) | 2026.04.30 |
