0. 주어진 조건
- MySQL의 한 테이블에 1000만 건 정도의 데이터가 있다. 이 데이터는 도서 데이터이다.
- 첵 제목, 작가, 출판사, 출판일자, 대중소 분류 등에 대한 정보가 한 row에 담겨 있다.
- 결과론 적으로 말하면 ElasticSearch를 이용해서 원하는 정보를 1초 안에 검색할 수 있게 만들었다. 하지만 MySQL로만 시행했을 때 어떻게 하면 검색 시간을 단축시킬 수 있을까?
- 단순히 like 검색 (ex %총균쇠%)와 같은 방식은 3분을 초과해서 아래 언급하지 않겠다.
- 그렇다면 사실 남은 방법은 인덱싱이 가장 유력하다.
1. 인덱스를 활용해 보기전에 인덱스란 무엇인가
- MySQL에서는 2 종류의 인덱스가 있다.
- 클러스터드 인덱스 : 우리가 영어 사전을 찾을 때 Elephant를 찾는다면 E가 시작되는 페이지로 간다. 이와 같이 어떤 기준에 따라서 순차적으로 정렬되어 있어 찾기 편하도록 해주는 것이 클러스터드 인덱스이다.
- 주로 PK(Primary Key)라고 불리며 테이블 당 1개만 생성할 수 있다.
- 주로 ID 형태로 사용하며, 설정하기에 따라서 다른 값으로 지정할 수 있다.
- 논 클러스터드 인덱스 : Secondary Index라고도 불리며, 일반적인 책의 '찾아보기' 페이지와 같다고 표현할 수 있다. 네트워크 책에서 표현 계층에 대한 내용이 갑자기 궁금해졌다. 목차에가서 OSI 계층을 찾아 표현 계층 챕터를 찾을 수 도 있지만, 그냥 책 뒤의 '찾아보기'에서 "표현 계층"이 몇 페이지에 있는지 찾는것이 더 쉽다.
- 클러스터드 인덱스 외에 내가 원하는 칼럼에 인덱스를 만들 수 있다.
- 간단히 요약하면 ㄱ~ㅎ 순서로 책 제목을 정렬하고 '총균쇠'는 xxx 번째 row에 있다고 기록해 두는 것이다.
- 당연하게도 중간에 삽입 혹은 삭제가 일어난다면 인덱스도 수정해줘야해서 인덱스를 여러 칼럼에 설정할 수 록 DB 사용에 부하가 늘어날 수 밖에 없다.
- 인덱스에는 키 값과 해당 키 값이 위치한 데이터의 주소가 포함된다.
- 클러스터드 인덱스 : 우리가 영어 사전을 찾을 때 Elephant를 찾는다면 E가 시작되는 페이지로 간다. 이와 같이 어떤 기준에 따라서 순차적으로 정렬되어 있어 찾기 편하도록 해주는 것이 클러스터드 인덱스이다.
Non-Clustered Index에는 어떤 것들이 있을까?
0) B-tree Index
- 하나의 열을 기준으로 정렬된 B-tree 구조를 생성한다.
- 인덱스 노드는 열의 값과 해당 행의 기본 키를 포함한다.
<데이터> Row 1: Accounting Row 2: Engineering Row 3 : Marketing |
<저장된 인덱스> 'Accounting' -> id: 1 'Engineering' -> id: 2 'Marketing' -> id: 3 |
1) Covering Index
- 인덱스에 필요한 모든 열을 포함하고 있어, 인덱스만으로 쿼리 처리 가능
- 데이터 페이지에 접근하지 않고, 인덱스 페이지에서 필요한 정보를 모두 얻을 수 있어 쿼리 성능이 매우 빠름
- 주로 읽기 성능에 최적화
- 예시
- 테이블: employees
- 인덱스 열: department_id, last_name, first_name
- 작동 : department_id = 10 쿼리를 실행하면, 인덱스에서 10을 찾고 해당 데이터(Smith, John)를 바로 반환합니다. 데이터 페이지 접근이 필요 없다.
<데이터> Row 1: 10, Smith, John Row 2: 20, Johnson, Emily |
<저장된 인덱스> 10 -> Smith, John (Row 1) 20 -> Johnson, Emily (Row 2) |
2) Unique Index
- 인덱스에 적용된 열의 모든 값이 고유하도록 강제
- 중복 데이터를 방지하는데 사용
3) Full-text Index (풀텍스트 인덱스)
- 문자열 데이터에 대해 단어 단위로 검색할 수 있도록 설계된 인덱스
- 텍스트 검색을 최적화 하기 위해 사용
- LIKE 연산다 보다 더 효율적인 전체 텍스트 검색 수행
- 주로 검색 엔진이나 대량의 텍스트 데이터가 있는 경우에 사용
- 예시
- 테이블: articles
- 인덱스 열: content
- 작동 : MATCH(content) AGAINST('korea') 쿼리를 실행하면, 인덱스에서 'korea'를 찾고 Row 1과 Row 2를 반환합니다.
<데이터>
Row 1: "voice of korea"
Row 2: "korea is beautiful"<저장된 인덱스>
'voice' -> Row 1
'of' -> Row 1
'korea' -> Row 1, Row 2
'is' -> Row 2
'beautiful' -> Row 2
- 함정
- 만약 위 예시에서 MATCH(content) AGAINST('re') 를 수행한다면?
- re 라는 단어가 인덱스에 없기 때문에 검색이 안된다. 영어는 단어 하나하나가 띄어쓰기 되어 있어서 오타를 제외하고는 그 단어에서 알파벳 몇개를 빼버린다면 그 단어가 아니게 된다.
- 반면에 한글은 어떨까?
- 만약 데이터가 "대한민국의 수도는 서울입니다"이고 이것을 풀텍스트인덱싱 했을때 [대한민국의, 수도는, 서울입니다]로 인덱싱 될것이다. 하지만 누가 "대한민국의"로 찾겠는가
- 한글의 경우에는 형태소 분석기를 활용하거나 N-gram 인덱싱을 고려해야한다. 더 나아가서는 ElasticSearch에서 해결해야한다. 하지만 이 부분은 검색 속도가 아니라 검색 성능에 대한 얘기라는 것을 참고 하자.
- 만약 위 예시에서 MATCH(content) AGAINST('re') 를 수행한다면?
4) Filtered Index
- 특정 조건에 맞는 행만 인덱싱하여 부분 인덱스를 생성
- 전체 테이블이 아닌 필터도니 행 집합만 인덱싱하여 디스크 공간 절약 및 인덱스 유지 비용 감소 시킴
- 자주 조회되는 특정 조건에 대해 쿼리 성능을 최적화 한다
- 예시
- 테이블: employees
- 인덱스 조건: department_id = 10
- 작동 : department_id = 10 쿼리를 실행하면, 인덱스에서 10을 찾고 Row 1의 데이터를 반환
<데이터>
Row 1: department_id = 10, last_name = 'Smith'
Row 2: department_id = 20, last_name = 'Johnson'<저장된 인덱스>
'10 -> Smith (Row 1)
5) Spatial Index
- 공간 데이터(좌표, 지도 데이터) 처리를 최적화하기 위한 인덱스
- GIS(Geographic Information Systems) 데이터베이스에서 사용
- 거리, 근접성 등을 기준으로 데이터를 검색하는 데 최적화되어 있다.
- 예시
- 테이블: locations
- 인덱스 열: location
- 작동 : ST_Distance(location, ST_MakePoint(40.7128, -74.0060)) < 10 쿼리를 실행하면, 인덱스에서 해당 위치와 가까운 좌표를 반환
<데이터>
Row 1: (40.7128, -74.0060) // New York
Row 2: (34.0522, -118.2437) // Los Angeles<저장된 인덱스>
(40.7128, -74.0060) -> Row 1
(34.0522, -118.2437) -> Row 2
그렇다면 하나의 컬럼에 여러 인덱스를 만들면 어떻게 되나?
결론은 하나의 쿼리에 한 인덱스만 사용한다.
책 테이블에서 제목에 풀텍스트 인덱스, 커버링 인덱스, 필터드 인덱스 등등을 만들었다고 하자.
그렇다면 DB가 똑똑하게 3 인덱스를 동시에 돌려서 제일 먼저 나오는 값을 뱉어 주나??
아니다. DB는 인덱스를 하나만 이용한다.
따라서 여러 인덱스를 만들어봤자 쓰기/수정/삭제에서 성능만 안좋아진다.
주의 : 하나의 쿼리에 한 인덱스만 사용한다는 것은
SELECT * FROM employees WHERE last_name = 'Smith' AND department_id = 10;
에서 last_name에서만 인덱스를 타나요?! 라는게 아니라 last_name과 department 각각 인덱스 조회를 하고
last_name에서 인덱스를 2개 이상 사용해서 조회하지 않는 다는 뜻이다.
2. 검색 결과를 확인해보자.
- MySQL에서 2개의 인덱스로 실험했다.
- 로컬에서 실험한 값이여서 AWS 무료버전 RDB를 사용했을 때 보다는 빠르다.
- 참고로 AWS RDS를 이용했을 때는 인덱스를 이용해도 최소 10초 이상은 소요했다.
0) 인덱스가 없을 때
- 예시 쿼리 : SELECT * FROM book WHERE title LIKE '%<검색어>%';
- 결과
- 운동 : 28s
- 경제 : 26s
1) B-tree Index
- CREATE INDEX idx_title_btree ON book(title);
- 예시 쿼리 : SELECT * FROM book WHERE title LIKE '<검색어>%';
- B-tree 인덱스는 %검색어% 조회 시 인덱스를 타지 않는다. 그래서 뒤에만 %를 붙여야한다.
- 결과
- 운동 : 0.063s
- 경제 : 0.0188s
2) Full-text Index
- CREATE FULLTEXT INDEX idx_title_fulltext ON book(title);
- 예시 쿼리 : SELECT * FROM book WHERE MATCH(title) AGAINST('운동');
- full-text 인덱스는 LIKE 조회 시 인덱스를 타지 않는다. 그래서 MATCH와 AGAINST를 이용해야한다.
- 결과
- 운동 : 0.063s
- 경제 : 0.0188s
3) 결론
- 확실히 인덱스를 타게 만들었을 때는 결과가 매우 빨라졌다.
- 하지만 AWS의 RDS를 이용해서 서비스한기엔 성능이 부족했다.
- 결국 ElasticSearch 서버를 새롭게 구축해서 해결하였다.
- 앞서 언급하기도 했지만 Paser에 따라서 검색 성능이 달라지고, 사용자가 사용하기에 적절한 검색 서비스를 구축하기 위해서라도 ElasticSearch를 사용할 수 밖에 없었다.
3. 그렇다면 왜 ElastciSearch는 빠른가?
0) 엘라스틱 서치의 특징과 단점
- 특징
- 분산 시스템 : 데이터를 여러 노드에 분산하여 저장하여, 데이터 양이 증가해도 처리 속도가 유지된다. 데이터는 자동으로 분할되어 여러 서버에 분산 저장되며, 이를 통해 높은 가용성과 확장성을 보장
- 전문(Full-Text) 검색 : Lucene의 강력한 검색 기능을 활용하여 전문 검색을 지원한다. 사용자가 특정 단어나 문구를 검색하면 관련도가 높은 문서를 빠르게 찾아낼 수 있다.
- 다양한 데이터 처리 기능 : 데이터의 색인 생성, 검색, 분석 등 다양한 기능 제공
- 실시간 검색 : 저장된 데이터에 대해 거의 실시간으로 검색과 분석을 수행할 수 있다.
- RESTful API : RESTful API를 제공하여 다양한 프로그래밍 언어로 된 애플리케이션에서 쉽게 사용할 수 있다.
- 단점
- 리소스 요구사항 : 고성능을 제공하는 만큼 상당한 양의 시스템 리소스를 필요로 한다. 충분한 메모리와 CPU가 동반되어야 성능을 보장한다.
- 관리의 복잡성 : 분산 시스템이기에 클러스터 관리, 데이터 샤딩, 복제 등을 올바르게 설정하고 관리하는게 중요하다.
- 리소스와 이어지며, 보통 자체 서버로 작동하여 별도의 비용을 사용하게 된다.
- 보안 고려 사항 : 기본적으로 보안 설정이 비활성화되어, 직접 추가해야한다.
- 업그레이드의 어려움 : 버전간 호환성 문제를 유발하여, 데이터 마이그레이션 또는 인덱스 재구축을 해야할 필요가 생길 수 있다.
- 쿼리 언어의 복잡성 : 쿼리 언어가 강력하고 유연하지만, 복잡한 쿼리를 작성하고 최적화하는 데는 학습 곡선이 있다.
1) 엘라스틱 서치의 어떤 기술들이 속도와 성능을 만들어 주었을까? (특징과 연관됨)
- 역 인덱스
- 문서에 포함된 모든 단어를 키로 하고, 해당 단어가 포함된 문서의 리스트를 값으로 저장하는 방식
- MySQL의 B-tree 인덱스는 일반적으로 데이터의 정렬과 범위 검색에 적합하지만, 복잡한 텍스트 검색에서는 효율이 떨어진다.
-
Elasticsearch (Inverted Index)
운동 -> Doc1, Doc2, Doc3
건강 -> Doc1, Doc4
좋다 -> Doc1, Doc3
Query: "운동"
-> Doc1, Doc2, Doc3
(Fast lookup using inverted index)MySQL (B-tree Index)
Document1: "운동은 건강에 좋다"
Document2: "운동을 시작하다"
Document3: "좋은 운동을 선택하다"
Query: "운동"
-> Full scan or partial scan
(Slow due to sequential search)
- 분산 아키텍처
- 분산 시스템으로 설계되어, 데이터를 여러 노드에 분산 저장하고, 검색을 병렬로 처리한다. 덕분에 대량의 데이터를 빠르게 검색하고 처리할 수있다.
- 반면에 MySQL은 단일 노드에서 인덱스를 처리하므로 대규모 텍스트 검색에서 속도가 제한된다.
- 강력한 텍스트 분석기
- 다양한 텍스트 분석기를 제공하여 언어별 토큰화, 어간 추출, 형태소 분석, N-gram 드을 지원하여 입력된 검색어를 더 정교하게 처리하고, 다양한 변형 형태의 단어도 정확하게 검색할 수 있다.
- MySQL의 Full-text 인덱스는 기본적으로 간단한 토큰화와 N-gram 분석을 제공하기만 한다.
- 다양한 검색 기능
- 정확도를 높이기 위해 스코어링과 랭킹을 적용하여 검색 결과를 정렬한다. 이를 통해 가장 관련성 높은 결과를 먼저 보여줄 수 있다.
- MySQL의 Full-text 검색은 단순 매칭 기반이므로, 결과의 정렬과 관련성 평가가 제한적이다.
- 실시간 색인
- 데이터를 실시간으로 색인화하여, 새로 추가된 문서나 변경된 문서를 즉시 검색할 수 있게 한다.
- MySQL에서는 데이터 변경 시 인덱스가 즉각적으로 업데이트 되지 않을 수 있고, 대규모 변경 시 성능 저하가 유발된다.
4. 참고
1) MySQL 인덱스 종류 : https://velog.io/@ymh92730/MySQL-Index-%EC%9D%98-%EC%A2%85%EB%A5%98
[MySQL] Index 의 종류
출처 및 참조MySQL 의 Index 는 클러스터드 인덱스(Clustered Index) 와 논클러스터드 인덱스(Non-Clustered Index) 또는 세컨더리 인덱스(Secondary Index) 라고 하는 보조 인덱스가 존재한다.먼저 이 둘을 책과 비
velog.io
2) MySQL에서 1개의 쿼리는 1개의 인덱스만 : https://dev.mysql.com/doc/refman/8.4/en/optimizing-innodb-queries.html
MySQL :: MySQL 8.4 Reference Manual :: 10.5.6 Optimizing InnoDB Queries
10.5.6 Optimizing InnoDB Queries To tune queries for InnoDB tables, create an appropriate set of indexes on each table. See Section 10.3.1, “How MySQL Uses Indexes” for details. Follow these guidelines for InnoDB indexes: Because each InnoDB table ha
dev.mysql.com
[엘라스틱서치] ElasticSearch란? (개념, 장단점, 면접 질문)
ElasticSearchElasticsearch는 Apache Lucene 기반의 오픈소스, 분산, RESTful 검색 엔진입니다. 대량의 데이터를 신속하고 거의 실시간에 가까운 속도로 저장, 검색, 분석할 수 있도록 설계되었습니다. Elasticse
yuna-ninano.tistory.com