쿼리 개선하기
느린 쿼리 밝혀내기
- 슬로우 쿼리 로그
- SHOW FULL PROCESSLIST 등 다양한 방법으로 확인 가능
- I/O 튜닝 방법
- 쿼리를 실행시키지 않기
- I/O를 시키지 않기 (테이블 스캔 → 인덱스 검색)
- 필요한 레코드만 I/O 시키기 (테이블 스캔 → 인덱스 검색)
- 정렬을 시키지 않기
- 병렬 I/O를 시키기(주로 배치 처리)
많은 I/O가 필요한 경우 순차적인 I/O에 귀착시키기
일치하는 수백 개의 레코드를 개별적으로 랜덤 액세스해 나가는 것이 아니라 인덱스를 스캔하는 것만으로 대상 레코드를 검색한 후에 되돌려 주어야 할 레코드만을 랜덤 액세스하는 것이 효과적
정렬의 실행 효율에 주의하기
효율이 나쁜 인덱스를 선택하고 있는 경우
MySQL 옵티마이저는 비용 기반이므로 항상 같은 인덱스를 선택하지는 않지만, 상황에 따라서는 정렬을 건너뛸 수 있는 i2가 아닌 명시적인 정렬이 필요한 i1을 선택하는 일이 있음
- 인덱스의 내용을 올바로 사용하지 않은 경우 (버그)
- 두 경우 모두
FORCE INDEX
로 인덱스를 강제함으로써 올바르게 인덱스를 사용하게 할 수 있음
실행 빈도가 많은 쿼리 밝혀내기
- 이런 종류의 문제를 분석하는 데 가장 쉽고 효과적이라고 말할 수 있는 것
- mk-query-digest : 슬로우 쿼리로그와 tcpdump 출력 결과 등을 분석하여 각 쿼리 유형마다 총 실행 시간을 계산하여 쿼리가 많은 순으로 나열하는 도구 (
Maatkit
라는 유틸리티 도구에 포함되어 있음. MySQL 커맨드라인 기반 유틸리티 도구들에서 가장 뛰어난 것 중 하나)
tcp-dump : 패킷 캡쳐를 하는 범용 도구
고부하 환경에서 사용하면 몇 초만에 100MB 수준으로 파일 사이즈가 되기에 클라이언트 IP 주소 등 다양한 조건으로 필터링하여 전체 통계 경향에 영향을 주지 않는 범위에서 캡쳐 크기를 작게 하는 것이 유효함
느린 트랜잭션 개선하기
- 어떤 트랜잭션이 레코드를 30초 동안 잠그고 있을 경우, 동일한 레코드를 잠그려고 하는 다른 요청은 최대 30초 동안은 기다려야 함
- 이러한 문제를 해결하기 위해서는 잠금을 유지하고 있던 트랜잭션이 어떤 처리를 하고 있었는지를 파악하는 것이 중요한 과정임
- 필자가 개발한 MySlowTranCapture와 같은 툴을 이용해 느린 트랜잭션 파악이 가능함
경쟁에서의 배려
- 잠금 경쟁의 과제
- 웹 서비스에서는 사용자 수가 많은 반면 데이터베이스 서버의 수는 한정되어 있기 때문에 동일 테이블에 대해서 다수의 클라이언트로부터 일제히 액세스가 발생하는 경향이 있음 → 잠금 경합 문제되기 쉬움
타임 아웃 설정
- 2~5초 정도의 작은 값으로 설정
- InnoDB는 innodb_lock_wait_timeout 동안만 잠금 대기함
잠금을 장시간 걸지 않기
- 잠금을 단축하는 데 중요한 것은 어떤 작업이 장시간 잠금을 걸 가능성이 있는지를 제대로 이해하는 것
- 현실적으로 문제가 되는 것은 잠금 상태에서 외부 네트워크에 액세스 하는 것 (잠금 걸린 상태로 외부 네트워크로의 액세스가 느려지게 되면 클라이언트는 더더욱 기다리게 됨)
- 데이터베이스 서버가 복수로 분산되어 있는 경우에는 잠금을 건 상태에서 외부 네트워크에 액세스하는 것을 거의 피할 수 없다. 그런 상황에서도 잠금 기간을 단축하려면 잠금을 확보하기 전에 필요한 MySQL 서버의 접속을 마치고, 그리고서 필요한 처리를 하면 효과적임
서로 다른 서버 간의 교착 상태에 주의하기
- 애플리케이션 측에서 봤을 때 부자연스러운 정도로 innodb_lock_wait_timeout 오류가 대량으로 나와 있는 경우는 이러한 복수 서버 간의 교착 상태 가능성이 충분히 있을 수 있음
- 원칙적으로 교착 상태를 방지하는 데 중요한 것은
잠금 순서를 통일하는 것
- 사용자 A와 사용자 B 의 레코드를 업데이트 해야 하는 상황에서 어떤 때는 사용자 A→ B, 어떤 때는 B→A 순서로 갱신한다면 잠재적으로 교착 상태의 가능성이 있음
- 동일 서버 내에 있다면 InnoDB 기능에 의해 보다 빨리 교착 상태가 감지되지만 Shard화 되어 있어 다른 서버 간에서 이것이 발생하면 자동 검출할 수 없음
- 이러한 문제를 방지하기 위해 사용자 ID의 오름차순으로 잠금을 걸 수 있도록 통일하는 것이 효과적