YSM 사내 교육 자료
검색팀 송원석
이 자료는 전문적이지 못합니다.
최대한 특정 디비가 아니라 전반적인 RDB내용을
담으려고 했습니다.
틀린점은 언제든지 지적 해주세요.
필드명을 명시하지 않으면 DB는 스키마 정보를 얻기위해 추가 SCHEMA테이블을 참조한다.
SELECT * FROM DUAL -- (X)
SELECT id, name FROM DUAL -- (O)
컬럼 데이터를 연산해서 조건을 거는 경우 인덱스를 탈 수 없다.
다만 오라클은 함수기반인덱스로 우회 가능
SELECT
NAME
FROM
DUAL
WHERE
(COUNT * 2) >= 10 -- (x)
----------------------------
SELECT
NAME
FROM
DUAL
WHERE
DOUBLE_COUNT >= 10 -- (O)
NOT (!= ,<>, NOT IN, NOT EXISTS)조건을 걸면
인덱스 컬럼이어도 조건이 아닌 데이터들을 모두 스캔해봐야 하기 때문에
인덱스를 타기 어렵다.
가변적으로 코드가 늘어나지 않는다면 IN조건을 사용하자
SELECT
NAME
FROM
DUAL
WHERE
CODE_TYPE <> 'A' --(X)
----------------------------
SELECT
NAME
FROM
DUAL
WHERE
CODE_TYPE IN ('B','C','D') -- (O)
앞에 %를 걸면 처음에 어떤 단어가 앞에 올지 모르기 때문에
DB는 전체 모든 테이블 데이터를 스캔한다.
그렇다고 모든 쿼리를 바꾸지 말고 상황을 보고 바꾸자.
(로직상 당연히 필요한데 느리다고 빼버리면 안돼요)
SELECT
NAME
FROM
DUAL
WHERE
ID LIKE CONCAT('%','silvernine', '%') --(X)
----------------------------
SELECT
NAME
FROM
DUAL
WHERE
ID LIKE CONCAT('silvernine','%') -- (O)
COUNT 구문은 조건에 맞는 모든 row를 전부 읽는다.
EXISTS를 쓰면 체크하는 row수가 절감된다.
(mysql은 별로 차이 없는거 같은게 함정..)
SELECT
(SELECT COUNT(*) FROM T_CATE_DEALS B WEHRE B.DID = A.DID) CATE_USE
FROM
T_DEALS A -- (X)
----------------------------
SELECT
EXISTS(SELECT 1 FROM T_CATE_DEALS B WEHRE B.DID = A.DID) CATE_USE
FROM
T_DEALS A -- (O)
DISTINCT는 FULL SCAN의 주범이다. (중복이 얼마나 있는지 다 찾아야하니)
단순 JOIN 중복제거의 목적이면 조건절로 걸러내거나 subquery, semi join
등을 이용하자.
SELECT
DISTINCT A.DID, B.DID AS CATE_DID
FROM
T_DEALS A INNER JOIN B T_CATE_DEALS --(X)
----------------------------
SELECT
A.DID,
(SELECT B.DID FROM
T_CATE_DEALS B
WHERE B.DID = A.DID LIMIT 1) AS CATE_DID
FROM
T_DEALS A -- (O)
----------------------------
SELECT
A.DID
FROM
T_DEALS A
WHERE
A.DID IN (SELECT DID FROM T_CATE_DEALS)
일반적으로 dependent subquery (scalar subquery)
보다는 join이 더 빠른것으로 인지되나
case by case 일 수 있으므로
실행계획을 참조하는것이 좋습니다.
view는 단순히 쿼리를 미리 저장해놓은 개념
view에 명시된 조건은 무조건 쿼리 실행시 시행됨.
application관점으로 필요치 않은 view조건이 호출될 수 있음.
view에 명시된 조건과 추가로 select view로 넣은 조건이 Optimize안될 수 있음
Oracle은 Material View를 이용해 view형식으로 데이터를 따로저장해 퍼포먼스 향상가능
Join 컬럼을 기준으로 데이터 정렬후 수행
전체 범위 처리 유리
Mysql은 아직 지원 안하는듯(?)
Join 컬럼을 별도의 hash set을 생성하여 join
대용량 처리 유리
mysql에는 block nested loop hash이라는 명칭으로 최근에 추가됨
FK(Foreign Key)는 인덱스가 아님. (중요)
그냥 A테이블 B테이블 모두 잘 정렬 돼있어야 좋은 퍼포먼스가 나옴
data Type, length, charset
mysql의 경우 Storage engine 부터 collation까지 모두!!
Statement.setFetchSize(Integer.MIN_VALUE)
세팅시 data를 stream방식으로 가지고옴
(mysql or mariadb)
Out of memory 조심
myBatis에서는 select tag의 attribute로 fetchSize를 지정 가능
<select id="select" fetchSize="1000">
SELECT * FROM DUAL
</select>
jdbc url에
rewriteBatchedStatements=true
설정하면 batch insert시 속도를 향상 시킬 수 있다.
mysql driver와 maria driver가 서로 지원하는 설정값이 틀리다.
(mysql이 지원하는 파라미터가 좀 더 많음)
뭐가 더 낫다고 얘기하긴 어려우니 잘 판단하여 사용합시다.
SELECT * FROM mysql.slow_log 혹은 file에 적재
쿠차 테스트DB 슬로우쿼리 설정
v$sql 혹은 v$sqlarea 에 elapsed_time 컬럼으로 조건을 걸어서 확인 가능
-- 가장 시간이 오래걸린 10개 목록을 가지고오는 쿼리
SELECT * FROM
(SELECT
sql_fulltext,
sql_id,
elapsed_time,
child_number,
disk_reads,
executions,
first_load_time,
last_load_time
FROM v$sql
ORDER BY elapsed_time DESC)
WHERE ROWNUM < 10
MS에서 지원하는 Sql Server Profiler를 이용해 실시간 모니터링 가능
EXPLAIN
SELECT * FROM DUAL
① rowid에 의해 단일행 실행
– 가장 빠름
– ROWID 구성 : 6(객체번호:emp) – 3 – 6(블럭주소) – 3(블럭에서 순번)
② cluster-join에 의한 단일행 실행
③ unique-key, primary-key를 사용한 hash-cluster key에 의한 단일행 실행
④ unique-key, primary-key에 의한 단일행 실행
– create unique index pk_emp on big_emp (deptno);
⑤ cluster 조인
⑥ hash-cluster key
⑦ 인덱스화된 cluster-key
⑧ 복합 인덱스
– create index deptno_job_idx on big_emp (job, deptno);
⑨ 단일 컬럼 인덱스(equal)
– create index sal_idx on big_emp (sal);
⑩ 인덱스가 구축된 컬럼에 대한 제한된 범위 검색
– between, like, < and >, = 표현식
– crete index ename_idx on big_emp (ename);
select ename, deptno from big_emp where ename between ‘B%’ and ‘S%’;
⑪ 인덱스가 구축된 컬럼에 대한 무제한 범위의 검색
– >=,=< 표현식
– crete index ename_idx on big_emp (sal);
select ename from big_emp where sal > 3000;
⑫ 정렬-병합 조인
⑬ 인덱스가 구축된 컬럼에 대한 MAX, MIN
⑭ 인덱스가 구축된 컬럼에 대한 ORDER BY
⑮ Full-Table Scan – 가장 느림
Oracle Rule-Based Optimizer
SIMPLE | 단순 SELECT (UNION 이나 서브쿼리를 사용하지 않음) |
PRIMARY | 가장 외곽의 SELECT |
UNION | UNION 에서의 두번째 혹은 나중에 따라오는 SELECT |
DEPENDENT UNION | UNION 에서의 두번째 혹은 나중에 따라오는 SELECT, 외곽쿼리에 의존적이다. |
UNION RESULT | UNION 의 결과물. |
SUBQUERY | 서브쿼리의 첫번째 SELECT |
DEPENDENT SUBQUERY | 서브쿼리의 첫번째 SELECT, 외곽쿼리에 의존적이다. |
DELIVERD | SELECT 로 추출된 테이블 (FROM 절 내부의 서브쿼리) |
UNCACHEABLE SUBQUERY | |
UNCACHEABLE UNION | |
MATERIALIZED | 서브쿼리 임시테이블 조회시 사용 |
INSERT | INSERT or UPDATE or DELETE 구문 |
system | 테이블에 단 하나의 행만 존재(시스템 테이블). const join 의 특수한 경우이다. |
const | 많아야 하나의 매치되는 행만 존재하는 경우. 하나의 행이기 때문에 각 컬럼값은 나머지 연산에서 상수로 간주되며, 처음 한번만 읽어들이면 되기 때문에 무지 빠르다. SELECT * FROM tbl_name WHERE primary_key=1; |
eq_ref | 조인수행을 위해 각 테이블에서 하나씩의 행만이 읽혀지는 형태. const 타입이외에 가장 훌륭한 조인타입이다. SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column; |
ref | 이전 테이블과의 조인에 사용될 매치되는 인덱스의 모든행이 이 테이블에서 읽혀진다. (PK or Unique key X) |
fulltext | mariaDB의 fulltext검색을 사용시 |
ref_or_null | ref와 같으며 null이 포함된 데이터 참조시 |
index_merge | 인덱스 병합으로 최적화 적용시 |
unique_subquery |
value IN (SELECT primary_key FROM single_table WHERE some_expr) unique_subquery 는 성능향상을 위해 서브쿼리를 단순 index 검색 함수로 대체한다. |
index_subquery | unique_subquery와 같으며 non unique사용시 value IN (SELECT key_column FROM single_table WHERE some_expr) |
range | 인덱스를 사용하여 주어진 범위 내의 행들만 추출된다. key 컬럼은 사용된 인덱스를 나타내고 key_len 는 사용된 가장 긴 key 부분을 나타낸다. ref 컬럼은 이 타입의 조인에서 NULL 이다. range 타입은 키 컬럼이 상수와 =, <>, >, >=, <, <=, IS NULL, <=>,BETWEEN 또는 IN 연산에 사용될때 적용된다. |
index | index scan. index가 들어갔다고 빠르다고 생각하면 안됨 |
ALL | TABLE FULL SCAN |
using index | 가지고 오는 데이터를 인덱스에서만 가지고 왔다는 표시. 일반적으로 좋은 성능. corverd index라고 불리며 mssql에서는 index lookup으로 부름 |
using index condition | where조건이 인덱스 조건을 탈 수 있는 경우 |
using where | 일반적인 조건에서 보여지며 스토리지 엔진에서 읽은 데이터를 필터링으로 버린것을 의미 |
using temporary | 쿼리 처리시 중간 결과 저장을 위해 임시 테이블을 사용할때보여짐. 임시테이블은 메모리에 생성될수도 있고 디스크에 생성되었을 수도 있음. 일반적으로 derived, union, distinct count시에 보여짐. |
using filesort | 인덱스를 사용하지 않고 정렬이 될때 나옴. 튜닝 필요. (RDB에서 일반적으로 file이란 단어가 들어가면 느리다고 봐도 무관함) |
extra는 종류가 많아 일부만 명시합니다.
검색 색인 쿼리 실행계획
SET profiling = 1;
SHOW PROFILE;
실제 쿼리를 날려 데이터 처리 순서대로 시간별 정보를 볼 수 있음
어느 테이블에 어느 데이터를 처리하는지 까지는 보이지 않음
set profiling = 1;
select * from CHART.T_DEALS order by DID desc limit 100;
show profile;
MYSQL은 EXPLAIN EXTENDED으로 filter 정보를 추가로 볼 수 있음
빠르게 한다고 이 조건들을 다 인덱스를 걸수는 없으니;;
인덱스는 비지니스적으로 접근하자.
Connection Monitoring
DB Schema
Buffer
Storage Engine
Skip Scan
lock
하고 싶은건 많지만 ...