Index는 데이터베이스 테이블의 검색 속도를 향상시키기 위해 사용되는 데이터 구조로 특정 column이나 column 집합에 대한 정렬된 참조를 제공하여 데이터를 빠르게 찾을 수 있게 한다.
하지만 Index를 위한 추가 공간이 필요하고, 데이터가 많이 있다면 생성에 많은 시간이 소요될 수 있다.
데이터 검색은 빨라지지만, INSERT, UPDATE, DELETE 작업은 상대적으로 느릴 수 있다. (Index 재배열)
MySQL에서 기본적으로 Clustered Index는 PK(Primary Key)에 적용되므로 Non-Clustered Index를 추가해보자.
프로젝트에서 OAuth로 로그인 후 OAuth 정보를 Users table에 저장한다. oauthId column에 index를 적용해 조회 성능을 향상시켜보자.
@Column()
또는 @Index()
decorator에 unique: true
option을 추가하면 된다.@Entity()
export class Users {
@PrimaryGeneratedColumn()
id: number;
⁝
@Column({ name: 'oauth_id' })
oauthId: string;
const { id: userId, oauthRefreshToken } = await this.usersService.findOne({ where: { oauthId } });
EXPLAIN ANALYZE
SELECT `Users`.`id` AS `Users_id`, `Users`.`nickname` AS `Users_nickname`, `Users`.`email` AS `Users_email`, `Users`.`profile_image_url` AS `Users_profile_image_url`, `Users`.`role` AS `Users_role`, `Users`.`main_dog_id` AS `Users_main_dog_id`, `Users`.`oauth_id` AS `Users_oauth_id`, `Users`.`oauth_access_token` AS `Users_oauth_access_token`, `Users`.`oauth_refresh_token` AS `Users_oauth_refresh_token`, `Users`.`refresh_token` AS `Users_refresh_token`, `Users`.`createdAt` AS `Users_createdAt` FROM `users` `Users` WHERE ((`Users`.`oauth_id` = '5176')) LIMIT 1;
-> Limit: 1 row(s) (cost=1051 rows=1) (actual time=3.8..3.8 rows=1 loops=1)
-> Filter: (Users.oauth_id = '5176') (cost=1051 rows=979) (actual time=3.8..3.8 rows=1 loops=1)
-> Table scan on Users (cost=1051 rows=9786) (actual time=0.0252..3.31 rows=5176 loops=1)
(Index 추가 및 select option으로 가져올 열 명시)
@Entity()
export class Users {
@PrimaryGeneratedColumn()
id: number;
⁝
@Column({ name: 'oauth_id', unique: true })
oauthId: string;
SHOW INDEX FROM users;
로 Index가 잘 추가된 것을 확인할 수 있다.
const { id: userId, oauthRefreshToken } = await this.usersService.findOne({
where: { oauthId },
select: ['id', 'oauthRefreshToken'],
});
EXPLAIN ANALYZE
SELECT `Users`.`id` AS `Users_id`, `Users`.`oauth_refresh_token` AS `Users_oauth_refresh_token` FROM `users` `Users` WHERE ((`Users`.`oauth_id` = '5176')) LIMIT 1;
-> Limit: 1 row(s) (cost=0..0 rows=1) (actual time=0.00112..0.00121 rows=1 loops=1)
-> Rows fetched before execution (cost=0..0 rows=1) (actual time=99e-6..99e-6 rows=1 loops=1)
항목 | 개선 전 | 개선 후 | 개선율 |
---|---|---|---|
실행 시간 | 3.8 ms | 0.00121 ms | 99.968% |
예상 비용 (cost) | 1051 | 0 | 100% |
스캔 행 수 | 5176 rows | 1 row | 99.981% |
oauth_id
에 UNIQUE
인덱스 추가로 인덱스를 사용하여 조회하고 불필요한 열 조회를 줄여 필요한 열만 선택한다.