MySQL 함수형 인덱스로 슬로우 쿼리 30초 -> 즉시 해결!

by DD
4개월 전
조회수 68

헤비 유저의 소셜 프로필 조회 시 30초 이상 소요되는 슬로우 쿼리 문제 발생

MySQL 8.0.13함수형 인덱스(Functional Index)를 적용하여 쿼리 성능 개선

비트 연산(Bitwise Operation) 조건의 인덱스 미사용 문제를 10진수 동등 비교로 해결

운영 환경 적용 중 복제 지연(Replication Lag) 문제 및 비트 연산 의미 오류 발생

점진적 롤아웃(Gradual Rollout) 전략을 통해 치명적인 버그를 사전에 발견하고 해결

슬로우 쿼리(Slow Query)의 원인: 비트 연산과 인덱스

본문에 따르면, 슬로우 쿼리의 주된 원인은 `category_flag & 0x0100`과 같은 비트 연산(Bitwise Operation) 조건이 인덱스(Index)를 무력화했기 때문이다. 비트 연산은 컬럼의 원본 값이 아닌 연산 결과를 조건으로 사용하므로, 인덱스를 활용하기 위해서는 모든 행에 접근하여 연산을 수행해야 한다. 특히, 헤비 유저의 경우 수십만 건의 데이터를 스캔해야 하므로 쿼리 성능 저하를 야기한다. user_id로 필터링 후, 해당 사용자의 모든 포스트를 순회해야 하는 문제점이 발생했다.

함수형 인덱스(Functional Index)의 작동 원리

글에 따르면, 함수형 인덱스는 MySQL 8.0.13에서 도입된 기능으로, 표현식의 결과를 인덱스로 저장하여 쿼리 성능을 향상시킨다. 일반 인덱스와 달리, 함수형 인덱스는 내부적으로 숨겨진 가상 컬럼(Hidden Virtual Generated Column)을 생성하여 표현식 결과를 저장하고 인덱스를 구성한다. 하지만, 쿼리의 표현식과 인덱스 정의가 정확히 일치해야 인덱스를 활용할 수 있다는 점에 유의해야 한다. 예를 들어, `(category_flag & 0x0100)`과 `(category_flag & 0x0010)`은 서로 다른 표현식으로 인식된다.

함수형 인덱스 적용 시 쿼리 변경 및 검증

필자에 따르면, 함수형 인덱스 적용을 위해 쿼리 조건을 10진수 동등 비교로 변경하는 과정이 필요했다. 기존의 비트 연산 truthy 체크(`category_flag & 0x0100`)는 인덱스를 사용하지 못했으나, 10진수 동등 비교(`(category_flag & 0x0100) = 256`)로 변경한 후 인덱스를 활용할 수 있었다. 개발 환경에서 EXPLAIN을 통해 스캔 행 수를 확인하고, 인덱스 사용 여부를 검증했다. 그 결과, 스캔 행 수가 805개에서 31개로 감소하여 쿼리 성능이 크게 개선되었다.

운영 환경 적용 과정에서의 문제점

본문에서는 운영 환경 적용 과정에서 발생한 문제점들을 상세히 설명한다. 먼저, 무중단 인덱스 생성을 위해 온라인 스키마 변경(Online DDL) 방식을 사용했다. 하지만, 인덱스 생성 작업 중 복제 지연(Replication Lag)이 발생하여 최신 데이터가 즉시 조회되지 않는 문제가 발생했다. 또한, 쿼리 변경 과정에서 비트 연산의 의미를 잘못 이해하여 치명적인 버그가 발생했다. 점진적 롤아웃(Gradual Rollout) 전략을 통해 이 버그를 사전에 발견하고 해결할 수 있었다.

점진적 롤아웃(Gradual Rollout)의 중요성

글에서 강조하는 점은 점진적 롤아웃(Gradual Rollout)의 중요성이다. 쿼리 변경의 영향 범위가 컸기 때문에, 동적 설정 관리 시스템을 활용하여 1번 샤드에 먼저 적용하고, 문제 발생 시 설정을 변경하여 즉시 롤백할 수 있도록 했다. 이 전략 덕분에 비트 연산의 의미를 잘못 이해하여 발생한 치명적인 버그를 프로덕션 전체 적용 전에 발견하고 수정할 수 있었다. 대규모 변경은 반드시 점진적으로 적용하여 리스크(Risk)를 최소화해야 한다.

슬로우 쿼리 해결기: 함수형 인덱스로 비트 연산 쿼리 최적화하기