이룸 프로젝트

[이룸] 검색 기능 고도화 4 - MySQL FULL-TEXT 성능 테스트

쿠마냥 2024. 4. 29. 00:39

이전에 MySQL의 FULL-TEXT 기능을 적용하는 글을 작성하였다. 오늘은 FULL-TEXT 기능에 대해 더 자세히 알아보고, 해당 기능을 적용하기 전과 후 성능을 비교해 보려 한다. 

 

1. MySQL 인덱스란?

인덱스는 우리에게 아주 친숙한 개념이다. 바로 아래처럼 단어가 어느 페이지에 있는지 알려주는 목록을 인덱스라고 한다. 

 

Database에도 이러한 인덱스가 있어서 빠른 검색 결과를 도출하도록 도움을 준다. 만약 인덱스가 설정되어 있지 않으면, 모든 데이터를 처음부터 끝까지 검색해야 한다. 만약 데이터의 양이 무척 많다면, 이런 동작은 아주 비효율적일 것이다. 

 

 

2.  MySQL FULL-TEXT 기능 개괄

  • MySQL은 텍스트 검색을 위하여 자체적인 인덱싱 기능을 제공한다. 그것이 바로 FULL-TEXT 인덱스, 전문 인덱스이다. 
  • 만약 기본 키(Primary Key)와 유니크(@Unique) 조건이 있으면, MySQL은 해당 컬럼에 대해 자동으로 인덱스를 생성한다. 
  • 그 외 컬럼은 수동으로 인덱스를 생성해야 한다. 
  • 인덱스는 특정 컬럼에 대한 '포인터'를 포함한다. (포인터는 어느 문서에 해당 키워드가 있는지 알려준다.)
  • 데이터를 추가, 삭제, 수정할 때 인덱스도 자동으로 업데이트된다. 이로 인한 성능 저하가 발생할 수 있으므로 꼭 필요한 컬럼에만 인덱스를 생성하는 것이 좋다. 
  • 인덱스 페이지 생성을 위해, 테이블 크기의 10% 정도에 해당하는 추가 공간이 필요하다는 점도 기억할 것. 

 

3. FULL-TEXT 인덱스 내부 작동 원리 

MySQL은 B-트리 인덱스, 해시 인덱스, 공간 인덱스 등 여러 인덱스를 제공한다. 내가 이번에 사용한 것은 전문 인덱스(FULL-TEXT 인덱스)로, 텍스트 필드를 검색하는 데 최적화되어 있다. 

 

FULL-TEXT는 다음과 같이 작동한다. 

 

  1. 텍스트 분해: 텍스트를 개별 단어(토큰)로 분리한다. 이를 '토크나이징'이라고도 부른다. 텍스트 분석 작업을 실시하여 단어로 나누는 작업이다. 
  2. 정규화: 추출된 토큰들은 대소문자 통일, 어간 추출 등 정규화하는 작업을 거친다. 
  3. 인덱싱: 정규화된 단어들은 인덱스에 저장된다. MySQL은 일반적으로 역인덱스(Inverted Index)를 사용한다. 
  4. 검색과 쿼리 처리: 사용자가 검색을 하면, 해당 키워드를 포함한 쿼리가 db에 닿을 것이다. 이 쿼리는 다시 토큰화, 정규화 과정을 거친다. 그렇게 남겨진 키워드는 인덱스된 토큰과 비교된다. 알맞은 결과가 있으면 모아서 반환된다. 

 

5. 성능 테스트 

 

1) 적용 과정은 이 글을 참고하시라!

 

2) 100만 건의 더미 데이터 삽입 :

처음에는 배치 처리를 하지 않고 데이터를 넣으려 했다. 그러나 2시간이 넘어도 100만 건을 처리하지 못했고... 노트북의 배터리가 어마무시한 속도로 닳기 시작했다. 배치 처리 후 약 3분만에 해결되었다. 큰 데이터는 꼭 배치 처리를 하자... 

// MySQL에 100만 건의 데이터를 삽입하는 메서드. INSERT문을 배치 처리한다.
    public void insertDataToMySQL() throws Exception {
        Connection conn = dataSource.getConnection();

        String sql = "INSERT INTO challenge (title, category, description, start_date, due_date, frequency, auth_explanation, limit_attendance, current_attendance, thumbnail_image_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
        PreparedStatement pstmt = conn.prepareStatement(sql);

        for (int i = 1; i <= 1000000; i++) {
            pstmt.setString(1, "Title" + i);
            pstmt.setString(2, "Education");
            pstmt.setString(3, "Description of challenge " + i);
            pstmt.setDate(4, java.sql.Date.valueOf(LocalDate.now()));
            pstmt.setDate(5, java.sql.Date.valueOf(LocalDate.now().plusDays(30)));
            pstmt.setString(6, "Daily");
            pstmt.setString(7, "explanation");
            pstmt.setShort(8, (short) 50);
            pstmt.setShort(9, (short) 0);
            pstmt.setString(10, "http://example.com/image.jpg");

            pstmt.addBatch();
            if (i % 1000 == 0) {
                pstmt.executeBatch();
            }
    }
        pstmt.executeBatch(); // 남은 배치 처리
        pstmt.close();
        conn.close();
    }

 

 

3) FULL-TEXT 인덱스 설정 확인

 

4) 성능 비교 : 

100만 건의 데이터 중 곳곳에 '포도'라는 키워드를 숨겨 두고 검색을 실시했다. 

FULL-TEXT 도입 전, 챌린지 검색 기능의 응답 속도는 약 800ms인 것을 확인하였다. 도입 후, 응답 속도는 약 400ms로 줄어들어 50%의 성능 개선 효과가 있었다. 

 

*** 도입 전 

 

*** 도입 후

 

6. 마치며

FULL-TEXT 기능은 조회 성능을 향상시키는 데 효과가 있음을 확인하였다. 이것으로도 충분히 놀라웠는데, Elastic search는 더더욱 놀라운 성능 향상을 보여 주어 나를 정말 신나게 하였다! ES 성능 테스트는 다음 글에서...