* 사내 위키에 정리했던 발표 자료를 블로그로 옮겨왔습니다.
* 발표일: 2020.07.21
인덱스란 (기본 동작 원리)
인덱스를 비유하자면.. 책의 목차!
e.g. 100000페이지 책에서 'DEADLOCK'을 찾으려 한다. 어떻게 찾을까요?
-> 특정 카테고리에 속해있을 것. 해당 카테고리 찾자
인덱스의 목적
인덱스 없이 테이블 풀스캔시 비용이 커집니다.
-> WHERE 구문과 일치하는 열을 빨리 찾기
-> 모든 데이터 조사 필요 없이 중간에서 검색 위치를 빠르게 잡아 내기
인덱스, 어떻게 빠르게 동작하나요?
인덱스 탐색 원리 중 하나: B 트리! (Balanced Tree)
중간 값을 기준으로 좌우 작은 값, 큰 값으로 나뉩니다.
인덱스 사용 언제가 적합할까?
- 데이터 양이 많고 검색이 변경 (Update, Delete, Insert)보다 빈번한 경우
(재정렬에 대한 부담)
- 인덱스를 걸고자 하는 필드의 값이 다양한 값을 가지는 경우 (예 : 주민번호 (870612- XXXX) )
= Cardinality가 높은 경우.= 중복도가 낮은 경우
e.g. 이름은 주민등록 번호에 대해 카디널러티가 낮다.
구체적으로 어떤 컬럼에 인덱스를 걸면 될까요?
- WHERE절 뒤에 자주 사용되는 컬럼
- ORDER BY에 자주사용되는 컬럼
- JOIN으로 자주사용되는 컬럼
- NOT연산자는 긍정문으로 바꿔서 쓴다
- 삽입과 삭제가 빈번한 컬럼은 인덱스로 좋지않다
(인덱스를 만드는데 사용되는 공간과, 정렬하는데 걸리는 시간등이 추가적으로 필요하다.)
출처 : https://nive.tistory.com/185
인덱스 사용 예시
Java를 공부하고 싶은 비상이는 도서관 DB에게 부탁합니다.
"JAVA에 관련된 책을 모두 찾아주세요"
DB는 아래의 책 만권을 모두 뒤집니다.
SELECT name, location FROM book_store where category = "Java"
책 목록
rowidnamecategorylocation
1 | let’s start java | java | A |
2 | python basic | python | K |
3 | js for seinor | javascript | B |
4 | let’s start java | java | C |
… | … | … | … |
4222 | java? java! | java | Z |
4223 | pythonic thinking | python | A |
… | … | … | … |
9999 | javavara | java | K |
10000 | i love C++ | C++ | N |
디비가 가져온 결과입니다.
namelocation
let’s start java | A |
let’s start java | C |
java? java! | Z |
javavara | K |
비상이릉 위해 Full scan을 하는 DB. 고생이 많습니다.
열일하는 DB를 위해 인덱스(=목차)를 만들어줍시다.
DB는 생각합니다.
"좋은 기준이란 뭘까?
아! Java에 관련된 거니까 category를 기준으로 정렬하자.
그리고 내가 쉽게 찾아갈 수 있게 rowid (=주소값)를 같이 넣어주자."
categoryid
… | … |
C++ | 10000 |
… | … |
java | 1 |
java | 4 |
java | 4222 |
java | 9999 |
javascript | 3 |
… | … |
python | 2 |
python | 4223 |
… | … |
인덱스는 문자열 순서대로 정렬되어있기 때문에,
‘java’ 라는 문자열을 계속 검색하다가 ‘javascript’ 라는 문자열을 만나는 순간 이제 더이상 ‘java’ 라는 문자열을 존재하지 않는다고 단정짓고 탐색을 종료할 수 있습니다.
게다가 내부적으로 데이터를 B-Tree라는 구조에 저장하기 때문에, ‘java’라는 문자열을 찾아낼 때 맨 처음부터 순차적으로 조회하는 것 보다 훨씬 빠릅니다.
이제 DB는 아래 스킬을 익혔습니다.
SELECT name, location FROM book_store WHERE rowid IN (1, 4, 4222, 9999)
근데, 우린 rowid를 매번 눈으로 체크하고 싶지 않습니다.
그냥 DB가 알아서 찾도록 하고 싶어요. -> 인덱스를 겁시다.
CREATE INDEX index_category ON book_store(category)
이 후 아래와 같이 사용하면 됩니다.
SELECT name, location FROM book_store WHERE category = 'java'
참고 : https://itholic.github.io/database-index/
인덱스 종류 : Clustered vs Non-clustered index
cluster 단어의 뜻
- cluster : 군집
- clustered 군집된
- clustered index: 군집화된 인덱스
Cluster와 non-cluster의 저장 방식 차이점
클러스터 인덱스는 해당 테이블을 row들을 인덱스의 순서에 따라 물리적으로 재정렬.
넌클러스터 인덱스는 인덱스 페이지를 별도로 생성. 주소값 만듦. 이를 이용해서 데이터에 접근하는 방식이다.
Cluster와 non-cluster의 동작 방식 차이점
클러스터 인덱스는 데이터 위치를 바로 알기때문에 그 데이터로 바로 접근할 수 있고,
넌클러스터 인덱스는 인덱스 페이지를 한번 거쳐서 데이터에 접근하는 방식이다.
Cluster와 non-cluster의 최대 개수
클러스터 인덱스 : 물리적으로 재정렬하므로 딱 1개만 생성 가능. 2개가 가능할경우 데이터 정렬이 꼬이므로 1개만!!
넌클러스터 인덱스 : 별도의 페이지 생성. 따라서 n(n>1)개. (최대 개수는 ..: 찾아봐주세요! :) )
예시. 숫자 8을 검색하는 방법
Cluster와 non-cluster의 성능비교
클러스터 인덱스 : 범위 쿼리, 포인트 쿼리 모두 성능 발휘
넌클러스터 인덱스 : 포인트쿼리는 성능을 발휘하나, 범위쿼리는 성능을 보장할 수 없다.
*범위 쿼리(Range Query) : 조회되는 값이 여러 행
* 포인트 쿼리(Point Query): 조회되는 값이 한 행인 쿼리
어떤 인덱스가 좋은 인덱스일까?
- 분별력(Cardinality)가 높은 컬럼 / where 절에서 equal 값으로 자주 쓰이는 컬럼
예. USER_MST_ST의 Status(삭제여부. e.g. Y/N), USER_GRADE(정,준,휴 회원값 e.g. 001,002,003) 보다는 USER_ID(e.g. AAA0292) 혹은 USER_IDX (e.g.6192)값
- 복합 인덱스 (여러 컬럼으로 조합된 인덱스) 일 경우, 분별력이 높은 컬럼이 앞으로 올 것!!!
인덱스의 잘못된 사용 예제
4.1 인덱스 컬럼을 가공
e.g. WHERE SUBSTR(ORDER_NO, 1,4) = '2019'
-> WHERE OREDER_NO LIKE '2019%'
4.2 인덱스 컬럼의 묵시적 형 변환 (같은 타입으로 비교할 것)
e.g.
SQL> desc myemp1
이름 유형
----------------------------------------- -------- ---------------
EMPNO NOT NULL NUMBER
ENAME VARCHAR2(100)
DEPTNO VARCHAR2(1)
ADDR VARCHAR2(100)
SAL NUMBER
SUNGBYUL VARCHAR2(1)
테이블 상태: myemp1 데이터건수 2000만건이며 empno 칼럼에는 primary key 인덱스, deptno 칼럼에도 인덱스가 생성되어 있다
올바른 실행
SQL> select count(*) from myemp1
2 where deptno = '3';
COUNT(*)
----------
5000001
경 과: 00:00:00.23
잘못된 실행
SQL> select count(*) from myemp1
2 where deptno = 3; 칼럼의 데이터 타입이 달라 숫자를 기준으로 아래처럼 내부적으로 변환된다.
SQL> select count(*) from myemp1
2 where to_number(deptno) = 3;
COUNT(*)
----------
5000001
경 과: 00:00:20.59
출처: http://ojc.asia/bbs/board.php?bo_table=LecSQLnPlSql&wr_id=259
4.3 인덱스 컬럼 부정형 비교
e.g. WHERE MEMBER_TYPE != '10'
> WHERE MEMBER_TYPE IN = ('20', '30')
4.4 LIKE 연산자 사용시 %가 앞에 위치 (NO! NO!)
4.5 OR 조건 사용 > UNION ALL로 대체
'개발' 카테고리의 다른 글
[Linux] 파일 접근 권한 변경 (0) | 2021.10.05 |
---|---|
[웹 백엔드 시스템 구현 스터디] 1주차 기록 (0) | 2021.10.03 |
[mac] 특정 포트를 사용중인 프로세스 확인, 종료하기 (2) | 2021.10.01 |
db: 데이터 물리 삭제 VS 논리 삭제 (0) | 2021.08.05 |
터미널 명령어를 사용하여, 프로젝트를 intelliJ IDE에서 열기 (0) | 2021.07.19 |