BTP

HANA Calc View 성능 잡는 체크 3가지 #shorts #SAP #HANA

개요: 왜 Calculation View가 느려지는가

SAP HANA에서 Calculation View는 분석 모델링의 중심이지만, 데이터가 수억 건으로 늘어나면 설계 시점에는 보이지 않던 병목이 드러납니다. 이 글은 가상의 물류 기업 "한빛로지스"의 출고 실적 분석 뷰가 45초까지 느려진 상황을 배경으로, 실무에서 바로 적용할 수 있는 성능 튜닝 체크리스트를 원인·진단·해결 순서로 정리한 실전 예제입니다.

  • 실행 엔진(Column/Row/ESX/HEX)과 SQL Unfolding 동작 원리 이해
  • 필터·집계 푸시다운이 깨지는 패턴 식별
  • PlanViz와 Expensive Statements로 병목 지점 진단
  • 파티션 프루닝, Union Pruning, 캐시 전략 적용

미리 알고 있으면 좋은 배경

Graphical Calculation View의 노드 구조(Projection, Aggregation, Join, Union, Rank), 기본 SQL 작성 능력, 그리고 컬럼 스토어가 딕셔너리 인코딩으로 데이터를 압축·스캔한다는 개념을 알고 있다면 내용을 따라오기 수월합니다. Business Application Studio 또는 Web IDE에서 HDB 모듈을 배포해 본 경험이 있으면 더 좋습니다.

실습 환경과 버전

이 글의 예제는 SAP HANA Cloud(2024 QRC 이후) 및 SAP HANA 2.0 SPS07 기준으로 작성했습니다. 두 버전은 HEX(HANA Execution Engine) 적용 범위와 힌트 동작이 다르므로 버전 확인이 먼저입니다.

-- 현재 버전 확인
SELECT VERSION FROM M_DATABASE;

-- 세션 권한: 성능 뷰 조회에는 아래 시스템 뷰 접근 권한이 일반적으로 필요
-- M_EXPENSIVE_STATEMENTS, M_CS_TABLES, M_SQL_PLAN_CACHE

가상 시나리오의 데이터 구조는 다음과 같습니다. 출고 헤더 LG_SHIPMENT_HDR(1.2억 건), 출고 품목 LG_SHIPMENT_ITM(4.8억 건), 운송사 마스터 LG_CARRIER_MST(3천 건), 그리고 이를 합친 Calculation View CV_SHIPMENT_PERF가 분석 대상입니다.

동작 원리: 뷰가 실행되는 진짜 순서

Calculation View를 고속도로 물류망에 비유하면, 각 노드는 화물 분류장이고 데이터는 트럭입니다. 튜닝의 본질은 "트럭(행)을 최대한 출발지(테이블 스캔 단계)에서 줄이는 것"입니다. 목적지 근처에서 화물을 버리는 설계는 이미 도로(메모리·CPU)를 낭비한 뒤입니다.

핵심 메커니즘은 세 가지입니다.

  • SQL Unfolding — 옵티마이저가 뷰 계층을 하나의 SQL 실행 계획으로 펼쳐 전역 최적화를 시도합니다. 스크립트 기반 로직, 일부 Rank 노드, 비결정적 함수가 섞이면 Unfolding이 차단되어 노드별 중간 결과가 물리적으로 생성됩니다.
  • 필터/집계 푸시다운 — 상위 노드의 WHERE 조건과 GROUP BY가 하위 Projection까지 내려가는 동작입니다. 계산 컬럼 위에 필터를 걸면 푸시다운이 끊기는 경우가 많습니다.
  • Pruning — Union 노드의 Constant Column이나 파티션 조건으로, 애초에 읽을 필요가 없는 데이터 소스 전체를 건너뜁니다.

진단의 출발점은 항상 측정입니다.

-- 느린 쿼리 상위 목록 (M_EXPENSIVE_STATEMENTS 활성화 필요)
SELECT STATEMENT_STRING, DURATION_MICROSEC / 1000000.0 AS DUR_SEC,
       MEMORY_SIZE / 1024 / 1024 AS MEM_MB
FROM   M_EXPENSIVE_STATEMENTS
WHERE  STATEMENT_STRING LIKE '%CV_SHIPMENT_PERF%'
ORDER  BY DURATION_MICROSEC DESC
LIMIT  10;

-- 실행 계획으로 Unfolding 여부 확인 (Column Search 구조 관찰)
EXPLAIN PLAN FOR
SELECT CARRIER_REGION, SUM(SHIP_QTY)
FROM   "CV_SHIPMENT_PERF"
WHERE  SHIP_DATE >= '2026-01-01'
GROUP  BY CARRIER_REGION;

체크리스트 기반 실전 예제 3단계

1단계 — 필터 푸시다운 복구 (기본)

점검 항목: 계산 컬럼 위 필터. 한빛로지스 뷰에서는 SHIP_YEAR라는 계산 컬럼(LEFTSTR(SHIP_DATE, 4))에 필터를 걸고 있었고, 이 때문에 4.8억 건 전체 스캔이 발생했습니다. 원본 컬럼에 범위 조건을 쓰도록 바꾸거나, Input Parameter를 하위 Projection의 필터식에 직접 매핑하는 방식이 일반적으로 권장됩니다.

-- 나쁜 예: 계산 컬럼 필터 → 푸시다운 차단
SELECT * FROM "CV_SHIPMENT_PERF" WHERE SHIP_YEAR = '2026';

-- 좋은 예: 원본 날짜 컬럼 범위 조건 + 파라미터 전달
SELECT CARRIER_ID, SUM(SHIP_QTY) AS TOTAL_QTY
FROM   "CV_SHIPMENT_PERF" (PLACEHOLDER."$$P_DATE_FROM$$" => '2026-01-01',
                           PLACEHOLDER."$$P_DATE_TO$$"   => '2026-06-30')
GROUP  BY CARRIER_ID;

이 변경만으로 스캔 대상이 4.8억 건에서 약 6천만 건으로 줄어 실행 시간이 45초에서 9초로 단축되었습니다.

2단계 — 조인 최적화와 진단 로깅 (실무)

점검 항목: 조인 카디널리티와 Optimize Join Columns. 헤더-품목 조인이 Aggregation 노드보다 위에 있으면 집계 전 4.8억 건이 조인에 참여합니다. 집계를 먼저 수행해 행 수를 줄인 뒤 마스터와 조인하도록 노드 순서를 바꾸고, 조인 정의에 카디널리티(N:1)를 명시하면 옵티마이저가 불필요한 조인 실행 자체를 생략(Join Pruning)할 수 있습니다. 튜닝 과정은 반드시 수치로 기록해 회귀를 감지합니다.

-- 튜닝 전후 비교를 위한 간이 로깅 테이블
CREATE COLUMN TABLE ZLOG_PERF_TUNING (
  RUN_ID     INTEGER GENERATED BY DEFAULT AS IDENTITY,
  TEST_LABEL NVARCHAR(60),
  DUR_MS     BIGINT,
  PEAK_MEM_MB DECIMAL(12,2),
  RUN_TS     TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- PlanViz 대용 간이 측정: 힌트로 엔진 경로 비교
SELECT COUNT(*) FROM "CV_SHIPMENT_PERF"
WITH HINT (NO_CALC_VIEW_UNFOLDING);  -- Unfolding 차단 시 성능 비교용

힌트로 성능이 오히려 좋아진다면 Unfolding 이후 계획이 비효율적이라는 신호이므로, 힌트를 영구 적용하기보다 모델 구조(계산 컬럼 위치, Rank 노드)를 먼저 손보는 편이 좋습니다.

3단계 — 파티셔닝·캐시·권한 분리 (프로덕션)

점검 항목: 파티션 프루닝과 결과 캐시. 월 단위 조회가 대부분이므로 품목 테이블을 날짜 기준 RANGE 파티션으로 재구성하고, 자주 조회되는 집계 패턴에는 Static Result Cache를 검토합니다. 보안 측면에서는 Analytic Privilege를 뷰에 걸어 지역별 데이터 접근을 분리하되, 복잡한 SQL 기반 권한식이 푸시다운을 막지 않는지 재측정이 필요합니다.

-- 날짜 RANGE 파티셔닝: WHERE 조건과 맞물려 파티션 프루닝 유도
ALTER TABLE LG_SHIPMENT_ITM
  PARTITION BY RANGE (SHIP_DATE)
  ( PARTITION '2024-01-01' <= VALUES < '2025-01-01',
    PARTITION '2025-01-01' <= VALUES < '2026-01-01',
    PARTITION '2026-01-01' <= VALUES < '2027-01-01',
    PARTITION OTHERS );

-- 프루닝 동작 검증
SELECT PART_ID, RECORD_COUNT
FROM   M_CS_TABLES
WHERE  TABLE_NAME = 'LG_SHIPMENT_ITM';

최종적으로 한빛로지스 사례는 9초에서 1.4초까지 개선되었고, 테스트 자동화를 위해 대표 쿼리 5종을 야간 배치로 실행해 ZLOG_PERF_TUNING에 적재, 전일 대비 30% 이상 느려지면 알림이 가도록 구성했습니다.

자주 발생하는 문제와 FAQ

  • Q1. 개발계는 빠른데 운영계만 느립니다. 데이터 분포 차이로 실행 계획이 달라진 경우가 대부분입니다. M_SQL_PLAN_CACHE에서 두 환경의 계획을 비교하고, 통계가 오래됐다면 델타 머지 상태(M_DELTA_MERGE_STATISTICS)를 확인하세요. 델타 스토어에 수천만 건이 쌓여 있으면 스캔 성능이 급락합니다.
  • Q2. 힌트를 넣었더니 다른 화면이 느려졌습니다. 힌트는 특정 쿼리 패턴에만 유효합니다. 전역 적용 대신 Statement Hint 테이블로 대상 SQL을 한정하고, 모델 구조 개선이 끝나면 힌트를 제거하는 운영이 일반적으로 권장됩니다.
  • Q3. Rank 노드를 넣은 뒤부터 급격히 느려졌습니다. Rank 노드는 버전에 따라 Unfolding을 막을 수 있습니다. 상위 N 추출이 목적이라면 소비 쿼리 쪽 LIMIT + ORDER BY로 대체 가능한지, 또는 윈도우 함수 기반 SQL 뷰로 분리할 수 있는지 검토하세요.
  • Q4. 메모리 초과(OOM)가 간헐적으로 발생합니다. 조인 전 집계 누락으로 중간 결과가 폭증하는 패턴이 흔합니다. PlanViz에서 노드별 출력 행 수를 확인해 가장 굵은 간선을 찾는 것이 지름길입니다.

더 파볼 주제

이 글의 체크리스트가 손에 익었다면, 워크로드 관리(Workload Class)로 리소스 상한을 설정하는 방법, NSE(Native Storage Extension)로 콜드 데이터를 디스크 계층으로 내리는 전략, SQLScript 테이블 함수와 Calculation View의 성능 특성 비교, 그리고 HANA Cloud의 HEX 엔진 최적화 패턴을 다음 주제로 살펴보길 권합니다.

댓글 0

아직 댓글이 없습니다.