유튜브 강의영상들, ai를 참조해 작성한 게시글입니다.


INDEX 

만약 1000만명의 유저가 가입 순서에 따라 나열되어있다고 생각해보자.

user_id [pk] nick_name [varchar(20)] email [varchar(50)] created_at [timestamp]
1 jo asd@asd.asd ...
2 woang qwe@qwe.qwe ...
~ ~ ~ ~
1000000 Kim qaz@qaz.qaz ...

각자의 닉네임이 모두 다른데, 그중에서 Kim을 찾으려고 한다면 1000만개의 닉네임을 선형적으로 탐색하기 때문에 어마어마한 시간이 걸릴것이다. 그래서 사용하는 방법이 nick_name 컬럼에 대한 인덱스를 생성하는 것이다. (보통 처음부터 만들어놓고 시작하겠지만)

 

인덱스를 만들면 postgresql은 캐릭터 테이블을 전부 스캔해 각 행의 nick_name 값을 추출해 1000만개의 (nick_name, CTID) 쌍을 만든다. CTID는 실제 물리적 행 위치. 그리고 1000만개의 닉네임을 B-tree를 사용해 정렬한다. 메모리가 부족하면 디스크 정렬까지 간다고 함. 정렬된 데이터를 여러 페이지로 나눠 저장, 리프 노드 구성, 상위 노드 생성, 루트 생성한다.

postgresql의 기본 인덱스 타입은 B-Tree

이렇게 만들어진 인덱스 파일은 테이블과 별도의 파일로 관리되고 추가적으로 공간을 잡아먹는다. 대신 테이블에서 nick_name 을 검색 시 더이상 테이블에서 선형탐색을 하지 않고, 정렬된 인덱스에서 빠르게 검색한 뒤 CTID로 실제 행의 위치를 알아내기 때문에 매우 빠르다.

 

값을 넣을때마다 인덱스 수정해줘야하기 때문에 하나의 테이블에 인덱스가 많이 설정되어있다면 공간도 많이 먹고 insert 가 상대적으로 오래걸릴 수 있다. 하지만 select가 매우 빨라지기 때문에 트레이드 오프가 있다.

INDEX를 만들기 좋은 컬럼

  • WHERE 을 자주 사용할 컬럼
  • JOIN 을 자주 사용할 컬럼
  • ORDER BY 를 자주  사용할 컬럼
  • 범위 검색이 많은 컬럼
  • 숫자 - 비교와 정렬이 빠르고 저장공간이 작음.
  • 날짜 - 내부적으로 숫자처럼 저장되고 보통 순차적으로 증가함
  • email - 일치 검색이 매우 유용함

INDEX를 만들기 안좋은 컬럼

  • 성별, is_active같이 중복이 많은 값 - 선택도가 떨어지면 인덱스 써도 이득이 없다.
  • 자주 업데이트 되는 컬럼 - 인덱스는 쓰기성능이 안좋기 때문
  • 인덱스가 필요 없을 정도로 작은 테이블
  • 검색을 거의 안하는 컬럼

사용로그나 결제정보같이 인덱스를 만들기 안좋아도 필수적으로 사용해야 하는 경우는 최소한의 인덱스만 사용하는 것이 좋다.

postgreSQL에서 INDEX 만들기

인덱스 생성

CREATE INDEX new_index_name ON table_name (column_name); -- 일반 인덱스
CREATE UNIQUE INDEX new_index_name ON table_name (column_name); -- 유니크 인덱스
CREATE INDEX new_index_name ON table_name (column1, column2); -- 복합 인덱스

-- 정렬 방향도 지정 가능
CREATE INDEX new_index_name ON table_name (column1 ASC, column2 DESC); -- 복합 인덱스

유니크 인덱스?

중복값 허용하지 않는 인덱스

테이블 생성 시 컬럼이 중복 허용 상태였더라도, 이후에 UNIQUE 인덱스를 생성하면 그 시점부터 중복이 금지된다. 단, 이미 중복 데이터가 존재하면 UNIQUE 인덱스 생성은 실패한다.

복합인덱스?

두 값을 가지고 정렬. 만약 (nickname, age) 로 설정한다면 nickname먼저 정렬하고 nickname이 같으면 age로 정렬한다.

복합인덱스를 탈 때는 아래와 같이 왼쪽컬럼부터 사용해야 한다. (Leftmost Prefix Rule)

WHERE nickname = 'Kim';
WHERE nickname = 'Kim' AND age = 20;
WHERE nickname = 'Kim' AND age > 18;
WHERE age = 20; -- 잘 못 쓰는 경우

 

인덱스 제거

DROP INDEX index_name;

 

그 외 INDEX 관련 정보

  • primary key, unique 컬럼은 자동으로 인덱스 만들어진다.
  • foreign key는 자동생성x - 외래키는 값이 부모테이블에 존재하는지 검증을 위한거지 빠른검색용이 아니기 때문
  • 쿼리문이 실제로 어떤 INDEX를 사용하는지는 EXPLAIN으로 확인할 수 있다.

테이블의 인덱스 정보 확인하는 SQL

SELECT * FROM pg_indexes WHERE tablename = '테이블 이름';

 

 

추가

부분 인덱스

특정 조건에 맞는 데이터만 인덱싱하여, 불필요한 데이터의 인덱싱을 방지

CREATE INDEX index_name ON table_name (column_name) WHERE condition;

CREATE INDEX active_users_index ON users (email) WHERE status = 'active';

 

인덱스 힌트

SQL을 실행할 때 어떤 인덱스를 사용할지는 db의 옵티마이저가 결정한다. mysql에서는 사용자가 인덱스를 선택할 수 있는 인덱스 힌트 기능을 제공하지만 postgresql에서는 제공하지 않는다.

 

커버링 인덱스

조회하려는 데이터를 index가 모두 커버할 때 조회 성능이 더 빠르다.

예를 들어 (nickname, created_at) 튜플을 복합인덱스로 설정했는데 내가 nickname과 created_at을 찾는다면 굳이 실제 table까지 가지 않아도 index안에서 필요한 데이터를 가져올 수 있어서 성능이 더 빠른 것.

 

해시 인덱스

조회 시간복잡도 1

rehashing 부담 있음 - array로 만들어져있는데 꽉차면 새 공간 구해서 copy하니까 이럴 때 성능 부담.

equality ( = ) 비교만 가능. range 비교 불가능

복합인덱스 불가

CREATE INDEX idx_user_id_hash 
ON users USING hash (user_id);

'공부 > DB' 카테고리의 다른 글

postgreSQL Transaction 트랜잭션, ACID  (0) 2026.02.16
postgreSQL PRIMARY KEY, FOREIGN KEY  (0) 2026.02.16
postgreSQL JOIN  (0) 2026.02.15
관계형DB 정규화와 정규형  (0) 2026.02.14
DBeaver 사용하기  (0) 2026.02.13

+ Recent posts