BTP

Calculation View 성능 90% 병목의 진짜 원인 #shorts #SAP #HANA

▶ YouTube에서 보기

개요 — 이 글에서 얻어갈 것

Calculation View는 SAP HANA 모델링의 중심축이지만, 잘못 설계하면 수 초에서 수 분까지 응답이 느려지는 대표적인 병목 지점이기도 합니다. 이 글은 "왜 느려지는가"를 엔진 동작 원리 수준에서 짚고, PlanViz와 Explain Plan으로 병목을 계측한 뒤, 실제 개선 전/후를 비교하는 실전 예제까지 다룹니다. 난이도는 중급으로, HANA 2.0 SPS07 및 HANA Cloud 환경을 기준으로 설명합니다.

  • Calculation View가 느려지는 6가지 대표 원인 이해 (Join Engine 과다 사용, Star Join 미활용, 불필요한 Aggregation, 통화 변환 남용, Filter Push-down 실패, Row/Column Engine 혼용)
  • Explain Plan과 PlanViz로 병목 구간을 정량적으로 찾는 방법
  • 개선 전/후 실행 시간과 메모리 사용량 비교 방식
  • 실무에 바로 붙여 쓸 수 있는 튜닝 체크리스트 확보

시작 전에 알고 있으면 좋은 것

SQL 기본 문법(JOIN, GROUP BY, WHERE)과 HANA Studio 또는 Business Application Studio에서 Calculation View를 한 번이라도 만들어 본 경험이 있으면 충분합니다. Projection, Join, Aggregation, Union 노드의 역할을 알고 있다면 더 빠르게 따라올 수 있습니다. 컬럼 스토어(Column Store)의 개념을 알면 4장 핵심 개념 이해가 쉬워집니다.

환경 · 버전 · 준비물

이 글의 예제는 아래 환경을 기준으로 작성했습니다. 온프레미스와 클라우드 모두 원리는 동일하지만, 개발 도구가 다르다는 점만 유의하면 됩니다.

  • SAP HANA Cloud (2024 QRC 이후 권장) 또는 SAP HANA 2.0 SPS06/SPS07 온프레미스
  • 모델링 도구: HANA Cloud는 Business Application Studio + HANA Database Explorer, 온프레미스는 HANA Studio 또는 Web IDE
  • 분석 도구: PlanViz(HANA Studio) 또는 Database Explorer의 Analyze > SQL Analyzer
  • 예제 테이블: SALES_ORDER(주문 헤더, 약 5백만 건), SALES_ORDER_ITEM(품목, 약 2천만 건), MATERIAL_STOCK(자재 재고), DIM_CUSTOMER, DIM_MATERIAL(마스터)
  • 권한: EXPLAIN PLAN 실행 권한과 해당 스키마 SELECT 권한

HANA Cloud에서는 일부 레거시 기능(Analytic View, Attribute View)이 지원되지 않고 Calculation View로 통합되었으므로, 마이그레이션 중이라면 이 글의 Star Join 패턴이 특히 유용합니다.

핵심 개념 — 왜 느려지는가

Calculation View의 성능을 이해하려면 HANA 내부에 여러 실행 엔진이 있다는 사실부터 알아야 합니다. 쿼리 하나가 실행될 때 Join Engine(단순 조인), OLAP Engine(Star Join + 집계에 최적화), Calculation Engine(복잡한 계산 로직), 그리고 Row Engine(윈도 함수 등 일부 연산)이 역할을 나눠 갖습니다. 비유하자면 물류센터의 분업과 같습니다. 대량 집계는 자동 분류기(OLAP Engine)가 처리해야 빠른데, 설계를 잘못하면 모든 상자를 수작업 검수대(Row Engine)로 보내는 꼴이 됩니다. 이때 발생하는 엔진 간 데이터 전달(materialization)이 바로 성능 저하의 주범입니다.

느려지는 대표 원인을 원리와 함께 정리하면 다음과 같습니다.

  • Filter Push-down 실패: 필터가 최하단 Projection까지 내려가지 못하면 2천만 건을 전부 읽은 뒤 상단에서 걸러냅니다. 계산 컬럼(Calculated Column)에 필터를 걸거나, 노드 중간에 집계가 끼어 있으면 push-down이 막히는 경우가 많습니다.
  • Star Join 미활용: 트랜잭션 테이블과 여러 마스터를 일반 Join 노드로 줄줄이 연결하면 Join Engine이 순차적으로 중간 결과를 만들어냅니다. Star Join 노드를 쓰면 OLAP Engine이 팩트-차원 구조를 한 번에 처리해 훨씬 효율적입니다.
  • 불필요한 Aggregation 노드: 집계 노드는 그 시점의 중간 결과를 실체화(materialize)합니다. "일단 집계해 두자"는 습관이 오히려 push-down을 막고 메모리를 낭비합니다.
  • Currency/Unit 변환 남용: 통화 변환은 행 단위 연산이라 비용이 큽니다. 집계 전에 수백만 행에 변환을 적용하면 최악의 패턴이 됩니다.
  • Row Engine 혼용: 윈도 함수, 일부 문자열/날짜 연산은 Row Engine으로 넘어가며, 컬럼 스토어 데이터를 행 형태로 변환하는 비용이 추가됩니다.
  • Join 카디널리티/타입 미지정: 카디널리티(n:1 등)를 지정하고 "Optimize Join Columns"를 켜면, 결과에 필요 없는 조인을 옵티마이저가 제거(join pruning)할 수 있습니다. 지정하지 않으면 이 최적화가 동작하지 않습니다.

정리하면, 튜닝의 대원칙은 두 가지입니다. 첫째, 데이터를 가능한 한 아래 노드에서 줄여라(필터/집계 push-down). 둘째, 엔진 전환과 중간 실체화를 최소화하라.

실전 예제 3단계 — 병목 찾기부터 개선까지

1단계: 기본 — Explain Plan으로 현재 상태 계측하기

먼저 느린 뷰가 실제로 어떤 연산자로 실행되는지 확인합니다. 고객별 월 매출을 조회하는 쿼리를 예로 들겠습니다.

-- 느린 쿼리의 실행 계획 확인
EXPLAIN PLAN SET STATEMENT_NAME = 'CV_SALES_SLOW' FOR
SELECT CUSTOMER_ID, ORDER_MONTH, SUM(NET_AMOUNT) AS TOTAL_AMT
FROM "_SYS_BIC"."sales.models/CV_SALES_ANALYSIS"
WHERE ORDER_MONTH = '2026-07'
GROUP BY CUSTOMER_ID, ORDER_MONTH;

SELECT OPERATOR_NAME, OPERATOR_DETAILS, TABLE_NAME, OUTPUT_SIZE, EXECUTION_ENGINE
FROM EXPLAIN_PLAN_TABLE
WHERE STATEMENT_NAME = 'CV_SALES_SLOW'
ORDER BY OPERATOR_ID;

여기서 볼 포인트는 세 가지입니다. (1) EXECUTION_ENGINE 컬럼에 ROW가 섞여 있는지, (2) 최하단 COLUMN TABLE 접근의 OUTPUT_SIZE가 필터 적용 후 기대치(예: 월 데이터 약 40만 건)보다 비정상적으로 큰지(2천만 건이면 push-down 실패), (3) JOIN 연산자가 필요 이상으로 많은지. 더 정밀한 분석이 필요하면 PlanViz(HANA Studio에서 쿼리 우클릭 → Visualize Plan → Execute)를 실행해 노드별 실행 시간(Inclusive/Exclusive Time)과 처리 행 수를 시각적으로 확인합니다. HANA Cloud라면 Database Explorer의 SQL Analyzer가 같은 역할을 합니다.

2단계: 실무 시나리오 — Push-down 실패 원인 제거와 Star Join 전환

계측 결과, ORDER_MONTH가 계산 컬럼(LEFT(ORDER_DATE, 7) 방식)이어서 필터가 테이블까지 내려가지 못하고, 마스터 3개가 일반 Join 노드로 직렬 연결된 것이 병목이었다고 가정합니다. 개선 방향은 두 가지입니다.

먼저 필터 대상 컬럼을 원본 컬럼 기반으로 바꾸거나, Input Parameter를 최하단 Projection의 필터식에 직접 매핑합니다.

<!-- 최하단 Projection 노드: Input Parameter를 필터로 직접 push-down -->
<projection id="PRJ_ORDER_ITEM">
  <input source="SALES_ORDER_ITEM"/>
  <filter>
    "ORDER_DATE" BETWEEN :IP_DATE_FROM AND :IP_DATE_TO
  </filter>
  <!-- 필요한 컬럼만 매핑: ITEM_GUID, CUSTOMER_ID, MATERIAL_ID, ORDER_DATE, NET_AMOUNT -->
</projection>

다음으로 CUBE with Star Join 타입으로 뷰를 재구성해, SALES_ORDER_ITEM을 팩트로 두고 DIM_CUSTOMER, DIM_MATERIAL을 Shared Dimension으로 연결합니다. 이때 각 조인에 카디널리티 n:1을 지정하고 Optimize Join Columns를 활성화합니다. 이렇게 하면 고객 속성을 조회하지 않는 쿼리에서는 DIM_MATERIAL 조인이 아예 제거(pruning)됩니다. 로깅 관점에서는 개선 전/후를 반드시 수치로 남깁니다.

-- 실행 통계 비교: 개선 전/후 동일 쿼리를 각각 실행 후 조회
SELECT STATEMENT_STRING, AVG_EXECUTION_TIME / 1000 AS AVG_MS,
       AVG_EXECUTION_MEMORY_SIZE / 1024 / 1024 AS AVG_MB, EXECUTION_COUNT
FROM M_SQL_PLAN_CACHE
WHERE STATEMENT_STRING LIKE '%CV_SALES_ANALYSIS%'
ORDER BY AVG_EXECUTION_TIME DESC;

실무 예시 수치로, 이 패턴의 개선에서 실행 시간 8.4초 → 0.6초, 피크 메모리 3.2GB → 180MB 수준으로 줄어드는 사례가 일반적으로 관찰됩니다. 핵심은 "최하단에서 40만 건으로 줄인 뒤 조인·집계"라는 순서 재배치입니다.

3단계: 프로덕션 — 통화 변환 최적화, 회귀 테스트, 보안

운영 반영 전에 세 가지를 마무리합니다. 첫째, 통화 변환은 집계 후에 수행하도록 노드 순서를 조정합니다. 2천만 행에 변환을 적용하는 대신, 고객×월로 집계된 수만 행에 적용하면 변환 비용이 수백 분의 일로 줄어듭니다(단, 환율 기준일이 행 단위로 달라야 하는 요건이라면 예외이므로 업무 요건을 먼저 확인해야 합니다). 둘째, 성능 회귀를 막기 위한 테스트를 자동화합니다.

-- 성능 회귀 테스트: 임계치(1초) 초과 시 알림 대상으로 기록
DO BEGIN
  DECLARE v_start TIMESTAMP = CURRENT_TIMESTAMP;
  SELECT COUNT(*) FROM "_SYS_BIC"."sales.models/CV_SALES_ANALYSIS"
    (PLACEHOLDER."$$IP_DATE_FROM$$" => '2026-07-01',
     PLACEHOLDER."$$IP_DATE_TO$$"   => '2026-07-31');
  IF NANO100_BETWEEN(v_start, CURRENT_TIMESTAMP) / 10000000 > 1 THEN
    INSERT INTO PERF_REGRESSION_LOG VALUES ('CV_SALES_ANALYSIS', CURRENT_TIMESTAMP);
  END IF;
END;

셋째, 보안 측면에서 Analytic Privilege(HANA Cloud는 DCL 기반 구조화 권한)를 적용할 때는 권한 필터 역시 push-down 가능한 원본 컬럼 기준으로 정의하는 것이 권장됩니다. 계산 컬럼 기반 권한 필터는 2단계에서 고친 병목을 다시 만들 수 있습니다. 또한 "Execute in SQL Engine" 플래그(HANA Cloud에서는 기본 동작)를 켜서 Calculation Engine 연산을 SQL 옵티마이저로 언폴딩(unfolding)하면 전역 최적화 기회가 늘어나므로, 결과 동일성 검증 후 적용을 권장합니다.

자주 겪는 문제와 해결 FAQ

  • Q1. 필터를 걸었는데 PlanViz에서 여전히 전체 행을 읽습니다. 계산 컬럼·집계 노드·특정 함수(예: 비결정적 함수)가 push-down을 막는 대표 원인입니다. 필터 컬럼을 원본 컬럼으로 바꾸거나, Input Parameter를 최하단 노드에 직접 매핑하세요. 뷰를 겹겹이 쌓은 경우(view-on-view) 중간 뷰의 Aggregation이 범인인 경우가 많습니다.
  • Q2. Star Join 노드에 테이블을 직접 붙일 수 없다고 나옵니다. Star Join의 차원 입력은 반드시 Dimension 타입 Calculation View여야 합니다. DIM_CUSTOMER 테이블을 감싸는 Dimension 뷰를 먼저 만들고 Shared로 연결하세요.
  • Q3. 개발계에서는 빠른데 운영계에서만 느립니다. 데이터 분포 차이로 옵티마이저가 다른 계획을 선택했을 가능성이 큽니다. 운영계에서 PlanViz를 다시 뜨고, M_SQL_PLAN_CACHE로 실제 실행 통계를 비교하세요. 파티셔닝된 테이블이라면 파티션 프루닝이 되는 필터 조건인지도 확인이 필요합니다.
  • Q4. 윈도 함수(RANK 등)를 쓰자 갑자기 느려졌습니다. Row Engine 전환 비용입니다. 가능한 한 집계로 데이터를 줄인 뒤 최상단에서 윈도 함수를 적용하거나, SQLScript 테이블 함수로 분리해 처리 범위를 좁히는 방법이 일반적입니다.

더 깊이 들어가려면

이 글의 다음 주제로는 (1) SQLScript 테이블 함수 vs Calculation View 선택 기준, (2) 대용량 테이블의 파티셔닝 전략(range/hash)과 뷰 성능의 관계, (3) HANA Cloud의 Workload Class로 무거운 분석 쿼리의 리소스를 제한하는 방법, (4) CDS View(ABAP RAP 환경)와 Calculation View의 성능 특성 비교를 권합니다. 특히 HANA Cloud로 이관을 앞두고 있다면 Calculation View 언폴딩 동작 차이를 먼저 검증해 보는 것이 좋습니다.

함께 보면 좋은 문서

댓글 0

아직 댓글이 없습니다.