Oracle Database In-Memory Concepts

Oracle 12C New Feature

GARIMOO
garimoo
22 min readMar 4, 2020

--

Oracle 10G, 11G DB들을 12C로 업그레이드하는 작업을 작년 내내 했다. 벌써 20C가 나온 와중에 12C도 늦은 감이 없진 않지만, 이제라도 12C의 New Feature들을 공부하고 서비스에 도입해보려 한다.

우선 제일 궁금한 기능인 In-Memory에 대해서 알아보았다. 요즘 Redis에 빠져서 In-Memory DB 공부를 엄청 하고 있기 때문에 이 기능을 Oracle은 어떻게 활용하는지 궁금했다.

원문: https://docs.oracle.com/en/database/oracle/oracle-database/12.2/inmem/index.html

1 Introduction to Oracle Database In-Memory

1.1 Challenges for Analytic Application

Description of “Figure 1–1 Multiple Indexes”
  • OLTP성 어플리케이션에 비해 분석형 어플리케이션은 더 많은 인덱스를 필요로 한다.
  • 하지만 인덱스가 늘어나면 효율성이 저하되고, insert시 성능이 저하되어 응답 시간이 증가한다는 단점이 있다.
  • 요즘은 어플리케이션에서 mixed-use database를 원한다. 즉 분석도 하면서, 빠른 트랜잭션도 처리해야 한다. 따라서 실시간 분석에 대한 요구가 늘어나고 있고, 전통적인 접근 방식은 지속 가능하지 않다.

1.2 The Single-Format Approach

  • 기존의 데이터베이스는 메모리와 디스크를 동일한 형식으로(Single-Format)으로 저장했다.
  • Row 기준으로 저장하는 방식은 OLTP성 어플리케이션에서의 트랜잭션 처리에 적합하다.
  • Column 기준으로 저장하는 방식은 분석형 데이터베이스에 적합하다.
  • 지금까지는 row와 column 중 한 가지 방식을 선택했어야 하지만, 한 가지를 선택한다면 한 형식의 장점을 취하고, 다른 형식의 장점을 잃게 된다. 따라서 mixed-use 데이터베이스에서의 성능 문제는 해결할 수 없게 된다.

1.3 The Oracle Database In-Memory Solution

1.3.1 What Is Database In-Memory?

1.3.1.1 IM Column Store

Description of “Figure 1–2 Dual-Format Database”
  • IM Column Store는 빠른 스캔을 위해 최적화된 상태로 테이블의 복사본, 파티션, 개별 컬럼을 압축하여 고유한 컬럼 형태로 유지한다.
  • 메모리 내의 SGA 영역에 위치하며, row based 스토리지나 버퍼 캐시를 보완하는 역할을 한다.
  • IM Column Store는 디스크 형태와 무관하게 추가적으로 transaction-consistent한 테이블 데이터의 복제본을 제공한다.

다음 레벨의 DDL 구문에서 INMEMORY 절을 사용하면 IM column store 을 사용할 수 있다.

  • Column (nonvirtual, virtual)
  • Table, materialized view, partition
  • Tablespace
    → 테이블스페이스 레벨에서 INMEMORY 절을 사용하면 생성되는 모든 테이블과 MView는 기본적으로 IM column store를 사용할 수 있다.

데이터베이스의 일부 또는 전체 오브젝트의 컬럼을 population 하여 IM column store을 구성할 수 있다.

1.3.1.2 Advanced Query Optimizations

  • IM expression: 자주 쓰이는 SQL expression에 대한 결과값을 IM Column Store에 저장해 놓을 수 있다. 예를 들어 last_name 이라는 컬럼이 있을 때, UPPER(last_name) 의 값을 IM Column Store에 저장해 놓을 수 있다.
  • join group: 테이블을 조인할 때 자주 사용되는 column 집합을 지정하는 user-defined object이다. 특정 쿼리에서 join group을 사용하면 데이터베이스에서 column값을 해싱하고, 압축해제 할 때 사용되는 오버헤드를 제거할 수 있다.
  • IM aggregation: VECTOR GROUP BY를 사용해서 조인 성능을 향상한다. 주로 fact table과 dimension table 사이에서 사용되며, fact table을 scan함과 동시에 aggregation을 한다.
  • repopulation: IMCU 의 데이터가 많이 수정된 후 자동으로 업데이트한다. IMCU에 오래된 항목이 있지만, 임계값을 넘진 않을 경우엔 tricle repopulation을 통해 점진적으로 IM column store을 repopulation한다.

1.3.1.3. High Availability Support

  • IM FastStart: 인스턴스가 재시작될 때 IM column store에 데이터를 채우는 시간을 줄인다. 이를 위해 주기적으로 IM column store에 저장된 columnar 포맷의 데이터의 카피본을 disk에 저장한다.
  • Oracle RAC환경의 각 노드는 자체 IM column store를 가지고 있다.
  • 12CR2 부터 Data Guard도 지원된다.

1.3.2 Improved Performance for Analytic Queries

1.3.2.1 Improved Performance for Data Scans

✔ IM column store를 사용하면 성능이 향상되는 쿼리

  • 많은 row에서 <, >, =, IN 같은 연산자로 필터를 적용하는 쿼리
  • 1000개 column중 5개만 액세스하는것과 같이 테이블에서 적은 수의 column을 선택하는 테이블이나 MView

✔ columnar format이 row-based보다 빠른 이유

  • Elimination of buffer cache overhead: 디스크에서 버퍼 캐시로 데이터를 읽어 오는 오버헤드를 줄임
  • Data pruning: 쿼리에 필요한 column만 검색, 또한 스토리지 인덱스와 내부 딕셔너리를 이용해 필요한 IMCU만 읽음
  • Compression: 공간을 절약하는 전통적인 ‘압축’이 아닌, 스캔을 가속화
  • Vector processing

✔ 예시

  • 버퍼캐시를 이용한다면 일반적으로 인덱스를 스캔해서 prod_id를 찾고, rowid를 사용해서 디스크에서 버퍼캐시로 행을 패치하고, 필요 없는 column값은 버린다. 버퍼 캐시에서 row 형식의 데이터를 스캔하려면 많은 CPU 명령이 필요하고, 효율적이지 않다.
  • IM column store를 사용한다면 디스크를 전체 스캔할 필요 없이 sales에서 요구되는 컬럼만 가져올 수 있다. column 형식의 파이프라인으로 데이터를 스캔하면 필요한 컬럼만 CPU에 전달되므로 효율적이다. 각 CPU 코어는 SIMD vector를 사용해서 로컬의 in-memory 컬럼을 스캔한다.

1.3.2.2 Improved Performance for Joins

  • Bloom filter 는 set의 멤버십을 테스트하는 low-memory 데이터 구조이다.
  • Bloom 필터는 작은 dimension 테이블을 큰 fact 테이블의 filter로 변환해서 조인 속도를 높인다.
  • 이 최적화는 한개의 fact 테이블로 여러 dimension 테이블을 조인할 때 유용하다.
  • 보통 fact 테이블이 dimension 테이블의 키를 가지고 있기 떄문에 중복되는 결과가 많다.

1.3.2.3 Improved Performance for Aggregation

  • GROUP BY 절을 사용할 때 전통적으로는 SORTHASH 연산을 사용했다.
  • 하지만 12C부터는 VECTOR GROUP BY 를 사용해서 array-based 연산을 가능하게 한다.

1.3.3 Improved Performance for Mixed Workloads

  • OLTP 어플리케이션은 IM column store을 사용했을 때 직접적인 장점은 없지만, dual-memory 포맷으로 인해 간접적인 성능 향상이 있을 수 있다.
  • 예를 들어 OLTP 어플리케이션의 성능 향상을 위해 3개의 인덱스(PK 1개, FK 2개)가 필요했고, 분석 쿼리를 위해서는 10~20개의 추가 인덱스를 필요로 했을 때, 분석 어플리케이션의 성능은 좋을 수 있지만, OLTP 성능은 느렸을 것이다. (insert마다 모든 index 업데이트 해야하므로 느려짐)
  • 하지만 IM column store에 데이터를 가져온다면 분석 access 구조를 삭제해도 되므로, 추가적인 20개의 인덱스를 줄여 업데이트 오버헤드를 줄인다.

✔ IM column store을 사용해도 성능 향상이 없는 쿼리

  • complex predicate가 있는 쿼리
  • 많은 컬럼을 반환하는 쿼리
  • 많은 row를 반환하는 쿼리

1.3.4 High Availability Support

  • IM column store은 모든 Oracle Database의 HA기능을 사용할 수 있다.
  • RAC 환경에서 각 노드는 기본적으로 고유한 IM column store를 가진다. 요구사항에 따라 각각 다른 데이터를 채울 수 있다.
    → 각 노드마다 다른 테이블 저장
    → 단일 테이블이 다른 노드로 분산
    → 테이블마다 모든 노드에서 볼 수도 있음. 예를 들어 products 테이블은 모든 노드에서 볼 수 있고, sales는 파티션단위로 분산시켜 채울 수 있음.

1.3.5 Ease of Adoption

Database In-Memory 를 채택할 때 다음과 같은 장점이 있다.

  • 간단한 배포
    → 별도의 마이그레이션이 필요하지 않다.
  • 어플리케이션 호환성
    → 어플리케이션 소스를 바꿀 필요가 없다. 옵티마이저가 자동으로 columnar format을 사용한다.
  • SQL 호환성
    → SQL을 사용할 때 제한이 없다. Oracle analytic 함수를 사용하거나 customized PL/SQL 코드를 사용할 때 모두 장점이 있다.
  • 사용의 용이성
    → 복잡한 설정이 필요 없다. INMEMORY_SIZE라는 초기화 파라미터가 얼만큼의 메모리를 IM column store을 위해 쓸 것인지를 결정한다. DDL 문에서 INMEMORY 절을 사용한 오브젝트나 컬럼은 IM column store에 저장된다. IM column store를 사용하자마자 기존의 분석이나 애드훅 쿼리를 사용할 수 있다.

1.4 Prerequisites for Database In-Memory

  • IM column store를 위한 100MB의 메모리.
    → 이 크기는 MEMORY_TARGET 안에 속함
  • RAC 데이터베이스에서 DUPLICATE 혹은 DUPLICATE ALL 옵션을 사용하려면 Oracle Engineered System이 필요하다.

2 In-Memory Column Store Architecture

2.1 Dual-Format: Column and Row

Description of “Figure 2–1 Columnar and Row-Based Storage”

2.1.1 Columnar Data in the In-Memory Area

2.1.1.1 Size of the In-Memory Area

  • In-Memory 영역은 INMEMORY_SIZE 초기화 변수에 의해 제어된다. 기본값은 0이고, 사용하지 않음을 의미한다.
  • IM column store를 사용하려면 최소한 100MB 이상으로 설정해야 한다. 이 값은 V$SGA 에서 확인할 수 있다.
Description of “Figure 2–2 INMEMORY_SIZE and SGA_TARGET”
  • In-Memory Area는 SGA_TARGET에 속한다. 예를 들어 SGA_TARGET이 10G이고, INMEMORY_SIZE를 4GB로 잡았다면, SGA의 40%가 In-Memory Area로 할당된다.
  • 자동 메모리 관리가 되는 SGA 내의 다른 요소들과는 달리, In-Memory size는 고정적이다.
  • 아래 조건을 만족할 때 12CR2부터 ALTER SYSTEM 절에 INMEMORY_SIZE을 추가해 이 사이즈를 동적으로 증가시킬 수 있다.
    → SGA에 여유 메모리가 있고
    INMEMORY_SIZE 값이 현재 설정보다 최소 128MB 더 클 때

2.1.1.2 Memory Pools in the In-Memory Area

Description of “Figure 2–3 Subpools in the In-Memory Area”
  • In-Memory 영역은 두개의 서브 풀로 나뉜다.
    → columnar data pool: 컬럼 데이터를 포함하는 IMCU를 저장함.
    → metadata pool: IM column store 내부의 오브젝트에 대한 데이터를 저장함.

2.1.2 Row Data in the Database Buffer Cache

Description of “Figure 2–4 IM Column Store”
  • 데이터베이스 버퍼 캐시는 IM column store에 관계 없이 이전과 동일한 방식으로 datablock을 저장하고 처리한다.
  • 데이터베이스는 OLTP쿼리(ex. PK 조회) 는 버퍼 캐시에 보내고, 분석 및 리포팅 쿼리는 IM column store로 보낸다.
  • dual-format 아키텍처를 사용한다 해서 메모리를 두배로 요구하지 않는다. 버퍼캐시는 데이터베이스보다 훨씬 작은 크기로 실행되도록 최적화되었다.
  • 모든 on-disk 포맷은 IM column store에서 지원되며, 이 기능을 사용한다 해도 모든 버퍼캐시, 리두로그, 언두에 영향을 끼치지 않는다.

2.2 In-Memory Storage Units

Description of “Figure 2–5 IM Column Store: Memory and Process Architecture”

2.2.1 In-Memory Compression Units (IMCUs)

기존의 테이블스페이스와 비슷한 개념

2.2.1.1 IMCUs and Schema Objects

  • IMCU는 단 하나의 객체에 대한 column 정보를 저장한다.
  • 이 쿼리를 실행하면 아래와 같이 sales 오브젝트에 대한 모든 IMCU는 sales 테이블의 7개 컬럼을 모두 갖도록 저장된다.
Description of “Figure 2–6 Columns and IMCUs”
  • 특정 컬럼을 IMCU에 저장하지 않으려면, 일단 위 쿼리처럼 모든 테이블을 INMEMORY에 저장한 다음, 일부 컬럼에 대해 NO INMEMORY 절을 사용한다.

2.2.1.1.1. In-Memory Compression

  • IM column store는 storage 크기에 대한 압축이라기 보다는, access 속도에 최적화된 특별한 압축 형식을 사용한다.
  • 이 형식은 쿼리가 압축 컬럼에 대해 직접 접근할 수 있게 한다.
  • 압축을 통한 스캔과 필터링 작업으로 훨씬 적은 양의 데이터를 처리할 수 있게 되므로 쿼리 성능이 최적화된다.
  • 모든 스캔이 끝난 후 결과 집합에 대한 데이터만 압축 해제한다.

2.2.1.1.2 IMCUs and Rows

  • 각각의 IMCU는 각 테이블 세그먼트의 모든 row subset의 column value를 저장한다. row의 subset을 granule 이라 한다.
  • 각 세그먼트의 IMCU는 대략 같은 수의 row를 포함한다. 데이터 유형, 형식, 압축 유형에 따라 자동으로 granule의 사이즈를 조절한다.
    → 압축이 잘 될 수록 IMCU의 row 수가 올라감 -> grandule 커짐
  • IMCU에서 컬럼은 정렬되지 않으며, 디스크에서 읽어온 순서로 저장된다.

2.1.1.2 Column Compression Units (CUs)

2.2.1.2.1 Structure of a CU

Description of “Figure 2–7 CUs in an IMCU”
  • IMCU는 여러개의 CU로 구성되며, 실제 데이터에 매핑되는 값을 저장한다.
  • CU는 body와 header로 나뉜다. 헤더는 CU에 대한 메타데이터를 포함하고, local dictionary를 저장한다.
    → 메타값이란 이 CU에 저장된 데이터의 Min, Max 값 등을 뜻함
  • CU는 값을 rowid순서로 저장한다.
  • 데이터가 rowid 순으로 저장되어있기 때문에 prod_id 컬럼에서 값이 5인 항목을 찾으면 다른 컬럼 값을 찾기 쉽다.

Local Dictionary

Description of “Figure 2–8 Local Dictionary”
  • local dictionary는 고유 값(distinct value)과 dictionary code를 가지고 있다.
  • 위 그림은 VEHICLES 테이블의 NAME 컬럼이다. 이 CU 내의 모든 고유값(Cadillac, Audi)는 다른 dictionary code를 갖는다.
  • CU는 기존 값이 아닌 dictionary code를 저장한다.
  • 각 IMCU 내의 CU에 대한 local dictionary 값은 다른 IMCU와 무관하다.
  • 만약 Audi를 찾는 쿼리가 나오면 데이터베이스에서는 이 IMCU에서 코드가 0인 값만 검색한다.

2.2.1.3 In-Memory Storage Indexes

  • 모든 IMCU 헤더는 해당 CU에 대한 IM storage Index를 자동으로 생성하고 관리한다.
  • IM storage index는 IMCU 내의 모든 column에 대한 최소, 최대값을 저장한다.
  • 예를 들어 sales 테이블이 IM column store에 저장될 때 prod_id가 여러 IMCU의 CU에 저장되며, IMCU 헤더에는 전체 CU값에 대한 최대, 최소값이 저장되어있다.
  • 쿼리에 대해 데이터베이스는 IMCU pruning을 시행하며, 아래 예와 같이 조건을 만족하는 IMCU만 검색한다.
Description of “Figure 2–9 Storage Index for Columnar Data”

WHERE prod_id > 14 AND prod_id < 29 라는 쿼리에 대해 Storage Index가 적용되는 모습이다.

2.2.2 Snapshot Metadata Unit (SMUs)

2.2.2.1 IMCUs and SMUs

Description of “Figure 2–10 IMCUs and SMUs”
  • data pool에는 IMCU가 저장되며, metadata pool에는SMU가 저장된다.
  • 각 IMCU는 별도의 SMU에 매핑된다.
  • SMU는 다음 정보를 포함한 IMCU에 대한 메타데이터를 저장한다.
    → Object number
    → Column number
    → Mapping information for rows

2.2.2.2 Transaction Journal

Description of “Figure 2–11 Transaction Journal”
  • 각 SMU는 transaction journal을 포함하고, 이 정보를 통해 IMCU를 일관성 있게 유지한다.
  • 데이터베이스는 IM Column Store가 활성화되지 않은 것처럼 버퍼 캐시를 사용하여 DML을 처리한다.
  • 예를 들어 UPDATE 구문으로 변경된 row가 IMCU에 저장되어 있을 때, 이 rowid를 transaction journal에 저장해서 이 데이터가 stale함을 저장한다.
  • 쿼리가 이 row 데이터를 물어보면, 데이터베이스는 버퍼캐시에서 데이터를 읽어온다.
  • IMCU는 repopulation 과정을 통해 다시 채워진다.

2.2.3 In-Memory Expression Units (IMEUs)

  • IMEU는 In-Memory Expression과 사용자 정의 가상 컬럼을 저장한다.
  • 데이터베이스는 구체화된 표현을 다른 IMCU처럼 저장한다. 개념적으로 IMEU는 IMCU의 논리적 확장 느낌이다.
    → IMCU는 다중 컬럼을 저장, IMEU는 다중 가상 컬럼을 저장
  • 일반적인 IM expression은 상수가 있는 하나 이상의 column이 포함되며, 태이블의 행과 일대일로 매핑된다.
    → 예를 들어 employees 테이블의 IMCU에는 1~1000 row data의 weekly_salary 컬럼이 저장되어 있다.
    → IMEU는 자주 쓰이는 표현인 weekly_salary*52 값을 자동으로 저장하고, 사용자가 생성한 quarterly_salary 에는 weekly_salary*12 에 대한 값을 저장한다.
    → IMCU의 3번째 row는 IMEU의 3번째 row와 매핑된다.

2.3 Expression Statistics Store (ESS)

  • ESS는 expression evaludation에 대한 통계를 저장한다.
  • IM column store가 활성화되면 ESS를 IM expression에 활용한다. 하지만 ESS는 IM Column Store 내부에 저장되지는 않고, SGA 내부에 위치한다.
  • 데이터베이스는 ESS를 사용해서 이 expression이 hot한지 판단하고, IM expression 후보가 될 수 있는지 확인한다.
  • 쿼리의 하드파싱 결과중 SELECT, WHERE, GROUP BY 절을 통해 활성화된 expression을 찾는다.
  • 각 세그먼트에 대해 ESS는 다음과 같은 통계 정보를 유지한다.
    → Frequency
    → Cost
    → Timestamp
  • 이 정보는 data dictionary에 저장해서 DBA_EXPRESSION_STATISTICS에서 확인할 수 있다.

2.4 In-Memory Process Architecture

  • In-Memory Coordinator Process (IMCO): IM Column Store에 대한 많은 작업을 관리함
  • Space Management Worker Process (Wnnn): IMCO를 대신해서 데이터를 채움

2.5 CPU Architecture: SIMD Vector Processing

  • IM column store에서 데이터를 스캔할 때, 데이터베이스는 SMID (Single Instrunction, Multiple Data) 벡터 프로세싱을 한다.
  • IM column store는 CPU가 vector 레지스터에 로드할 수 있는 컬럼의 수를 최대화한다.
  • CPU 명령어는 한번에 여러 개의 column value를 처리한다.
  • SMID 벡터 처리를 통해 데이터베이스는 초당 수십억 행을 스캔할 수 있다.
  • 예를 들어 sales 테이블에서 promo_id가 9999인 데이터의 갯수를 알고 싶고, 이 테이블이 IM Column Store 안에 존재할 때, 쿼리는 다음 그림과 같이 promo_id 값만 검색한다.
Description of “Figure 2–12 SIMD Vector Processing”

CPU는 다음과 같이 처리된다.

  1. 한번의 명령어로 promo_id 컬럼의 처음 8개 값(데이터 타입과 압축 모드에 따라 개수는 달라질 수 있음) 을 SIMD 레지스터로 가져와서 9999와 비교한다.
  2. 항목을 버린다
  3. SIMD 레지스터에 다른 8개의 값을 로드한 다음 모든 항목에 대해 비교할 때까지 계속한다.

--

--