게임 렉 백서 › L12 데이터베이스
핫 로우 잠금 경합 Hot row lock contention
원인 ID db-hot-row · 주 담당 게임개발팀·서버 개발 · 함께 인프라팀·DB 인프라
그림과 실험이 있는 원본 카드로 열기 →
모두가 같은 행(길드 창고, 경매장 인기 아이템, 서버 전체 카운터)을 고치려 하면 한 명씩만 잠금을 얻습니다.
왜 이벤트·인기 아이템으로 같은 행에 수정이 몰림 → 그러면 잠금을 얻을 때까지 요청이 대기 → 화면에서는 거래 실패, “잠시 후 다시 시도”, 타임아웃
- 증상
- 씹힘·롤백, 입력 지연
- 요인
- 정체, 지연
- 누가 겪나
- 특정 기능만
- 언제
- 사람이 몰릴 때
- 담당
- 주 담당 게임개발팀·서버 개발 · 함께 인프라팀·DB 인프라
- 게임개발팀 할 일
- 행을 나누기(샤딩된 카운터), 트랜잭션 짧게, 메모리에서 모아 한 번에 반영.
- 인프라팀 할 일
- 행 잠금 대기 시간·건수를 모니터링해 경합이 몰리는 행을 찾아 공유.
- 수치 감각
- 한 요청이 잠금을 10ms 잡고 있으면 그 행은 1초에 최대 100번만 고칠 수 있습니다. 트랜잭션 안에 다른 서버 왕복이 끼면 그만큼 더 줄어듭니다.
- 그래프에서는
- 인원·부하를 따라 오름 · 행 잠금 대기 수·시간
- 확인할 곳
- MySQL은 Innodb_row_lock_waits·Innodb_row_lock_time 증가량과 Innodb_row_lock_current_waits를 보고, sys.innodb_lock_waits로 누가 누구를 기다리는지 찾음. PostgreSQL은 pg_stat_activity에서 wait_event_type이 Lock인 세션, pg_locks에서 granted가 false인 요청을 보고 log_lock_waits(기본 꺼짐)를 켜면 오래 기다린 잠금이 로그에 남음
- 이러면 맞음
- 이벤트·인원을 따라 잠금 대기가 가파르게 늘고 기다리는 요청 대부분이 같은 테이블의 같은 행(같은 키)을 가리킴
- 이러면 아님
- 대기가 여러 테이블·행에 고르게 흩어지면 디스크·CPU 포화 쪽. 한 세션이 잠금을 오래 잡고 놓지 않으면 오래 열린 트랜잭션(db-long-tx)
- 확인 수단
- 인프라 도구로 확인(게임 코드 불필요)
출처
- InnoDB Locking MySQL
한 트랜잭션이 행(인덱스 레코드)에 잠금을 걸면 다른 트랜잭션은 그 행을 고치지 못하고 기다림 - How to Minimize and Handle Deadlocks MySQL
트랜잭션을 작고 짧게 유지하고 관련 변경 직후 바로 커밋해 충돌을 줄이라는 권고 - Server Status Variables MySQL
Innodb_row_lock_waits·Innodb_row_lock_time으로 행 잠금 대기 횟수와 시간, Innodb_row_lock_current_waits로 지금 기다리는 수를 확인 - The innodb_lock_waits and x$innodb_lock_waits Views MySQL
기다리는 쿼리(waiting_query)와 막고 있는 세션(blocking_pid), 대기 시간(wait_age) - The Cumulative Statistics System (PostgreSQL Documentation) PostgreSQL
pg_stat_activity의 wait_event_type: Lock이면 무거운 잠금을 기다리는 중 - pg_locks (PostgreSQL Documentation) PostgreSQL
granted가 false면 그 프로세스가 잠금을 얻으려고 기다리는 중 - Error Reporting and Logging (PostgreSQL Documentation) PostgreSQL
log_lock_waits: deadlock_timeout보다 오래 잠금을 기다리면 로그를 남김, 기본 꺼짐
함께 보면 좋은 원인
같은 층: L12 데이터베이스
같은 증상(씹힘·롤백)의 다른 층 원인
그림과 실험이 있는 원본 카드 보기