1. 컬럼 타입 가이드2. 테이블 종류 및 사용법3. 인덱스 생성 가이드 (개발 관점)4. JOIN 종류 및 장단점5. Slow Query 방지를 위한 가이드6. 플랜 확인법7. DB 모니터링 하는 법8. DB에 자주 지연을 발생시키는 요소들나. 지연을 일으키는 요소9. DB 지연 발생 시 긴급 조치
1. 컬럼 타입 가이드
자주 쓰는 컬럼 타입 및 추천 데이터 타입
데이터 | 추천 데이터 타입 | 사유 |
• 일반 String 형태의 데이터
-> 영문 데이터 | [데이터 길이가 가변일 경우]
Oracle
-> Varchar2
Mysql, Postgresql, Mssql
-> varchar
[데이터 길이가 고정일 경우]
Oracle, Mysql, Postgresql, Mssql
-> char | • 오라클의 경우 varchar가 아닌 varchar2이며, 추후 varchar는 다른 형태의 데이터 타입으로 쓰일 예정
• varchar의 경우 실제 데이터가 차지하는 공간 외에 빈 공간을 압축해주기에 공간 낭비 감소
• 단 고정 길이의 경우 필요한 만큼을 가진 char와 다르게 사용하는 공간 외엔 압축하는 varchar의 경우 공간 낭비 존재 |
• 일반 String 형태의 데이터
-> 한글 데이터 | [데이터 길이가 가변일 경우]
Oracle, Mssql
-> nvarchar
Mysql, Postgresql
-> varchar
[데이터 길이가 고정일 경우]
Oracle, Mssql
-> nchar
Mysql, Postgresql
-> char | • Oracle 과 Mssql의 경우 유니코드 및 한글의 경우 nvarchar, nchar라는 별도의 데이터 타입 존재하며 해당 타입을 쓰는 것이 유리 |
• 메모, 비고, url 같은 데이터 사이즈가 큰 문자열 데이터
-> 인덱스 가 필요지 않고 where 조건에서 존재하지 않는 데이터 | Oracle, Mssql,Mysql, Postgresql
-> TEXT | • 데이터 처리 시 Disk 영역에서 처리 되기에 검색 조건에서 사용하지 않을 경우 사용
• 단순 보관 및 select 절에 오는 긴 데이터에 대하여 해당 컬럼 사용 |
날짜 타입 | [데이터 변환이 필요 없으며 날짜 연산이 없을 경우, 년월일만 쓸경우]
Oracle, Mssql,Mysql, Postgresql
-> Char, Varchar
[시분초 까지 존재해야하며, 날짜에 대한 연산이 많을 경우]
Oracle,Mysql, Postgresql
-> date(년월일), timestamp(년월일시분초)
Mssql
-> date, datetime, smalldatetime | • 날짜의 경우 스트링으로 들어온 날짜에 대해 비교 연산을 할 경우 varchar 선호
• 날짜에 특정 날짜를 더하거나, 차이를 구하는 등의 연산이 자주 일어날 경우 datetime 선호
• 실제 스토리지 사용량의 경우 datetime 류가 약 3~8 Bytes로 적게 사용 |
숫자 | [정수형]
Mssql,Mysql, Postgresql
-> INT
Oracle
-> Number
[실수형]
Mssql,Mysql, Postgresql
-> DECIMAL/Number
Oracle
-> Number
| • 오라클의 경우 Number 타입에 소숫점 자리를 컨트롤 하여 INT 처럼 사용 하는 것을 선호 |
대용량 데이터 | Orcle
-> clob
mysql
-> longtext
postgresql, mssql
-> TEXT | • 데이터가 약 4GB까지의 경우 Lob 타입을 사용하면 용이
• Lob 타입의 경우 별도의 스토리지를 사용하며, 해당 lob 타입 데이터를 엑세스 시 많이 느림
• |
주의사항
- Oracle의 Char 타입의 경우 빈공간이 NULL로 채워지나, Postgresql의 경우 빈공간이 빈값으로 채워짐
2. 테이블 종류 및 사용법
- 일반 테이블
- 통상적으로 사용하는 일반 테이블
- Temp 테이블(Temporary 테이블)
- 임시 테이블로 평상시에는 빈 테이블로 존재
- 세션이 들어와서 해당 테이블에 DML을 통해 데이터를 넣으면 테이블 내 데이터를 들고 있다가 세션 종료 시 자동 Truncate
- 동일한 Temp 테이블을 바라보더라도 세션이 다를 경우 독립적으로 존재 및 독립적으로 DML 수행
- in 조건에 다량의 변수가 들어오거나. ETL 과정에서 중간에 집계 및 거처가는 테이블 들로 주로 사용
- 인덱스도 생성 가능하나, 가급적 필요한 데이터만 인입 시키고 인덱스는 미생성 (PK제외) 권장

- 파티션 테이블
- 하나의 큰 테이블을 쪼갠 여러 자식 테이블로 분리하는 테이블 구조
- 큰 테이블을 특정 기준으로 잘라내어 작은 테이블들로 자르게 되면 데이터 조회시 파티션 키 라는
- 데이터 사이즈가 크고 자를수 있는 기준이 더 있을 경우 파티션의 파티션, 즉 이중 파티션 구조로도 생성 가능
- 장점으로는 다른 테이블 스페이스를 배정하여 IDC 환경에서 동일 디스크를 자주 접속 하는 것을 방지
- 큰 테이블에 대하여 물리적으론 1개 테이블이나 논리적으로 쪼개어져 있기에 데이터 조회 시 필요한 작은 테이블만 access 하여 성능 개선(12개의 파티션으로 나누어져 있을 경우 실제 데이터의 1/12만 조회)
- 하나의 파티션 테이블을 Truncate 하여도 다른 파티션 테이블에 영향이 없기에 데이터 파기에 용이
- 대량의 데이터 혹은 주기적으로 파기 해야하는 테이블에 대하여 주로 사용하며, 날짜나 값의 범위를 파티션 키로 쓰는
- 파티션 테이블을 만들 때 인덱스는 local 타입으로 만들어야 파티션 인덱스로 생성되며, 권한의 경우 각 파티션 테이블 별로 권한을 부여하여야 하는 DBMS 존재
- 무분별한 파티션 테이블은 오히려 성능을 악화시키기에 파티션 테이블 내 데이터가 약 50만건 이상일 경우 생성을 권장 드리며, 파티션키를 통해 나눠지는 기준도 많지 않은 좋은 key 값 선정이 핵심

테이블을 자른 기준 컬럼을 명시해줄 경우 전체 큰 테이블이 아닌 해당하는 파티션 테이블만 조회
Range 파티션을 주로 사용한다.
- * 좋은 파티션 키란?
- > null 값이 유입되지 않고, 데이터가 바뀌지 않는 불변성을 만족해야함
- > 해당 키를 기준으로 각 파티션 데이터가 고르게 분포 되어야 함 (데이터 쏠림 현상 발생 시 파티션 테이블 사용 무의미)
- > 가급적 모든 Sql문에선 해당 파티션 키를 조건에 사용하여야 한다.
3. 인덱스 생성 가이드 (개발 관점)
- 인덱스의 종류
- Unique index : 인덱스의 값은 해당 테이블에서 유일한 값을 가지는 인덱스
- non unique index : 인덱스의 값이 해당 테이블에서 유일하지 않아도 되는 인덱스
- partition index : 파티션 테이블에서 생성 가능한 인덱스
- Local partition index : 각 파티션 별로 파티션 키를 기준으로 하여 각각의 인덱스가 생성되는 지역 인덱스
- Global partition index : 파티션 테이블의 부모 테이블에 생성하는 인덱스로, 파티션 키가 불필요하고 전체 테이블 스캔이 필요할 경우에 사용하는 인덱스, 전역 인덱스
- single index : 인덱스를 구성하는 컬럼이 1개만 명시되는 인덱스
- Multi index : 인덱스를 구성하는 컬럼이 여러개인 인덱스
- Function based index : 인덱스를 생성할 때 각 컬럼별로 함수 혹은 날짜 변환 등을 사용한 결과 값을 인덱스로 생성
- Partial index : 인덱스를 구성할때 공통적인 where 조건을 인덱스 생성 시 명시하여 필요한 부분만 가지고 있는 인덱스

- 인덱스 생성 시 주의사항
- 구성하는 인덱스의 크기가 전체 데이터의 15% 이상이 넘지 않아야 한다.
- 인덱스 생성 시 순서가 매우 중요하며 앞에 순서에 있는 컬럼이 조건에 없을 경우 해당 인덱스를 옵티마이저는 잘 선택하지 않는다. (ex: 인덱스(a,b, c)에 대하여 조회조건이 where b ='1' and c ='10' 일 경우 인덱스 미사용)
- 인덱스는 가급적 1개 테이블에 10개 이하로 생성하는 것을 권장
- Null 값이 많은 컬럼은 인덱스로 잡지 않는 것을 권장
- 조회가 빈번하고 DML이 거의 이뤄지지 않을 경우 인덱스를 많이 생성해도 성능상 이슈가 적음
- DML이 빈번하거나 대량 데이터의 변경 들이 있을 경우 인덱스 생성을 최소화 하는 것을 권장
- > DML이 발생하면 인덱스에도 DML이 발생하기에 DML이 이뤄질 때 인덱스가 많을 경우 부하 발생
- 인덱스로 명시된 컬럼이 where 절에서 to_char, dated, decode 등 함수로 변환할 경우 인덱스를 타지 못하기에 이럴 경우 funtion - based index 생성 검토
- 인덱스로 만드는 컬럼 선정 법
- 조건 절에서 많이 사용되는 컬럼을 선행으로 배치
- 컬럼의 분포도가 높아서 distinct 한 값이 다양할 수록 선행으로 배치
- = 조건을 사용할 수록 앞으로 배치하며, 그 다음은 between, 이후 >,<,<=.>= 순으로 선행으로 배치하기 적합하다
- 데이터 정렬 (order by) 에서 자주 사용하는 컬럼일 수록 선행으로 배치

4. JOIN 종류 및 장단점
- Nested Loop Join
- 가장 많이 쓰이는 join으로 성능이 안정적인 것이 특징
- 두 테이블을 join 할때 서로의 인덱스를 통해 필요한 부분만큼 추출하여 join
- 선행 테이블의 인덱스를 읽어와서 해당 인덱스 데이터에 해당하는 후행 테이블의 인덱스 값과 매치 하기에 부하가 적다
- 쿼리 조건을 통해 join에 쓰이는 테이블들이 전부 인덱스를 탄다면 옵티마이저는 가급적 NL JOIN을 탐
- 가장 중요한건 선행 테이블(쿼리 앞 부분에 적힌 테이블)의 데이터가 작아야 성능이 좋다
- 선행 테이블의 데이터 양에 따라 join 되는 갯수가 달라지기에 선행 → 후행으로 갈 수록 데이터 양이 많도록 배치 하는 것이 중요
- 적은 데이터 및 인덱스를 잘 탈 경우 NL JOIN이 유리하며, NL JOIN의 경우 성능이 일정하다.
- 일반적인 사용자 조회, 검색 등 수초 내에 끝나는 작업들은 NL JOIN으로 구성하는 것이 유리하다.

- Hash Join
- 선행 테이블에서 join에 쓸 키 값을 해시 값으로 변환하여 해시 테이블을 생성
- 후행 테이블의 join 키 값을 해시값으로 변환하여 위에 생성된 해시테이블과 비교하여 join 한다.
- 인덱스를 타지 않고 Full scan을 하거나 인덱스를 탓음에도 데이터 양이 많을 때 유리하다
- CPU와 메모리 영역을 다수 사용하기에 서버 상태 및 성능에 따라 쿼리의 성능이 달라진다.
- join을 할 때 = 연산자를 사용할 경우만 사용 가능하다
- 대용량 데이터를 join 할때 hash join을 사용하는 것이 유리할 경우가 있기에, 그럴 경우 인덱스를 타지 않고 full table scan을 하도록 유도하는 방식으로 hash join을 사용한다.
- 대용량 성 배치, 집계 등 수행 시간이 오래 걸리는 쿼리에서 hash를 사용하는 것이 유리하나, hash join이 많은 테이블은 서버의 충분한 CPU, 메모리 확보가 선행되어야 한다.

- Merge Join
- 선행 테이블, 후행 테이블에 join key에 해당하는 값을 검색하여 불러온 뒤 해당 데이터를 정렬한다.
- 이후 정렬된 key 값을 비교하여 join 하는 방식으로 이루어진다
- hash join과 마찬가지로 대용량 데이터를 join 할때 유리하며 cpu, 메모리를 소모하는 hash join과 다르게 디스크 영역을 사용하기에 성능 적으로 떨어진다.
- 디스크 영역을 사용하며, DB에서 가장 부하가 심한 작업인 order by 가 이뤄지기에 3개의 join 중 가장 성능이 떨어진다.
- join 조건이 = 가 아닌 비교 연산자(>,<,<=,>=) 에서 사용할 수 있으며, 가급적 join 전에 정렬을 끝내고 범위를 최소한으로 줄이는 방식으로 성능을 개선하여야 한다.

추천 join
화면 조회 : NL join
대용량 데이터 집계, 배치성 작업 : Hash join
범위값에 대한 집계, 배치성 작업 : Sorted Merge join
5. Slow Query 방지를 위한 가이드
지연 발생 가능 query 유형 | 이슈사항 | 개선 포인트 |
LIKE '%키워드%' 쿼리 사용 | 쿼리 속도 저하 | • LIKE 문을 사용하지 않는 방향으로 개선 검토
• like '키워드%' 로 쿼리 변경 시 성능 개선
• 각 DBMS에서 like 검색을 위해 제공하는 여러 기능들 사용
- Oracle : oracle Text 기능
- Postgresql : GIN Index 사용, pg_trgm 기능 사용
- Mssql : Full Text Index 사용
- Mysql : Full Text Index 사용 |
Cursor 및 Loop 사용 | 쿼리 속도 저하
서버 자원 부하 | • 커서 및 loop로 돌릴 경우 동일한 쿼리를 인입 변수만 바꾸어 반복 수행시키기에 임시테이블(With)절을 활용하여 1번에 처리 되도록 개선
• Bulk collect into 같은 대용량 처리 기법에 대해 사용 검토 |
대량 데이터에 대한 인서트 | 쿼리 지연, 오류 및 lock 발생 | • 대용량 데이터에 대한 인서트의 경우 한번에 전부 Insert 후 commit을 하는 구조로 할 경우, 데이터 인서트 중 다른 세션이 잠기는 TX lock 이 발생 가능
• Bulk Insert 기능을 활용하여 대용량 데이터에 인서트에 대한 개선 및 부하 방지 권장
• 대량 데이터가 인서트 되는 테이블은 nologging 모드로 바꾼뒤 insert하고, 오라클의 경우 /* append*/ 힌트를 사용하는 것을 권장 |
무분별한 With 절 사용 | 쿼리 속도 지연
서버 자원 부하 | • with 를 쓸 경우 해당 결과를 세션 종료 전까지 메모리에 적재 중
• 결과 데이터가 1건 같이 테이블로 굳이 생성할 필요가 없을 경우 임시 테이블이 아닌 변수를 활용하여 처리
• 오라클의 경우 /*+ materialize */ 힌트를 통해 해당 with 절을 global temporary 성으로 변환
• 자주 쓰는 테이블의 경우 with 가 아닌 temp 테이블 생성 검토 |
많은 DML에 비해 적은 COMMIT 수 | LOCk 발생
오류 발생 및 운영 이슈 발생 가능 | • 같은 세션에서 다른 dml 작업이 남아있어도 해당 테이블에 dml을 완료했다면 commit을하는 것을 권장
• 배치의 경우 commit 을 중간 중간 진행하되, 로그성 테이블을 별도로 관리하여 진행 및 오류 발생 부분 확인
• 중간에 temp 테이블에 데이터를 담고, temp 테이블에 정제 작업을 한 뒤 마지막 부분에 temp 테이블 → 운영 테이블 로 ETL을 통해 배치 완성 권장 |
Union all 사용 | 쿼리 속도 지연 및
결과 오류 발생 가능 | • Union all의 경우 중복된 값을 별개 값으로 취급하기에 로직을 잘 따진채 사용
• union all이 길어질 경우 temp 테이블을 만들어서 적당한 결과들을 중간 중간 temp 테이블에 insert 하여 처리 하는 것을 권장 |
IN / NOT IN / EXISTS/ NOT EXISTS 사용 | 쿼리 속도 지연 | • in / not in 절의 경우 변수에 따라 해당 쿼리를 각 변수 숫자 만큼 반복 수행하는 경우 존재
• 항상 쿼리가 느린 것은 아니나 인덱스의 상황, 자원 상태, 쿼리에 따라 지연이 발생하는 경우 다수 존재
• in 및 Exists의 경우 inner join으로 변환하여 처리가 가능한지 검토 후 개선
• not in 및 not exists의 경우 left(right) outer join으로 변환하여 처리 가능한지 검토 후 개선 |
Where 절에 1=1 사용 | 보안 위협 | • 항상 참이 되는 조건이 들어감으로써 Sql Injection이 발생가능한 보안 이슈 발생 가능
• 과거 많은 회사들이 해당 조건으로 인해 해킹 피해 발생
• 개발 간 편의성만을 위해 사용하는 구문이기에 상용기 반영 금지 |
무분별한 인덱스 추가 | 쿼리 속도 지연 | • 검색 속도 개선을 위해 인덱스를 계속 늘리게 될 경우 DML 속도 지연 발생
• DML이 자주 일어나는 테이블의 경우 인덱스 추가에 대해 충분히 검토하며, 주기적으로 DBA 에게 요청하여 중복인덱스, 미사용 인덱스, 통합 가능한 인덱스 등에 대한 정리 필요 |
통계정보 미수집 | 쿼리 속도 지연
안 좋은 Plan 수행 | • 각 DB 들에 대하여 대량 데이터 인입 혹은 주기적인 통계정보 갱신 필요
• 해당 통계정보를 근거로 옵티마이저는 플랜을 짜기에 통계정보 미갱신 시 플랜 저하
• 통계정보 갱신 수행 시 Plan들이 바뀌기에 적용 정책에 대하여 충분한 논의 필요
• 신규 테이블 생성 후 자동 통계 정보 생성이 안되는 경우 수기로 통계정보 갱신 필요 |
인덱스 리빌드 미진행 | 쿼리 속도 지연
안 좋은 Plan 수행 | • 인덱스는 시간이 지날수록 빈 공간이 늘어나는 구조로 단편화 발생
• 인덱스 리빌드라는 작업을 통해 단편화를 막고 실 사용하는 만큼의 공간으로 인덱스 재정렬 필요
• 해당 리빌드 작업은 Down Time이 없는 작업이나, 해당 테이블 조회시 약간의 지연 발생
• 특히 Mssql의 경우 해당 인덱스 리빌드는 중요한 작업으로 최소 주 1회 작업 권장 |
파티션 키 누락 쿼리 | 쿼리 속도 지연 | • 파티션 테이블에 대하여 파티션 키를 누락한채 쿼리를 짜게 될 경우 파티션 테이블이 아닌 전체 테이블을 탐색
• 파티션 키 누락시 인덱스를 타기도 쉽지 않으며 Full parition table scan을 진행하는데 일반 테이블 Full Scan 보다도 성능 저하 발생
• 파티션 테이블 설계 시 파티션 키는 모든 쿼리에 필수조건으로 쓰이도록 설계 진행
• 인덱스 생성 시 가급적 파티션 키가 선행으로 오는 인덱스 생성 권장
• 쿼리 생성 시 필수적으로 파티션 키 명시 |
높은 디스크 사용률 | 쿼리 속도 지연 | • AWS를 쓰지 않는 IDC 환경에서 디스크는 제한되어 있는 자산
• 디스크의 여유 공간이 없을 경우 큐잉 및 디스크 부하 등으로 인해 DB 성능 저하 발생
• 가급적 디스크 사용률은 85% 미만을 유지하는 것을 권장 |
유저 생성 함수 사용 | 쿼리 속도 지연 | • 쿼리의 결과가 여러 Row 일 경우 개발자가 편의성만을 위해 여러 사용자 테이블을 join 하여 만든 함수를 사용할 경우 row 수만큼 해당 함수 수행
• 함수 수행량이 많기에 서버 부하 및 지연 발생
• 쿼리 튜닝 시 함수의 경우 영향도가 크기에 튜닝이 어려움
• 함수를 사용하는 경우는 split 등 단순한 업무 외엔 미사용 권장 |
outer join 사용 | 쿼리 속도 지연 | • Outer join의 경우 inner join과 비교하여 유의미한 격차 만큼의 성능 차이 발생
• 로직을 확인하여 inner join으로 변경 검토 |
동일한 테이블 반복 호출 | 쿼리 속도 지연 | • 동일한 테이블을 계속 반복하여 호출 시 불필요한 Block 소모로 인해 성능 저하
• 동일한 테이블이지만 조건이 다를 경우 가장 큰 집합 부분으로 쿼리를 생성하여 With 절 사용 |
정렬 사용 자제 | 쿼리 속도 지연 | • Order by 구문의 경우 DB에서 가장 비용이 높은 부하 부분
• 해당 정렬 부분은 꼭필요한 곳만 사용하며, 가급적 자바단에서 처리 |
Group by / Distinct에 대한 구분 및 사용 | 쿼리 속도 지연 | • DBMS에 따라 해당 쿼리가 중복 제거 뿐 아니라 정렬까지 진행을 하여 느려지는 현상 발생
• 정렬이 필요 없다면 내용 확인하여 정렬하지 않는 로직 사용 |
6. 플랜 확인법
oracle / Postgresql
- 전체적으로 Plan을 살핀 뒤 Buffer(Block)의 사용량을 파악하여 과도하게 많이 사용된 부분 탐색
- Buffer 의 사용량이 비슷하다면 각 단계 별로 수행 시간을 확인하여 수행시간이 오래 걸린 부분에 대해 확인
- 보통 Plan이 느린 많은 케이스는 아래와 같다.
1. 인덱스 미존재로 인한 Full 테이블 scan
2. 대량 데이터에 대하여 인덱스를 통한 NL JOIN 수행
3. 동일한 테이블이 반복하여 호출
4. 다수의 loop 발생
select 문 안에 함수가 있으면 모든 데이터에 대해서 함수를 다 돌려야해서 오래 걸릴 수 있음
5. 실제 결과 값에 비해 많은 Row가 반환되는 테이블
left join 이런것보다 inner join 으로 바꾸는게 낫다
Mysql

항목 | 설명 |
select_type | select 문의 유형
1. SIMPLE : 일반적인 조회
2. PRIMARY : 최상단에 나오는 최종 조회
3. SUBQUERY : 서브 쿼리 조회 문
4. DERIVED : From 절로 쓰이는 SubQuery |
table | 참조되는, 참조하고있는 table |
type | JOIN 방식
1. system: 1개 이하의 row 를 가진 table
2. const: JOIN을 하는 테이블들이 전부 1개의 결과만 가지고 있는 경우
3. eq_ref: primary key 나 unique key 로 검색하는 경우
4. ref: 일반 인덱스를 사용할 경우
5. Range : 인덱스를 사용하나 키 컬럼이 상수와 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN 또는 IN 연산에 사용되는 경우
6. ALL : 인덱스를 타지 못하고 전체 데이터를 읽어서 Join 하는 경우 |
key | 쿼리에서 사용한 인덱스 목록 |
ref | 인덱스를 유도한 컬럼 목록 |
rows | 각 테이블에서 나온 record 의 수 |
- type 이 중요함. 위에서부터 빠른 순서. pk나 unique key는 일반 인덱스와 달리 정렬되어 있음
- key가 null이면 풀스캔 탄다는것
7. DB 모니터링 하는 법

- 평상시에는 CPU 50% 이하, peak 시에는 80% 이하여야 함. 그 이상이면 증설해야 함
- 메모리 부족하면 디스크로 가기 때문에(DISK IO) 성능 차이 많이 남. Freeable memory 확보 필요
- Oracle ms 는 메모리 설정을 할 수 있음. Postgresql, Mysql 은 인스턴스 타입 변경이 필요함(메모리 증설)
- DB I/O 가 생긴다는게 메모리가 부족한것
- DB 내 지연이 발생하면 운영 담당자는 긴급 모니터링에 들어간다.
- 긴급 모니터링에서 위의 순으로 단계 별로 체크해가며 모니터링 및 조치를 취한다.
(긴급 조치 법은 8. DB 지연 발생 시 긴급 조치 참고)
- AWS 에서는 성능 개선 도우미에서 오래 걸린 SQL 다 확인할 수 있음
8. DB에 자주 지연을 발생시키는 요소들

나. 지연을 일으키는 요소
주요 요소 | 지연 발생 사유 |
주요 요소 | 지연 발생 사유 |
DB 내 LOCK 발생 | • 쿼리 로직내 동일 테이블에 대하여 여러 다른 쿼리들에서 DML이 이뤄질 경우 Lock이 발생하며 전체 DB에 부하 발생
• 해당 LOCK 지속 시간이 길어지게 될 경우 Dead Lock을 유발할 가능성이 존재함
• 개발 당시에는 LOCK이 발생하지 않았어도 시간이 지나 데이터 양이 증가함에따라 추후 LOCK 발생 가능 |
타 배치 지연으로 인한 전체적 리소스 부족 | • 배치 중 일부가 평소보다 지연이 되면, 리소스가 여유롭지않은 DB서버의 경우 해당 지연 배치로 인해 그 이후 모든 배치들이 영향을 받아 조금 씩 느려지며, 조금 씩 밀린 배치들이 쌓여 어느 시점에 지연 이슈 발생 가능
• 타 배치 지연 이슈의 경우 DB내 Lock 및 세션 타임아웃, CPU 과부하 등 타이슈를 연쇄적으로 발생시킬 수 있음 |
오래 걸리는 쿼리 로직 및 플랜 사용 | • 개발 초기엔 데이터 양이 작아 몰랐으나 데이터 사이즈가 증가함에 따라 점점 지연이 발생되는 쿼리 발생
• 통계 정보를 수집하지 않거나, 장기간 갱신하지 않아 초기에 설정된 플랜을 사용하여 느려지는 현상 발생 가능
• 잘못 설정된 쿼리 힌트를 사용하여 지연 발생 |
DB 내 과도한 I/O 발생 | • 불필요한 FULL SCAN 나 INDEX SCAN으로 인한 전체 I/O 증가
• 대용량 데이터 테이블에 대하여 파티션 테이블로 구성하지 않아 전체 I/O 증가
• 적절치 않은 파티션 키 설정으로 인한 파티션 ALL SCAN으로 전체 I/O 증가
• JOIN시 테이블 내 필요한 정보만큼 가져오는게 아닌 전체 데이터에대한 JOIN으로 인해 전체 I/O 증가
• 로직상 불필요해진 테이블에 대하여 쿼리에서 제거 하지 않아 전체 I/O 증가 |
CPU 및 메모리 등 자원 부족 | • 초기 운영기 도입 과정에서 자원 예측 실패로 인한 자원 부족 • 무분별한 패러럴 옵션 사용으로 인한 CPU 부족
• 적절하지 않은 메모리 사용량 산정(논리적 메모리)으로 인한 메모리 부족
• 1개의 서버에 여러 인스턴스를 사용함으로 인한 자원 부족 |
테이블 내 불필요한 물리 BLOCK 발생 | • 테이블에 대하여 지속적으로 DELETE, INSERT를 진행하며 REORG 나 TRUNCATE를 하지 않는 이슈
• 테이블 생성후 보관주기 수립 및 파기하지 않는 이슈
• 데이터 파기 시 플래그 값만 변경하거나 DELETE 만 진행하는 이슈 |
기타 | • 프로시저 내 많은 양의 DML이 이뤄질 경우 중간 중간 COMMIT을 하지 않고 마지막에 COMMIT을 1회 함으로써 대량 COMMIT으로 인한 DB 부하
• 대용량 INSERT 작업으로 인한 DB 부하
• 무분별한 WITH 절 사용으로 인한 DB 부하 |
9. DB 지연 발생 시 긴급 조치
- DB 의 LOCK 확인 및 세션 킬 방법
가. Oracle
/* 세션 KILL 권한 확인(ALTER SYSTEM 권한이 존재하여야 함)*/ SELECT * FROM DBA_SYS_PRIVS WHERE GRANTEE = '계정명‘ ; /* TM, TX 락 여부 확인(테이블 락, 로우 락) */ SELECT * FROM V$LOCK WHERE TYPE IN( 'TM‘, TX); /* LOCK 걸린 개체의 정보 확인 */ SELECT sid, serial#, username FROM V$SESSION WHER SID = ‘락걸린SID’; /* 세션 KILL */ -- ALTER SYSTEM KILL SESSION 'SID , serial# ‘ ;
나. Mssql
/* mode 가 x 대상의 paid 추출*/ EXEC sp_lock /* Lock 걸린 쿼리 내용 확인*/ DBCC inputbuffer(spid) /* lock 해제*/ kill spid -- 이것도 마찬가지로 권한 중요
다. Postgresql
SELECT t.relname, l.locktype, page, virtualtransaction, pid, mode, granted FROM pg_locks l, pg_stat_all_tables t WHERE l.relation = t.relid ORDER BY relation ASC; SELECT pg_terminate_backend(pid);
라. Mysql
SELECT * FROM information_schema.`PROCESSLIST` WHERE Command <> 'Sleep' AND USER NOT IN ('system user', 'event_scheduler') ORDER BY TIME DESC ; kill sid;
- 오래 걸리는 쿼리 확인
가. Oracle
SELECT * FROM( SELECT ROUND(ELAPSED_TIME/EXECUTIONS/1000000,3) AS ELAPSED_TIME , LAST_ACTIVE_TIME , HASH_VALUE , SQL_TEXT FROM V$SQL WHERE parsing_schema_name = '스키마명' -- 스키마명 아니면 느림 AND LAST_ACTIVE_TIME >= TO_DATE('20180307', 'YYYYMMDD') ) WHERE ELAPSED_TIME > 7 --초 단위 입력 order by ELAPSED_TIME desc;
나. Mssql
SELECT TOP 20 qt.text AS SP, qs.execution_count, qs.execution_count/DATEDIFF(Second, qs.creation_time, GetDate()) , qs.total_worker_time/qs.execution_count, qs.total_worker_time , qs.total_elapsed_time/qs.execution_count, qs.max_logical_reads , qs.max_logical_writes , qs.total_physical_reads , DATEDIFF(Minute, qs.creation_time, GetDate()) FROM sys.dm_exec_query_stats AS qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt WHERE qt.dbid = db_id() ORDER BY qs.total_worker_time DESC
다. postgresql
SELECT * from pg_stat_activity where state = ‘active’ and application_name = ‘’ -- api 지정 and current_timestamp - query_start > ‘7 sec’ -- 시간 지정
라. Mysql
SELECT start_time, user_host, query_time, lock_time, rows_sent, rows_examined, db, CONVERT(sql_text USING utf8 ) sql_text FROM mysql.slow_log;