PostgreSQL은 고성능의 데이터베이스 관리 시스템(DBMS)으로, 대규모 데이터를 처리하는데 탁월한 능력을 자랑합니다. 그러나 때로는 데이터 스큐(Data Skew)와 같은 문제로 인해 성능이 저하될 수 있습니다. 특히, 쿼리 최적화에서 발생하는 통계의 부정확성은 상당한 성능 문제를 초래할 수 있습니다.
이 글에서는 이러한 문제를 해결하기 위한 기법 중 하나인 인덱스 프로빙(Index Probing)을 소개하고, 이를 PostgreSQL에서 어떻게 구현할 수 있는지 살펴보겠습니다.
특정 값의 비정상적인 빈도: 예를 들어, 특정 컬럼에 ‘100’이라는 값이 수백만 건 존재하고 다른 값들은 드문드문 존재하는 경우, 이 데이터를 대상으로 한 쿼리 성능이 크게 저하될 수 있습니다.
편향된 데이터 분포: 예를 들어, 지역별 판매 데이터에서 특정 지역의 데이터가 전체 데이터의 90%를 차지한다면, 그 지역에 대한 쿼리가 다른 지역보다 훨씬 더 자주 실행될 것입니다.
이러한 경우 PostgreSQL의 쿼리 최적화기(Query Optimizer)는 데이터 분포를 정확히 예측하지 못할 수 있습니다. 그 결과 옵티마이저가 부적절한 쿼리 계획을 수립하여, 인덱스 스캔 대신 풀 테이블 스캔을 선택하게 되거나, 인덱스를 사용하더라도 효율적이지 못한 실행 계획을 채택하게 됩니다. 이런 경우 쿼리 성능이 크게 떨어질 수 있습니다.
이러한 데이터 스큐가 있는 테이블에서 쿼리를 실행할 때, PostgreSQL 옵티마이저는 데이터의 정확한 분포를 알지 못하기 때문에 부정확한 쿼리 계획을 수립할 수 있습니다. 예를 들어, 특정 필드에 대한 조건이 있을 때, 옵티마이저는 잘못된 통계를 바탕으로 인덱스 스캔 대신 풀 테이블 스캔을 선택할 수 있습니다. 이는 성능 저하의 주된 원인 중 하나입니다.
문제가 되는 테이블에서 옵티마이저가 부정확한 통계를 사용한 경우, 쿼리 실행 계획이 실제 데이터 분포와 크게 어긋날 수 있습니다. 예를 들어, 특정 값에 대한 조건절을 포함하는 쿼리에서, 이 값이 데이터 집합 내에서 매우 빈번하게 나타나는 경우, 옵티마이저는 이를 제대로 반영하지 못할 수 있습니다.
이러한 상황에서는 통계 분석 도구인 ANALYZE 명령을 사용하여 최신 통계 정보를 업데이트하는 것이 필수적입니다. 그러나, 데이터 스큐가 심각한 경우에는 이 명령으로도 정확한 성능을 보장하기 어려울 수 있습니다. 이럴 때 인덱스 프로빙 기법이 유용할 수 있습니다.
예를 들어, 다음과 같은 쿼리를 생각해 봅시다
SELECT * FROM orders WHERE customer_id = 100;
이 쿼리는 customer_id가 100인 모든 주문을 검색합니다. 만약 customer_id 값이 데이터베이스 전체에 고르게 분포되어 있다면, 옵티마이저는 인덱스 스캔을 사용해 효율적인 검색을 수행할 것입니다.
그러나 customer_id 값 중 ‘100’이 전체 데이터의 50% 이상을 차지한다면, 옵티마이저는 풀 테이블 스캔을 선택할 가능성이 높아집니다. 이는 데이터가 수백만 건에 이르는 대규모 테이블에서는 매우 비효율적입니다.
인덱스 프로빙 기법은 데이터의 특정 조건이 옵티마이저의 예측을 넘어서는 상황에서 인덱스를 이용해 보다 정확한 예측을 가능하게 하는 방법입니다.
PostgreSQL에서는 인덱스 프로빙을 통해 특정 값의 존재를 평가하고, 이를 기반으로 보다 정확한 쿼리 계획을 세울 수 있습니다.
3.1 데이터 생성 및 초기 설정
먼저, 실험을 위해 데이터가 비정상적으로 분포된 테이블을 생성해 보겠습니다. 그리고 필요한 확장을 활성화합니다.
CREATE EXTENSION tablefunc;
-- 정규 분포를 따르는 데이터 생성
CREATE TABLE norm_test AS
SELECT abs(r::integer) AS val
FROM normal_rand(1E7::integer, 5.::float8, 300.::float8) AS r;
-- 테이블 분석 (통계 생성)
ANALYZE norm_test;
CREATE EXTENSION tablefunc; 명령어는 PostgreSQL의 tablefunc 확장을 활성화합니다. 이 확장은 정규 분포 랜덤 데이터를 생성하는 normal_rand와 같은 함수를 제공하며, 실험 데이터를 생성하는 데 유용합니다.
normal_rand 함수를 사용해 정규 분포를 따르는 데이터를 생성했습니다. ANALYZE 명령어를 통해 생성된 데이터의 통계 정보를 갱신합니다.
3.2 데이터 스큐와 인덱스 생성
데이터 스큐를 모방하기 위해 값이 집중된 데이터를 추가로 삽입하고, 인덱스를 생성합니다.
-- 데이터 스큐를 나타내는 값 추가
CREATE TABLE norm_test1 AS SELECT gs % 100 AS val
FROM generate_series(1,1E7) AS gs;
-- 일부 희귀한 값 추가
INSERT INTO norm_test1 (val) SELECT gs
FROM generate_series(101,105) AS gs;
VACUUM ANALYZE norm_test1;
ALTER TABLE norm_test1 SET (autovacuum_enabled = 'false');
-- 중복된 값을 대량 삽입
INSERT INTO norm_test1 (val) SELECT 100 FROM generate_series(1,1E5);
-- 인덱스 생성
CREATE INDEX idx_val ON norm_test1(val);
이 과정에서 특정 값(val = 100)이 데이터 집합 내에서 대량으로 중복되어 추가되며, 이는 실제로 데이터 스큐가 있는 상황을 시뮬레이션합니다. 이후, 해당 컬럼에 대해 인덱스를 생성하여 쿼리 성능을 최적화할 준비를 합니다.
3.3 인덱스 프로빙 기법의 구현 및 벤치마킹
이제 인덱스 프로빙 기법을 적용한 쿼리를 실행해 보고, 성능을 분석합니다.
-- 인덱스 프로빙을 적용한 쿼리
EXPLAIN ANALYZE
SELECT val
FROM norm_test1
WHERE val = 100;
-- 결과 분석
EXPLAIN (ANALYZE, BUFFERS)
SELECT val
FROM norm_test1
WHERE val = 100;
EXPLAIN ANALYZE는 쿼리의 실행 계획과 실제 실행 시간을 보여주며, EXPLAIN (ANALYZE, BUFFERS)는 추가적으로 쿼리 실행 중 사용된 버퍼(메모리) 정보도 제공합니다. 이를 통해 인덱스 프로빙 기법이 적용된 쿼리의 성능을 분석할 수 있습니다.
벤치마킹 결과, 인덱스 프로빙 기법은 특정 조건에서 성능을 크게 향상시킬 수 있음을 확인할 수 있습니다. 특히, 데이터가 극도로 치우쳐 있는 상황에서 이 기법은 옵티마이저가 부정확한 통계로 인해 잘못된 결정을 내리는 것을 방지할 수 있습니다.
빠른 데이터 검색: 인덱스를 활용해 특정 값을 빠르게 찾을 수 있습니다. 예를 들어, val = 100인 데이터를 검색할 때 인덱스를 사용하면 테이블 전체를 스캔할 필요 없이 필요한 데이터만 빠르게 검색할 수 있습니다.
쿼리 계획의 정확성 향상: 인덱스 프로빙을 통해 옵티마이저가 보다 정확한 데이터 분포를 파악할 수 있게 되므로, 최적의 쿼리 계획을 수립할 수 있습니다.
4.1 성능 분석 결과
인덱스 프로빙 기법을 적용한 후, 쿼리의 실행 시간이 눈에 띄게 감소했음을 알 수 있습니다. 이는 특히 데이터가 불균형하게 분포되어 있는 경우에 유용합니다. 또한, 인덱스 프로빙을 통해 불필요한 테이블 스캔을 방지하고, 필요한 데이터에 빠르게 접근할 수 있습니다.
인덱스 프로빙은 PostgreSQL에서 데이터 스큐로 인한 성능 문제를 해결할 수 있는 강력한 도구입니다. 이 기법을 통해 옵티마이저는 데이터 분포를 보다 정확하게 이해하고, 적절한 쿼리 계획을 수립할 수 있습니다. 그러나 인덱스 프로빙은 모든 경우에 유용한 것은 아닙니다. 데이터 스큐가 존재하는 특정 상황에서만 효과를 발휘하므로, 이 기법을 사용할 때는 사전 분석과 테스트가 필수적입니다.
직접 실험해보고 싶다면 다음과 같은 접근 방법을 제안드립니다:
기본 쿼리 성능 측정: 인덱스 프로빙 기법을 적용하기 전의 성능을 측정하세요. EXPLAIN ANALYZE를 사용하여 쿼리 실행 계획과 시간을 기록합니다.
데이터 스큐 적용 및 재측정: 위에서 설명한 대로 데이터를 생성하고, 데이터 스큐를 적용한 후 성능을 다시 측정해보세요. 인덱스 프로빙을 적용하기 전후의 성능 차이를 비교하는 것이 중요합니다.
다양한 상황에서 테스트: 데이터 크기, 스큐 정도, 인덱스 유무 등 다양한 상황에서 인덱스 프로빙의 성능 효과를 테스트하세요.
이를 통해 인덱스 프로빙이 실제로 얼마나 유효한 기법인지 직접 체감할 수 있을 것입니다.