DB관련 알아두면 좋을 몇 가지 주의사항
쿼리 타임아웃
- 실제 서비스에서 응답시간이 길어지면 처리량만 감소할까? 아니다
- 동접이 증가하면서 특정 쿼리 실행시간이 15초 이상이 되었다 가정
- 사용자는 몇 초만 지나도 서비스가 느리다 느끼고 다시 몇 초 후에 재시도를 하게 된다
- 응답 지연으로 인한 재시도는 서버 부하를 더욱 가중
- 아직 앞선 요청 처리중인 상황에 추가적인 요청이 유입되기 때문
- 이런 재시도가 누적되면 요청 수가 기하급수적으로 늘어난다
- 이런 상황 방지하는 방법중 하나는 쿼리 실행 시간을 제한(타임아웃)하는 것
- 5초로 제한했다 하자, 트래픽이 증가해 쿼리 실행 시간이 5초를 넘기면 제한시간 초과로 에러가 발생
- 사용자는 에러 화면을 보게 되지만 서버 입장에서는 해당 요청을 정상적으로 처리한셈
- 타임아웃은 서비스와 기능의 특성에 따라 다르게 설정해야한다
- 블로그 글 조회는 몇 초 이내로 하면되겠지만 상품 결제 기능은 보다 긴 타임아웃이 필요
- 결제 처리 중 타임아웃으로 에러가 발생하면 후속 처리와 데이터 정합성이 복잡해 질 수 있기 때문
상태 변경 기능은 복제 DB 조회하지 않기
- 주 - 복제 디비 구조 쓸때 변경은 주 DB를 사용하고 조회는 복제 DB를 사용한다
- 그런데 이를 잘 못 이해해 모든 SELECT를 무조건 복제 DB 에서 실행하는 경우 있다
- 이는 2가지 측면에서 문제를 일으킬 수 있다.
첫째, 주 DB와 복제 DB는 순작적으로 데이터가 불일치할수있다. 주 DB 변경 후 복제 DB에 반영된다.
- 네트워크를 통해 복제 DB에 전달
- 복제 DB는 자체 데이터에 변경 내용을 반영
- 이 과정이 시간이 꽤 걸린다
- 데이터 복제에는 지연이 발생한다
- 아직 복제 DB에 변경이 반영되기전에 SELECT 가 발생할 수 있다
- 이 경우 잘못된 데이터를 조회하게 되어 사용자 요청을 제대로 처리할 수 없게 된다
둘째, 트랜잭션 문제가 발생할 수 있다.
- 주 DB와 복제 DB 간 데이터 복제는 트랜잭션 커밋 시점에 이뤄진다
- 주 DB의 트랜잭션 범위 내에서 데이터를 변경하고, 복제 DB에서 변경 대상이 될 수 있는 데이터를 조회하면 데이터 불일치로 문제가 생길 수 있다
- C U D 하고 이 데이터를 반영한걸 안전히 보려면 주 DB를 조회하자
배치 쿼리 실행 시간 증가
- 배치 프로그램은 데이터를 일괄 조회하거나 집게하거나 생성하는 작업 수행
- 예를 들어 일별 회원 통계, 사용자 사용 내역 기준으로 요금 계산
- 문제는 데이터가 쌓이고 집계 쿼리를 쓰면 많은 양의 메모리 쓰고 특정 임계점 넘기면 실행시간이 예측할 수 없을 만큼 길어질 수 있다는 것
- 이런 문제 예방하려면 배치에서 사용하는 쿼리의 실행 시간을 지속적으로 추적해야한다
- 추적을 통해 쿼리 실행 시간이 갑자기 큰 폭으로 증가했는지 감지할 수 있고, 문제가 되는 쿼리 발견시 원인 찾아 해결 가능
- 해결책은 장비 사용 높이기가있지만 항상 만능은 아니기에(돈) 다른 방법도 고려 ㄱㄱ
- 커버링 인덱스 활용
- 집계 쿼리는 특성상 많은 데이터를 스캔한다
- 이때 집계 대상 칼럼이 인덱싱 되있다면 데이터를 직접 읽지 않고도 인덱스만 스캔해 집계를 수행할 수 있다
- 커버링 인덱스를 쓰면 처리속도는 빨라지고 DB가 쓰는 메모리도 준다
- 데이터를 일정 크기로 나눠 처리
- 예를 들어 접속 로그로 한 달간 다양한의 데이터 추출해야한다고 가정
SELECT ... 각종 집계
FROM accessLog al
WHERE al.accessDatetime >= ~9월
AND al. < 8월
GROUP BY ...
- 이걸 하루로 나눠 말일 까지 실행하고 각 결과를 다시 합치면 같은결과가 나옴
- 다른 방법으로는, 데이터를 나눠 처리하면 짧은 간격으로 집계쿼리를 실행할 수 도 있다
- 예를 들어 새벽에 배치 처리 안하고 다음 처럼 10분 간격으로 집계 작업실행할 수 도 있다
- 통계 테이블에 반영된 마지막 accessDatetime 시간을 구한다
- [마지막 accessDatetime, 마지막 accessDatetime + 10분]에 속하는 accessLog 데이터를 집계한다.
- 2에서구한 집계 데이터를 통계 테이블에 반영한다.
- 이렇게 하면 쿼리 실행시간 단축도 하고 필요 집계 데이터를 안정적으로 생산 가능함.
타입이 다른 컬럼 조인 주의
- 조인시 비교하는 칼럼의 타입이 달라서 인덱스를 활용하지 못하는 문제를 해결하려면 두 칼럼의 타입을 맞춰 비교해야함
- 다은은 MySQL에서 타입을 변환해 두 칼럼의 타입을 일치시킨 후 비교하는 예시
SELECT u.userId, u.name, p.*
FROM user u, push p
WHERE u.userId = 145
AND CAST(u.userId as char set utf8mb4) collate 'utf8mb4_unicode_ci' = p.receiverId
AND p.receiverType = 'MEMBER'
ORDER BY p.id DESC
LIMIT 100;- 이렇게 비교 대상 칼럼 타입 맞추면 쿼리 실행중 발생하는 불필요한 타입 변환을 줄일 수 있다.
테이블 변경은 신중하게
- 데이터가 많은 테이블에 새로운 칼럼추가나 기존 열거 타입 칼럼을 변경할 때는 매우 주의해야한다
- 정말로 주의해야한다
- 그 이유는 DB의 테이블 변경 방식 때문이다
- 예를 들어 MySQL은 테이블 변경마다 새 테이블 생성하고 원본 테이블의 데이터를 복사한 뒤, 복사가 완료되면 새 테이블로 대체함
- 이 복사 과정에서는 UPDATA, INSERT, DELETE 같은 DML 작업을 허용하지 않기에 복사 시간만큼 서비스가 멈춘다
- DML 허용하면서 테이블 변경하는 기능도 있지만 항상 가능한 것은 아님
- 그래서 이런 작업은 점검 시간을 따로 잡고 하는 경우가 많다
DB 최대 연결 개수
- 다음 상황 가정 ㄱ
- API 서버는 세 대
- 트래픽이 증가하고 있어 수평 확장이 필요
- DB서버의 CPU 사용률은 20% 수준으로 여유있다
- 트래픽 증가를 감당하기 위해 API 서버를 추가할 수 있다
- 그런데 추가한 API 서버에서 DB 커넥션 생성에 실패한다면 뭐가 문제인가?
- DB 서버 자원에는 여유가 있지만 API 서버에서 DB에 연결되지 않는다면 DB에 설정된 최대 연결 개수를 확인해야한다
- 예를들어 DB 최대 연결 개수가 100개라면 API 서버의 커넥션 풀 개수가 30개일때 API 서버를 네 대로 늘리면 필요한 커넥션 수는 120개
- 이것때문에 실패한것
- DB의 최대 연결 개수를 120개로 늘리면 해결될것임
- 단 주의해야하는게 무작정 연결 개수 늘리면안됨
- CPU 사용률 70% 정도 찍으면 스탑
- 이런경우는 캐시 서버 구상이나 쿼리 튜닝같은 조치로 DB 부하줄이고 해야됨