검색 성능 개선을 해오면서 고민했던 기록들을 정리해보았습니다.
📔 테스트 항목
- 검색 기능
- 가게 이름, 도로명 주소, 업종
- 가게 이름, 도로명 주소, 업종 평점n점 이상 검색
- 가게 이름, 도로명 주소, 업종 리뷰n개 이상 검색
- 가게 이름, 도로명 주소, 업종 평점 높은순 검색
- 가게 이름, 도로명 주소, 업종 리뷰 많은순 검색
- 약 200만건의 데이터에 대한 검색 성능 개선 (1초 이내)
- 개선율 1000% 이상 달성
- 검색 정확도 향상
검색 기능 ( 가게 이름, 도로명 주소, 업종)
- 각 카테고리별 평점n점 이상 검색
- 각 카테고리별 리뷰n개 이상 검색
- 각 카테고리별 평점 높은순 검색
- 각 카테고리별 리뷰 많은순 검색
성능 개선에 대한 기술적인 시도들
- full scan
- B-TREE Index 색인 검색
- covering Index
- composite Index(복합 인덱스)
- full-text Index
- full-text Index + composite Index
- ElasticSearch 도입
성능 개선을 위해 도입한 기술스택
- JPA findBy Method
- Native Query
- QueryDsl
- DynamicSQL
🏁 성능 개선 목표
🙋🏻 컴퓨웨어(Compuware)의 웹 성능 사업부에 근무하는 고메즈(Gomez)에 따르면
사용자의 40%가 **3초**동안 기다리게 되면 사이트를 **이탈**하는 것으로 나타났다고 하였으며
월마트는 페이지 속도를 100ms까지 개선하면 **수익이 1% 증가**한다는 사실을 발견했다고 합니다.
- 약 200만건의 데이터에 대한 검색 성능 개선 (1초 이내)
- 개선율 95% 이상 달성
- 검색 정확도 향상
✅ 기술적 선택
기술적인 시도
- full scan
- B-TREE Index 색인 검색
- covering Index
- composite Index(복합 인덱스)
- full-text Index
- full-text Index + composite Index
- ElasticSearch 도입
성능 개선을 위해 도입한 기술스택
- JPA findBy Method
- Native Query
- QueryDsl
- DynamicSQL
💪🏻 성능 개선
1. Full Scan
개요 :
- 성능개선 후 데이터 비교를 위해 스토어 정보(약 200만건)의 데이터 검색 시 성능 측정
테스트 결과 :
👉 최소 : 1.488초 , 최대 : 4.142초 ⇒ 목표치까지 많은 성능 개선이 필요하다!!
2. B-Tree Index
- 도입 이유 :
- 현재 프로젝트에 적용된 QueryDsl을 기반으로한 동적쿼리는 DB에 Full-Scan이 발생하여 데이터 수가 늘어날 경우 조회시간이 길어진다.
- 속도 개선을 위해 책처럼 색인을 추가할 수 있는 Index 기능을 적용함
- 자세한 내용 👇🏻
- 테스트 결과 :
- 적용 전 :
- 1.45초 소요
- 적용 후 :
- 0.02초 소요
- ⇒ 약 98.62% 검색 소요 시간 단축
- 성능 측정 결과 :
→ 검색 시간은 98.62% 단축되었으나, Index 적용 시 데이터 실효성 부분에서 문제가 발생하였다.
문제점 발생 🚨
1. 도로명 주소를 기준으로한 검색 부분에서 문제 발생
- 문제 상황 :
- “용산구”를 기준으로 한 검색 시 데이터를 찾지 못하는 문제가 발생
실제로 DB에서는 용산구 데이터가 존재하지만, Index 적용한 쿼리로 조회시 데이터를 찾을 수 없었음
- %Like%, %Like 쿼리로 검색 시 데이터가 있는 것으로 확인할 수 있었다.
- 문제 발생 이유 :
- B-TREE 기반 Index 방식의 단점으로 첫글자를 기준으로 인덱싱 처리를 하기때문
- 용산구 라는 키워드는 인덱스를 탈 수가 없어서 발생한 문제
- %Like% 쿼리와 %Like 쿼리를 인덱스로 검색할 수 있는 방법은 없다.
위 사진과 같이 “용산구%”만이 인덱스를 적용받았던 것을 확인 할 수 있다.
2. 같은 인덱스의 키워드만 다르게 했을 경우 시간 차가 있었다.
조회하는 데이터의 갯수가 너무 많기 때문에 시간차가 발생했다.
이 두가지 키워드 모두 인덱스를 적용받았지만
한식 22만1851건
일식 1만7512건
약 20배가 넘는 차이로 그만큼 인덱스를 적용받아 검색하는 시간도 증가하였다.
이는 다른 부분들도 공통으로 모두 적용되었다.
3. Covering Index
- 도입 이유 :
- 검색 성능 개선을 위해 Covering Index의 도입을 고려
- 인덱싱 처리 시 데이터로 접근하는 주소값을 Value로 가지게 되는데, Value에 주소값 외 데이터도 저장한다면 데이터로 접근하는 절차를 줄여서 속도를 개선할 수 있을 것 같아서 적용함
- 테스트 결과 :
- 적용 전 :
2.45초 소요
- 적용 후 :
0.49초 소요
⇒ 약 80% 검색 소요 시간 단축
성능 측정 결과 :
→ 검색 소요시간은 단축되었으나, Covering Index를 사용할 경우 아래와 같은 문제가 있어 미적용
미적용 사유 🔻:
- 우리 프로젝트의 경우 검색 시 여러개의 컬럼 값들이 필요한데 Covering Index를 사용할 경우 현재 테스트 기준은 한가지의 컬럼 값 만을 가져왔을때의 결과이다
- 만약에 우리가 가진 여러개의 컬럼값들을 모두 가지고 오게 될 경우 Index로 처리하는것과 성능 차이가 없어 미적용 하였다
4. Composite Index
- 도입 이유 :
- 검색 성능 개선을 위해 Composite Index의 도입을 고려
- 2개 이상의 컬럼으로 구성 된 인덱스로 단일 인덱스를 능가하는 성능을 낼 수 있다.
- 테스트 결과 :
- 적용 전 (index 만 적용) :
0.54초 소요
- 적용 후 :
0.46초 소요
⇒ 약 14% 검색 소요 시간 단축
⇒ 시간이 단축되었지만 속도 차이가 미비함
왜 성능 차이가 미미할까?
복합 인덱스 미적용 Explain
Using where : full scan(index 미적용)
복합 인덱스 적용 Explain
Using index condition : Index 적용
🙋🏻 복합 인덱스가 적용되어 있는데도 왜 미미할까?
- 복합 인덱스를 적용한 별점(star_score), 리뷰갯수(review_cnt) 부분이 double/ int Type이기 때문이다.
- B-TREE Index 특성상 앞글자를 기준으로 index 처리를 하기 때문에 숫자의 범위가 넓으면 넓을수록 scan해야할 영역이 늘어나게 되고 결국 full scan과 비슷한 성능을 낼 수 밖에 없다.
복합인덱스가 적용되어 있음에도 불구하고 Full-Scan이 발생하기도 한다?
- 복합인덱스가 적용되어 있는 인덱스 임에도 불구하고 full scan이 발생했다.
- ⇒ 이는 mysql의 optimizer에 의해 최선의 결과를 낼 수 있는 방식으로 실행을 시켜 준것 !
🙋🏻 만약 강제로 복합 인덱스로 타게 한다면?
Full Scan 발생 시 : 3.22초 소요
- 복합 인덱스 강제 실행 : 3.56초 소요
- 복합인덱스를 실행하는 것이 응답이 더 오래걸리는 것을 볼 수 있다.. !
- 성능 측정 결과 :
5. Full-Text Index
- 도입 이유 :
- 검색 정확도 개선을 위해 Full-Text Index 도입을 고려
- ( BTree-Index는 데이터 실효성 측면에서 사용이 어려움 )
- 테스트 결과 :
- 1차 테스트 결과 비교:
Full-Scan
3072건 / 1.45초 소요
B-Tree Index
26건 / 0.02초 소요
Full-Text Index 검색
346건 / 0.03초 소요
⇒ 테스트 결과 : Full Scan > Full Text Index > Index
왜 Full-Scan보다 결과가 적게 나왔을까?
- 이유는 Stop-word parser와 ngram parser 때문 !
- Stop-word parser 방식은 공백을 기준으로 인덱싱을 하는데, 한글의 특성을 생각 했을 때 적합한 방식은 Ngram 방식일 것 같아서 ngram방식으로 리인덱싱을 하였음
- 2차 테스트 결과 비교:
Full-Scan
Full-Text Index(Stop-word) 검색
Full-Text Index(Ngram) 검색
⇒ 테스트 결과 : Stop-word parser > Full Scan > ngram parser
왜 Full-Scan보다 결과가 많이 나왔을까?
- ngram parser를 적용하여 “순대국”뿐만 아니라 순대라는 단어까지 포함되었기 때문이다.
- ngram은 최소 2글자로 잘라서 인덱싱을하고, 자연어 모드의 검색을 이용하였다.
🔎 Full-text Index의 검색 모드
- Boolean Mode 검색을 이용한 검색 결과🔻
Full Scan
Full-Text index ngram BooleanMode
⇒ Boolean 모드로 검색할 경우 동일한 갯수의 데이터를 리턴하지만 속도는 향상된것을 볼 수 있다.
그럼 다 ngram parser를 적용하는것이 좋을까?
⇒ 아니다! 프로젝트에서 도로명 주소에 ngram parser를 적용하였을 때 오히려 검색 정확도가 감소했다.
⇒ 이유는 용산구를 검색할때 “용산”, “산구” 처럼 모두 검색이 되어서 유사한 다른 주소까지 나오면서 정확도가 감소하였다.
(ngram으로 인덱싱된 full-text index에서 용산구로 조회한 결과 창원시의 성산구까지 나오는것을 확인했다.)
⇒ 도로명은 stop-word parser의 적용이 필요!
✨ 적용 후 검색 속도 및 정확도가 상승 하였다.
- 성능 측정 결과 :
→ 다른 항목들은 검색 성능이 개선되었지만, “업종” 부분에서 추가적으로 개선이 더 필요
6. Full-text Index + Composite Index
- 도입 이유 :
- Full-Text Index를 사용 하였지만, 업종부분에서는 성능 개선이 되지 않았음.
- ⇒ “한식” 키워드로 검색할 경우 조회되는 데이터량이 많고 중복되는 데이터가 많기 때문에 업종 관련 검색에 대해서는 복합 인덱스를 사용하는것이 좋을 것 같다고 판단.
- 테스트 결과 :
- Full-Text Index 사용
리뷰(1000~10000개) : 1.06초 소요 / 별점(4점 ~ 5점) : 1.43초 소요
Composite Index 사용
리뷰(1000~10000개) : 0.56초 소요 / 별점(4점 ~ 5점) : 0.44초 소요
- 성능 측정 결과 :
→ 하지만 평점 높은순 및 리뷰 많은순에 대해서는 복합인덱스로도 해결되지 않았다 🤔
7. Full-Text Index + Composite Index + Limit
- 도입 이유 :
- 프로젝트 특성상 상위 10개의 검색결과 값을 받아서 지도에 마커를 찍어서 사용자에게 보여주게 되는데 Limit을 걸어서 검색 성능을 높이는 방법 또한 고려하였다.
- 테스트 결과 :
- Limit X 업종별 리뷰 높은 순
3.21초 소요
- Limit O 업종별 리뷰 높은 순
- 0.29초 소요
- ⇒ 약 90.97% 검색 소요 시간 단축
Full-Text Index 방식으로는 개선할 수 없을까?
결론부터 얘기하자면, 어려움이 있는것을 확인하였다!
full-text index Limit X
full-text index Limit O
* 이전에 Limit을 걸기 전보다 개선되었지만 유효한 수치는 얻지 못했다.
8. Elastic Search
- 도입 이유 :
- Full-Text Index를 통해 어느정도 사용할 수 있는 수준의 검색 소요시간과 정확도를 확보하였으나 한글 주소를 검색 하였을 때 문제가 발생하여서 Elastic Search 도입을 고려함
🙋🏻 어떤 문제가 발생했나요?
주소 검색 시 Stop-word Parser와 ngram Parser의 문제
현재 가게 이름은 Ngram parser, 도로명 주소는 stop-word 방식으로 인덱싱 되어있다.
현 상태에서 검색 키워드를 “화곡”으로 검색한다면
⇒ 화곡동으로 검색 시 11427건의 데이터가 있는것을 확인할 수 있음
⇒ 이때, 키워드를 “화곡”으로 검색한다면 어떠한 검색 결과도 얻을 수 없다.
이유는 주소는 Stop-word 인덱싱 방식이기 때문에 “화곡동”이라는 키워드로만 검색을 할 수 있다.
⇒ 해당 인덱싱 방식에서는 “화곡동”과 “화곡”은 다른 단어가 된다.
“강서구 화곡동 화곡빌딩”을 인덱싱 한다면?
stop-word ⇒ 강서구 / 화곡동 / 화곡빌딩
ngram ⇒ 강서 / 서구 / 구화 / 화곡/ 곡동 / 동화 / 화곡 / 곡빌 / 빌딩
하지만 위에서 알아보았던 것 처럼 도로명주소를 “ngram”방식으로 인덱싱 한다면,
”용산구”를 검색하였을 때 아래와 같이 잘못된 키워드를 받게 될 가능성이 있다. 😅
그럼 어떻게 해결 할 수 있을까?
- 주소 검색을 위해서는 “화곡동”이라는 정보를 얻기 위해서는 “화곡동”이라는 키워드도 쓸 수 있고, ”화곡”이라는 키워드도 쓸 수 있어야 한다.
- 위의 두 키워드를 동시에 사용하려면 Stop / ngram 인덱싱 방식을 동시에 사용해야함
- ⇒ 이러한 문제를 해결하기 위해 Elastic Search의 역색인 방식을 적용 하였다.
Elastic Search를 이용해 인덱싱 한다면?
stop-word ⇒ 강서구 / 화곡동 / 화곡빌딩
ngram ⇒ 강서 / 서구 / 구화 / 화곡/ 곡동 / 동화 / 화곡 / 곡빌 / 빌딩
역색인 ⇒ 강서구 / 화곡동 / 화곡빌딩 / 강서 / 서구 / 구화 / 화곡 / 곡동 / 동화 / 화곡 / 곡빌 / 빌딩
* stop-word 방식과 ngram에 대한 부분 모두 대비된 채로 인덱싱이 되었다!
테스트 결과 :
“화곡” 키워드 Elastic Search에 검색 시
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 11,
"relation" : "eq"
},
"max_score" : 11.055591,
"hits" : [
{
"road_name_address" : "서울특별시 강서구 화곡로25길 25, 1층 (화곡동, 화곡 3동)",
"road_name_address" : "서울특별시 강서구 강서로 191, 지하1층 (화곡동, 화곡 3동)",
"road_name_address" : "서울특별시 강서구 곰달래로16길 29, 1층 (화곡동, 화곡 1동 )",
"road_name_address" : "서울특별시 강서구 화곡로25길 25, 1층 (화곡동, 화곡 3동)",
"road_name_address" : "서울특별시 강서구 강서로 145, 1층 101호 (화곡동, 보눔 하우스 화곡)",
"road_name_address" : "서울특별시 강서구 화곡로 286-4, 화곡 6동 지하 1층 (화곡동)",
"road_name_address" : "서울특별시 강서구 화곡로 286-4, 화곡 6동 지하 1층 (화곡동)",
"road_name_address" : "서울특별시 강서구 강서로 27, 화곡 심포니 타워 4층 401호 (화곡동)",
"road_name_address" : "서울특별시 강서구 강서로 145, 1층 101호 (화곡동, 보눔 하우스 화곡)",
"road_name_address" : "서울특별시 강서구 강서로 145, 1층 102호 (화곡동, 보눔 하우스 화곡)",
}
}
⇒ 원하는 방식대로 “화곡”이라는 키워드로 “화곡”과 “화곡동”의 정보를 다 얻을 수 있게 되었다.!
성능 측정 결과 :
⇒ 검색 성능 개선이 어려웠던 업종 부분의 검색 소요시간이 78%로 단축 된 것을 확인할 수 있다.
⇒ FullScan과 비교시 최소 -95.44%, 최대 -98.68%의 검색 소요시간을 단축하였다.
🔻 테스트 리포트 🔻
https://docs.google.com/spreadsheets/d/1m5xCjN74SXanhTsfDbwBwHEw8OLao8oJ/edit#gid=1365463729
[Pin-Table] 검색 성능 개선 테스트_V1.2.xlsx
검색 성능 개선 결과 55555555,* 작성자 : 이상훈 Pin-Table 검색 성능 보고서 NO,구분,키워드 검색,키워드 + 평점(4~5점),키워드 + 평점 높은 순,키워드 + 리뷰(1000개 이상),키워드 + 리뷰 높은 순,비교 순
docs.google.com
'legacy > Pin-Table 성능 개선 기록' 카테고리의 다른 글
검색 성능 개선 #16 ElasticSearch 적용 (0) | 2023.02.26 |
---|---|
검색 성능 개선 #15 Full-text index VS ElasticSearch (0) | 2023.02.26 |
검색 성능 개선 #14 Full-Text Index + Composite Index limit (0) | 2023.02.26 |
검색 성능 개선 #13 Full-Text Index + Composite Index 적용 (0) | 2023.02.26 |
검색 성능 개선 #12 Stop-word / ngram parser (0) | 2023.02.26 |