Background Information
<래플 어플리케이션 시스템 '피라미드'>
다수의 유저들이 소장 가치가 높은 패션 아이템들을 래플 방식으로 구매할 수 있도록 도와주는 웹 어플리케이션입니다. 신뢰성있고 신속한 대응으로 만족스러운 유저 경험을 제공하는 '피라미드' 시스템 구축을 위한 실험 과정을 기록한 내용입니다.
Framework : nest.js
DB : AWS RDS postgres
Title
exp018_DB 인덱스 적용에 따른 래플 비딩 데이터 상세 조회 Latency 변화 관찰
Research Question
- 래플 비딩 데이터 상세 조회에 인덱싱을 적용하면 latency가 얼마나 개선될까?
Summary
bidSize = 270
조건 검색 쿼리의 성능 경우 인덱스 적용 시 API 요청 로직은 큰 변화가 없었다. 반면 bidSize > 250 and bidSize < 270
와 같은 범위 조건문의 경우 비즈니스 로직에 order by까지 포함되어 복잡도가 커서 인덱스의 성능 개선 효과 폭(61%)이 상대적으로 컸다.
Procedures
Data Recording & Analysis
특정 bidSize 조회
피라미드 시스템은 다양한 카테고리의 상품들을 지원할 수 있지만 VIP는 스니커즈 상품에 한정한다. bidSize는 유저가 입찰한 상품의 사이즈를 의미하는 프로퍼티다. 즉, 유저가 조던 검빨 신발 275을 입찰할 경우, 275가 바로 bidSize의 값에 해당하는 정보다.
bids.controller.ts
...
@Get(':size')
findBySize(@Param('size') size) {
this.logger.log('size logger is triggered');
return this.bidsService.findBySize(size);
}
...
}
bids.service.ts
...
async findBySize(size) {
const result = await this.bidRepository
.createQueryBuilder('bid')
.select()
.where('bid.bidSize = :size', { size: size})
.getMany();
return result;
}
...
인덱스 적용 전
테스트한 결과 404ms가 나왔다. 참고로 어플은 로컬, DB는 AWS RDS Postgres 연동했다.
-- DB SQL 스크립트
-- 특정 bidsize 조회.
explain analyze select * from "Bid" b where "bidSize" = 275;
QUERY PLAN |
--------------------------------------------------------------------------------------------------------------------------+
Gather (cost=1000.00..20693.11 rows=19384 width=48) (actual time=0.229..109.378 rows=18967 loops=1) |
Workers Planned: 2 |
Workers Launched: 2 |
-> Parallel Seq Scan on "Bid" b (cost=0.00..17754.71 rows=8077 width=48) (actual time=0.014..95.624 rows=6322 loops=3)|
Filter: ("bidSize" = 275) |
Rows Removed by Filter: 400059 |
Planning Time: 0.054 ms |
Execution Time: 110.848 ms |
DB SQL 스크립트에서 다이렉트로 요청 시 110ms가 소요된다.
인덱스 적용
인덱싱을 생성해보자.
-- bidsize 인덱스 생성
create index idx_bidsize on "Bid"("bidSize");
인덱스 생성 전에는 약 404ms가 소요되었던 반면 인덱스 생성 이후에는 250ms가 소요되었다. 인덱스 생성 이후 Latency가 150ms 더 빨라졌다.
-- DB SQL 스크립트
-- 특정 bidsize 조회.
explain analyze select * from "Bid" b where "bidSize" = 275;
QUERY PLAN |
----------------------------------------------------------------------------------------------------------------------------+
Bitmap Heap Scan on "Bid" b (cost=218.65..12243.19 rows=19384 width=48) (actual time=2.433..13.686 rows=18967 loops=1) |
Recheck Cond: ("bidSize" = 275) |
Heap Blocks: exact=9255 |
-> Bitmap Index Scan on idx_bidsize (cost=0.00..213.81 rows=19384 width=0) (actual time=1.218..1.218 rows=18967 loops=1)|
Index Cond: ("bidSize" = 275) |
Planning Time: 0.066 ms |
Execution Time: 14.420 ms |
DB 스크립트에서 직접 실행 시 약 14ms가 소요되는데 이는 인덱스 적용 전인 110ms의 약 10%에 해당하는 수준이다.
인덱스 적용 취소
혹시 인덱스를 다시 지우면 감소했던 만큼 동일하게 다시 Latency가 상승할까? 라는 의문이 들었다.
API 요청 결과의 경우, 놀랍게도 큰 변화가 없었다. 265~400ms 사이로 측정된다. DB스크립트로 직접 DB요청할 경우에는 다시 이전 100ms 대 속도로 Latency가 증가한다. 정리하자면 이렇다.
bidSize = 270
조건 검색 쿼리에 인덱스 적용에 따른 Latency 변화
- API 요청
- 인덱스 미적용 : 404ms
- 인덱스 적용 후 : 250ms(38% 감소)
- 인덱스 삭제 후 : 265 ~ 400ms
- DB 스크립트로 직접 요청
- -> 인덱스 미적용 : 110ms
- -> 인덱스 적용후 : 14ms (87% 감소)
- -> 인덱스 삭제 후 : 104ms
DB에 직접 요청할 경우 인덱스의 효과를 관찰하기가 수월하다. 하지만 레이어가 더 추가된 API 요청의 경우 노이즈로 인해 인덱스 적용으로 인한 성능 개선 효과를 뚜렷하게 관찰하기가 상대적으로 어려웠다.
bidSize 범위로 조회
그렇다면 bidSize = 275 대신 260 ~ 270과 같이 range를 조건으로 주면 어떻게 될까?
인덱스 적용 전
bids.service.ts
...
async findByRange(sizeFrom, sizeTo) {
const result = await this.bidRepository
.createQueryBuilder('bid')
.select()
.where('bid.bidSize > :sizeFrom', { sizeFrom: sizeFrom })
.andWhere('bid.bidSize < :sizeTo', { sizeTo: sizeTo })
.orderBy('bid.bidSize', 'ASC')
.take(100)
.getMany()
return result;
}
...
218ms이면 양호한 듯 하다.
-- DB SQL 스크립트
-- 특정 구간 사이즈 조회.
explain analyze select * from "Bid" b where "bidSize" > 250 and "bidSize" < 270;
QUERY PLAN |
----------------------------------------------------------------------------------------------------------------+
Seq Scan on "Bid" b (cost=0.00..29692.17 rows=354649 width=48) (actual time=0.011..131.516 rows=356067 loops=1)|
Filter: (("bidSize" > 250) AND ("bidSize" < 270)) |
Rows Removed by Filter: 863078 |
Planning Time: 0.048 ms |
Execution Time: 144.738 ms |
스크립트 상으로는 약 144ms가 소요되었다.
인덱스 적용 후
-- DB SQL 스크립트
-- bidsize 인덱스 생성
create index idx_bidsize on "Bid"("bidSize");
이전과 동일한 방식으로 bidSize에 인덱스를 생성해주었다.
적용 전 218ms에서 84ms으로 감소했다. 몇번 더 클릭하니 30ms 대로 감소하기도 하는데, DB 내에서 캐싱이 되어서 그런게 아닐까 생각한다.
-- DB SQL 스크립트
-- 특정 구간 사이즈 조회.
explain analyze select * from "Bid" b where "bidSize" > 250 and "bidSize" < 270;
QUERY PLAN |
---------------------------------------------------------------------------------------------------------------------------------+
Bitmap Heap Scan on "Bid" b (cost=4843.58..21568.31 rows=354649 width=48) (actual time=12.550..90.067 rows=356067 loops=1) |
Recheck Cond: (("bidSize" > 250) AND ("bidSize" < 270)) |
Heap Blocks: exact=11405 |
-> Bitmap Index Scan on idx_bidsize (cost=0.00..4754.92 rows=354649 width=0) (actual time=11.041..11.042 rows=356067 loops=1)|
Index Cond: (("bidSize" > 250) AND ("bidSize" < 270)) |
Planning Time: 0.068 ms |
Execution Time: 103.655 ms |
스크립트 상으로도 144ms에서 103ms으로 감소했다.
정리하면 이렇다.
bidSize > 250 and bidSize < 270
조건 검색 쿼리에 인덱스 적용에 따른 Latency 변화
- API 요청
- 인덱스 미적용 : 218ms
- 인덱스 적용 후 : 84ms(61% 감소)
- DB 스크립트로 직접 요청
- -> 인덱스 미적용 : 144ms
- -> 인덱스 적용후 : 103ms (28% 감소)
Discussion
Conclusion
bidSize = 270
조건 검색 쿼리에 인덱스 적용에 따른 Latency 변화
- API 요청
- 인덱스 미적용 : 404ms
- 인덱스 적용 후 : 250ms(38% 감소)
- 인덱스 삭제 후 : 265 ~ 400ms
- DB 스크립트로 직접 요청
- -> 인덱스 미적용 : 110ms
- -> 인덱스 적용후 : 14ms (87% 감소)
- -> 인덱스 삭제 후 : 104ms
bidSize > 250 and bidSize < 270
조건 검색 쿼리에 인덱스 적용에 따른 Latency 변화
- API 요청
- 인덱스 미적용 : 218ms
- 인덱스 적용 후 : 84ms(61% 감소)
- DB 스크립트로 직접 요청
- -> 인덱스 미적용 : 144ms
- -> 인덱스 적용후 : 103ms (28% 감소)
위에서 정리한 데이터를 한 곳에 모았다.
bidSize = 270
조건 검색 쿼리의 성능 경우 인덱스 적용 시 API 요청 로직은 약 38% 개선된 반면 DB 스크립트 상에서는 87% 개선되었다. 다만, 인덱스를 삭제하고 재실험 시 API 요청은 큰 변동이 없다는 점에서 성능 개선 결과를 신뢰하기 어렵다.
bidSize > 250 and bidSize < 270
와 같이 범위 조건 검색 쿼리의 성능으 ㅣ경우, 인덱스 적용 시 API 요청은 약 61%로 개선 폭이 컸던 반면 DB 스크립트는 28% 감소했다. 범위 조건문의 경우 비즈니스 로직에 order by까지 포함되어 복잡도가 크기 때문에 인덱스의 성능 개선 효과가 더 커진 것이 아닐까 추측된다.
실제 서버에서 테스트
local환경에서 테스트해보았다면 이제 실제 서버에서 테스트해보자.
bidSize = 260
조건 테스트
인덱스 생성 전 : 669ms
인덱스 생성 후 : 470ms
- 인덱스 생성 전 : 669ms
- 인덱스 생성 후 : 470ms
- 인덱스 제거 후 : 500ms
인덱스를 생성 후 latency가 669ms에서 470ms로 약 29% 감소했다. 하지만 인덱스를 제거해도 큰 변화가 없었다. 왜 인덱스를 적용할 때는 소폭의 감소가 있었는데, 인덱스를 제거한 뒤에도 큰 변화가 없다는 점이 조금 이상하다.
조건문이 너무 단순해서 그런 것일까? 데이터가 100만개 수준 밖에 되지 않아서일까?
복잡도가 좀 더 높은 로직을 테스트해보자.
bidSize > 250 and bidSize < 270
조건 테스트
인덱스 적용 전 344ms
인덱스 적용 후 24msbidSize > 250 and bidSize < 270
성능 변화
- 인덱스 적용 전 : 344ms
- 인덱스 적용 후 : 24ms
- 인덱스 제거 후 : 365ms
빠르다. 93% 개선되었다. 혹시 몰라서 인덱스 제거를 하고 테스트해보니 다니 10배 수준으로 Latency가 증가했다. bidSize에 대한 조건들로 인한 복잡도가 증가하는 만큼 인덱스로 인한 성능의 개선 효과가 증가한다.
Reference
Appendix
'Log > Experiment' 카테고리의 다른 글
어플리케이션 시스템의 목표 트래픽 기준 설정하기 (1) | 2023.03.10 |
---|---|
exp017_nGrinder script 수정 후 VUser-게시글 성공률 상관관계 관찰 재실험(No ramp-up) (0) | 2023.03.08 |
exp016_VUser 수에 따른 게시글 생성 성공률 변화 관찰 (0) | 2023.03.08 |
exp015_Nginx 적용 여부에 따른 게시글 생성 요청 성공률 변화 관찰 (0) | 2023.03.06 |
exp014_DB Connection Pool 수 변경 시에 따른 성능 변화 추가 실험 (0) | 2023.03.06 |
댓글