HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🤩
개발
/데이터베이스(Database)/
Database
Database
/
DB 꿀팁

DB 꿀팁

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. 테이블 종류 및 사용법

  1. 일반 테이블
      • 통상적으로 사용하는 일반 테이블
  1. Temp 테이블(Temporary 테이블)
    1. notion image
      • 임시 테이블로 평상시에는 빈 테이블로 존재
      • 세션이 들어와서 해당 테이블에 DML을 통해 데이터를 넣으면 테이블 내 데이터를 들고 있다가 세션 종료 시 자동 Truncate
      • 동일한 Temp 테이블을 바라보더라도 세션이 다를 경우 독립적으로 존재 및 독립적으로 DML 수행
      • in 조건에 다량의 변수가 들어오거나. ETL 과정에서 중간에 집계 및 거처가는 테이블 들로 주로 사용
      • 인덱스도 생성 가능하나, 가급적 필요한 데이터만 인입 시키고 인덱스는 미생성 (PK제외) 권장
  1. 파티션 테이블
    1. notion image
      • 하나의 큰 테이블을 쪼갠 여러 자식 테이블로 분리하는 테이블 구조
      • 큰 테이블을 특정 기준으로 잘라내어 작은 테이블들로 자르게 되면 데이터 조회시 파티션 키 라는
        • 테이블을 자른 기준 컬럼을 명시해줄 경우 전체 큰 테이블이 아닌 해당하는 파티션 테이블만 조회
      • 데이터 사이즈가 크고 자를수 있는 기준이 더 있을 경우 파티션의 파티션, 즉 이중 파티션 구조로도 생성 가능
      • 장점으로는 다른 테이블 스페이스를 배정하여 IDC 환경에서 동일 디스크를 자주 접속 하는 것을 방지
      • 큰 테이블에 대하여 물리적으론 1개 테이블이나 논리적으로 쪼개어져 있기에 데이터 조회 시 필요한 작은 테이블만 access 하여 성능 개선(12개의 파티션으로 나누어져 있을 경우 실제 데이터의 1/12만 조회)
      • 하나의 파티션 테이블을 Truncate 하여도 다른 파티션 테이블에 영향이 없기에 데이터 파기에 용이
      • 대량의 데이터 혹은 주기적으로 파기 해야하는 테이블에 대하여 주로 사용하며, 날짜나 값의 범위를 파티션 키로 쓰는
        • Range 파티션을 주로 사용한다.
      • 파티션 테이블을 만들 때 인덱스는 local 타입으로 만들어야 파티션 인덱스로 생성되며, 권한의 경우 각 파티션 테이블 별로 권한을 부여하여야 하는 DBMS 존재
      • 무분별한 파티션 테이블은 오히려 성능을 악화시키기에 파티션 테이블 내 데이터가 약 50만건 이상일 경우 생성을 권장 드리며, 파티션키를 통해 나눠지는 기준도 많지 않은 좋은 key 값 선정이 핵심
 
  • * 좋은 파티션 키란?
  • > null 값이 유입되지 않고, 데이터가 바뀌지 않는 불변성을 만족해야함
  • > 해당 키를 기준으로 각 파티션 데이터가 고르게 분포 되어야 함 (데이터 쏠림 현상 발생 시 파티션 테이블 사용 무의미)
  • > 가급적 모든 Sql문에선 해당 파티션 키를 조건에 사용하여야 한다.

3. 인덱스 생성 가이드 (개발 관점)

  1. 인덱스의 종류
    1. notion image
      • Unique index : 인덱스의 값은 해당 테이블에서 유일한 값을 가지는 인덱스
      • non unique index : 인덱스의 값이 해당 테이블에서 유일하지 않아도 되는 인덱스
      • partition index : 파티션 테이블에서 생성 가능한 인덱스
        • Local partition index : 각 파티션 별로 파티션 키를 기준으로 하여 각각의 인덱스가 생성되는 지역 인덱스
        • Global partition index : 파티션 테이블의 부모 테이블에 생성하는 인덱스로, 파티션 키가 불필요하고 전체 테이블 스캔이 필요할 경우에 사용하는 인덱스, 전역 인덱스
      • single index : 인덱스를 구성하는 컬럼이 1개만 명시되는 인덱스
      • Multi index : 인덱스를 구성하는 컬럼이 여러개인 인덱스
      • Function based index : 인덱스를 생성할 때 각 컬럼별로 함수 혹은 날짜 변환 등을 사용한 결과 값을 인덱스로 생성
      • Partial index : 인덱스를 구성할때 공통적인 where 조건을 인덱스 생성 시 명시하여 필요한 부분만 가지고 있는 인덱스
  1. 인덱스 생성 시 주의사항
      • 구성하는 인덱스의 크기가 전체 데이터의 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 생성 검토
  1. 인덱스로 만드는 컬럼 선정 법
    1. notion image
      • 조건 절에서 많이 사용되는 컬럼을 선행으로 배치
      • 컬럼의 분포도가 높아서 distinct 한 값이 다양할 수록 선행으로 배치
      • = 조건을 사용할 수록 앞으로 배치하며, 그 다음은 between, 이후 >,<,<=.>= 순으로 선행으로 배치하기 적합하다
      • 데이터 정렬 (order by) 에서 자주 사용하는 컬럼일 수록 선행으로 배치

4. JOIN 종류 및 장단점

  1. Nested Loop Join
    1. notion image
      • 가장 많이 쓰이는 join으로 성능이 안정적인 것이 특징
      • 두 테이블을 join 할때 서로의 인덱스를 통해 필요한 부분만큼 추출하여 join
      • 선행 테이블의 인덱스를 읽어와서 해당 인덱스 데이터에 해당하는 후행 테이블의 인덱스 값과 매치 하기에 부하가 적다
      • 쿼리 조건을 통해 join에 쓰이는 테이블들이 전부 인덱스를 탄다면 옵티마이저는 가급적 NL JOIN을 탐
      • 가장 중요한건 선행 테이블(쿼리 앞 부분에 적힌 테이블)의 데이터가 작아야 성능이 좋다
      • 선행 테이블의 데이터 양에 따라 join 되는 갯수가 달라지기에 선행 → 후행으로 갈 수록 데이터 양이 많도록 배치 하는 것이 중요
      • 적은 데이터 및 인덱스를 잘 탈 경우 NL JOIN이 유리하며, NL JOIN의 경우 성능이 일정하다.
      • 일반적인 사용자 조회, 검색 등 수초 내에 끝나는 작업들은 NL JOIN으로 구성하는 것이 유리하다.
  1. Hash Join
    1. notion image
      • 선행 테이블에서 join에 쓸 키 값을 해시 값으로 변환하여 해시 테이블을 생성
      • 후행 테이블의 join 키 값을 해시값으로 변환하여 위에 생성된 해시테이블과 비교하여 join 한다.
      • 인덱스를 타지 않고 Full scan을 하거나 인덱스를 탓음에도 데이터 양이 많을 때 유리하다
      • CPU와 메모리 영역을 다수 사용하기에 서버 상태 및 성능에 따라 쿼리의 성능이 달라진다.
      • join을 할 때 = 연산자를 사용할 경우만 사용 가능하다
      • 대용량 데이터를 join 할때 hash join을 사용하는 것이 유리할 경우가 있기에, 그럴 경우 인덱스를 타지 않고 full table scan을 하도록 유도하는 방식으로 hash join을 사용한다.
      • 대용량 성 배치, 집계 등 수행 시간이 오래 걸리는 쿼리에서 hash를 사용하는 것이 유리하나, hash join이 많은 테이블은 서버의 충분한 CPU, 메모리 확보가 선행되어야 한다.
  1. Merge Join
    1. notion image
      • 선행 테이블, 후행 테이블에 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
notion image
항목
설명
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 모니터링 하는 법

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

8. DB에 자주 지연을 발생시키는 요소들

notion image

나. 지연을 일으키는 요소

주요 요소
지연 발생 사유
주요 요소
지연 발생 사유
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 지연 발생 시 긴급 조치

  1. DB 의 LOCK 확인 및 세션 킬 방법
    1. 가. 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;
  1. 오래 걸리는 쿼리 확인
    1. 가. 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;