BRIN 인덱스

BRIN 인덱스 (BRIN Indexes)

PostgreSQL에는 여러 인덱스 종류가 있는데, 그중에서도 BRIN(BLock Range Index)은 "매우 큰 테이블"을 다룰 때 특히 유용해요. 일반 인덱스(B-tree 등)는 테이블이 커지면 인덱스 자체도 커져서 관리 부담이 생기지만, BRIN은 아주 작은 인덱스로 큰 테이블의 검색 효율을 크게 올려줄 수 있죠. 다만 모든 상황에 적합한 건 아니고, 특정 조건이 맞을 때 진가를 발휘한다는 점을 이 페이지에서 함께 살펴볼게요.

출처: PostgreSQL 공식 문서 — BRIN Indexes

소개 (Introduction)

BRIN은 Block Range Index의 약자예요. 특정 컬럼의 값이 테이블 안의 물리적 위치와 자연스러운 상관관계(correlation)를 갖는 아주 큰 테이블을 처리하기 위해 설계된 인덱스죠.

BRIN은 블록 범위(block range) 또는 "페이지 범위(page range)" 단위로 동작해요. 블록 범위란 테이블에서 물리적으로 인접한 페이지들의 묶음인데, 각 블록 범위마다 인덱스에 요약 정보(summary info)가 저장됩니다. 예를 들어 매장의 판매 주문 테이블에 주문 날짜 컬럼이 있다고 해볼게요. 대부분의 경우 이른 주문일수록 테이블 앞쪽에 있는 경향이 있죠. 마찬가지로 우편번호(ZIP code) 컬럼을 저장하는 테이블이라면, 같은 도시의 코드들이 자연스럽게 묶여 있을 거예요. 이런 식으로 값이 물리적 순서와 함께 정렬되는 성격일 때 BRIN이 효과적인 거예요.

BRIN 인덱스는 일반적인 **비트맵 인덱스 스캔(bitmap index scan)**으로 쿼리를 처리할 수 있어요. 인덱스에 저장된 요약 정보가 쿼리 조건과 *일치(consistent)*하면 해당 범위 안의 모든 페이지의 모든 튜플을 반환하죠. 이때 쿼리 실행기가 튜플을 다시 검사(recheck)해서 조건에 맞지 않는 것을 걸러내는데, 즉 이 인덱스는 **손실적(lossy)**이에요. BRIN 인덱스는 크기가 아주 작아서 인덱스 스캔의 오버헤드가 순차 스캔(sequential scan)에 비해 거의 없으면서도, 일치하는 튜플이 없다고 알려진 테이블의 큰 영역은 스캔하지 않고 건너뛸 수 있어요.

BRIN 인덱스가 실제로 저장하는 데이터와 처리할 수 있는 쿼리 종류는, 인덱스의 각 컬럼에 선택한 연산자 클래스(operator class) 에 따라 달라져요. 예를 들어 선형 정렬 순서(linear sort order)를 갖는 데이터 타입은 각 블록 범위의 최솟값과 최댓값을 저장하는 연산자 클래스를 쓸 수 있고, 기하(geometrical) 타입은 블록 범위 안 모든 객체의 경계 상자(bounding box)를 저장할 수 있죠.

블록 범위의 크기는 인덱스 생성 시점에 pages_per_range 저장 파라미터로 정해요. 인덱스 엔트리의 개수는 테이블의 페이지 수를 선택한 pages_per_range 값으로 나눈 것과 같아져요. 따라서 이 숫자가 작을수록 인덱스는 커지지만(더 많은 인덱스 엔트리를 저장해야 하니), 동시에 저장되는 요약 데이터는 더 정밀해지고 인덱스 스캔 중에 건너뛸 수 있는 데이터 블록도 많아집니다.

인덱스 유지보수 (Index Maintenance)

BRIN 인덱스를 막 생성할 때는 기존의 모든 힙(heap) 페이지를 스캔해서, 마지막의 불완전한 범위를 포함해 각 범위마다 요약 인덱스 튜플(summary index tuple)을 만들어요. 그리고 새 페이지에 데이터가 채워지면, 이미 요약된 페이지 범위는 새 튜플의 데이터로 요약 정보를 갱신하죠. 만약 마지막으로 요약된 범위에 속하지 않는 새 페이지가 생기면, 그 페이지가 속한 범위는 자동으로 요약 튜플을 얻지 못해요. 그런 튜플은 나중에 요약(summarization) 작업이 실행되어 해당 범위의 최초 요약을 만들 때까지 **요약되지 않은 상태(unsummarized)**로 남아 있게 됩니다.

페이지 범위의 최초 요약을 트리거하는 방법은 여러 가지예요. 테이블을 수동으로 vacuum 하거나 autovacuum이 처리하면, 요약되지 않은 모든 페이지 범위가 요약돼요. 또한 인덱스의 autosummarize 파라미터를 켜면(기본값은 꺼져 있어요) 데이터베이스에서 autovacuum이 실행될 때마다, 테이블 자체가 autovacuum에 의해 처리되는지와 관계없이 채워진 모든 요약되지 않은 페이지 범위에 대해 요약이 수행됩니다. 자세한 내용은 아래를 참고할게요.

마지막으로, 다음 함수들을 직접 호출할 수도 있어요. 이 함수들이 실행되는 동안엔 search_path가 잠시 pg_catalog, pg_temp로 바뀐다는 점만 알아두면 돼요.

함수 설명
brin_summarize_new_values(regclass) 요약되지 않은 모든 범위를 요약합니다.
brin_summarize_range(regclass, bigint) 주어진 페이지가 포함된 범위만, 아직 요약되지 않았다면 요약합니다.

자동 요약(autosummarization)이 켜져 있으면, 다음 블록 범위의 첫 페이지에 첫 번째 항목이 삽입되는 것이 감지될 때 autovacuum에 해당 블록 범위의 표적 요약을 요청해요. 이 요청은 같은 데이터베이스에서 autovacuum 워커가 다음에 실행을 끝낼 때 처리되죠. 그런데 요청 큐가 꽉 차 있으면 요청이 기록되지 않고 서버 로그에 메시지가 남습니다.

LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was not recorded

이 경우 해당 범위는 테이블의 다음 정기 vacuum 실행이나, 위에서 언급한 함수 호출이 있을 때까지 요약되지 않은 상태로 남아요.

반대로 범위를 요약 해제(de-summarize)할 수도 있어요. 기존 값이 변해서 인덱스 튜플이 더 이상 좋은 표현이 아니게 됐을 때 brin_desummarize_range(regclass, bigint) 함수를 쓰면 됩니다. 자세한 내용은 Section 9.28.8 — Index Maintenance Functions를 참고할게요.

내장 연산자 클래스 (Built-in Operator Classes)

PostgreSQL 핵심 배포판에는 다음 표의 BRIN 연산자 클래스들이 포함돼 있어요.

  • minmax 연산자 클래스: 범위 안에서 인덱스된 컬럼에 나타난 최솟값과 최댓값을 저장해요.
  • inclusion 연산자 클래스: 범위 안 인덱스된 컬럼의 값을 포함하는 값을 저장해요.
  • bloom 연산자 클래스: 범위 안 모든 값의 Bloom 필터를 만들어요.
  • minmax-multi 연산자 클래스: 범위 안 인덱스된 컬럼에 나타난 값을 나타내는 여러 개의 최솟값/최댓값을 저장해요.

Table 65.4. Built-in BRIN Operator Classes

이름 (Name) 인덱스 가능 연산자 (Indexable Operators)
bit_minmax_ops =, <, >, <=, >= (bit,bit)
box_inclusion_ops @> (box,point), <<, &<, &>, >>, <@, @>, ~=, &&, `<<
bpchar_bloom_ops = (character,character)
bpchar_minmax_ops =, <, <=, >, >= (character,character)
bytea_bloom_ops = (bytea,bytea)
bytea_minmax_ops =, <, <=, >, >= (bytea,bytea)
char_bloom_ops = ("char","char")
char_minmax_ops =, <, <=, >, >= ("char","char")
date_bloom_ops = (date,date)
date_minmax_ops =, <, <=, >, >= (date,date)
date_minmax_multi_ops =, <, <=, >, >= (date,date)
float4_bloom_ops = (float4,float4)
float4_minmax_ops =, <, >, <=, >= (float4,float4)
float4_minmax_multi_ops =, <, >, <=, >= (float4,float4)
float8_bloom_ops = (float8,float8)
float8_minmax_ops =, <, <=, >, >= (float8,float8)
float8_minmax_multi_ops =, <, <=, >, >= (float8,float8)
inet_inclusion_ops <<, <<=, >>, >>=, =, && (inet,inet)
inet_bloom_ops = (inet,inet)
inet_minmax_ops =, <, <=, >, >= (inet,inet)
inet_minmax_multi_ops =, <, <=, >, >= (inet,inet)
int2_bloom_ops = (int2,int2)
int2_minmax_ops =, <, >, <=, >= (int2,int2)
int2_minmax_multi_ops =, <, >, <=, >= (int2,int2)
int4_bloom_ops = (int4,int4)
int4_minmax_ops =, <, >, <=, >= (int4,int4)
int4_minmax_multi_ops =, <, >, <=, >= (int4,int4)
int8_bloom_ops = (bigint,bigint)
int8_minmax_ops =, <, >, <=, >= (bigint,bigint)
int8_minmax_multi_ops =, <, >, <=, >= (bigint,bigint)
interval_bloom_ops = (interval,interval)
interval_minmax_ops =, <, <=, >, >= (interval,interval)
interval_minmax_multi_ops =, <, <=, >, >= (interval,interval)
macaddr_bloom_ops = (macaddr,macaddr)
macaddr_minmax_ops =, <, <=, >, >= (macaddr,macaddr)
macaddr_minmax_multi_ops =, <, <=, >, >= (macaddr,macaddr)
macaddr8_bloom_ops = (macaddr8,macaddr8)
macaddr8_minmax_ops =, <, <=, >, >= (macaddr8,macaddr8)
macaddr8_minmax_multi_ops =, <, <=, >, >= (macaddr8,macaddr8)
name_bloom_ops = (name,name)
name_minmax_ops =, <, <=, >, >= (name,name)
numeric_bloom_ops = (numeric,numeric)
numeric_minmax_ops =, <, <=, >, >= (numeric,numeric)
numeric_minmax_multi_ops =, <, <=, >, >= (numeric,numeric)
oid_bloom_ops = (oid,oid)
oid_minmax_ops =, <, >, <=, >= (oid,oid)
oid_minmax_multi_ops =, <, >, <=, >= (oid,oid)
pg_lsn_bloom_ops = (pg_lsn,pg_lsn)
pg_lsn_minmax_ops =, <, >, <=, >= (pg_lsn,pg_lsn)
pg_lsn_minmax_multi_ops =, <, >, <=, >= (pg_lsn,pg_lsn)
range_inclusion_ops =, <, <=, >=, >, &&, @>, <@, <<, >>, &<, &>, `-
text_bloom_ops = (text,text)
text_minmax_ops =, <, <=, >, >= (text,text)
tid_bloom_ops = (tid,tid)
tid_minmax_ops =, <, >, <=, >= (tid,tid)
tid_minmax_multi_ops =, <, >, <=, >= (tid,tid)
timestamp_bloom_ops = (timestamp,timestamp)
timestamp_minmax_ops =, <, <=, >, >= (timestamp,timestamp)
timestamp_minmax_multi_ops =, <, <=, >, >= (timestamp,timestamp)
timestamptz_bloom_ops = (timestamptz,timestamptz)
timestamptz_minmax_ops =, <, <=, >, >= (timestamptz,timestamptz)
timestamptz_minmax_multi_ops =, <, <=, >, >= (timestamptz,timestamptz)
time_bloom_ops = (time,time)
time_minmax_ops =, <, <=, >, >= (time,time)
time_minmax_multi_ops =, <, <=, >, >= (time,time)
timetz_bloom_ops = (timetz,timetz)
timetz_minmax_ops =, <, <=, >, >= (timetz,timetz)
timetz_minmax_multi_ops =, <, <=, >, >= (timetz,timetz)
uuid_bloom_ops = (uuid,uuid)
uuid_minmax_ops =, <, >, <=, >= (uuid,uuid)
uuid_minmax_multi_ops =, <, >, <=, >= (uuid,uuid)
varbit_minmax_ops =, <, >, <=, >= (varbit,varbit)

연산자 클래스 파라미터 (Operator Class Parameters)

일부 내장 연산자 클래스는 동작에 영향을 주는 파라미터를 지정할 수 있어요. 연산자 클래스마다 허용되는 파라미터 집합이 다르고, 파라미터 지정이 가능한 건 bloomminmax-multi 연산자 클래스뿐입니다.

bloom 연산자 클래스가 받는 파라미터는 다음과 같아요.

n_distinct_per_range

블록 범위 안의 서로 다른(distinct) non-null 값의 예상 개수를 정의해요. BRIN bloom 인덱스가 Bloom 필터의 크기를 정할 때 사용하죠. ALTER TABLEn_distinct 옵션과 비슷하게 동작해요. 양수로 설정하면 각 블록 범위에 그만큼의 서로 다른 non-null 값이 있다고 가정하고, 음수(-1 이상)로 설정하면 블록 범위의 최대 가능 튜플 수(블록당 약 290행)에 따라 서로 다른 non-null 값의 수가 선형으로 증가한다고 가정해요. 기본값은 -0.1이고, 서로 다른 non-null 값의 최솟값은 16이에요.

false_positive_rate

BRIN bloom 인덱스가 Bloom 필터 크기를 정할 때 사용하는 목표 거짓 양성률(false positive rate)을 정의해요. 값은 0.0001에서 0.25 사이여야 하고, 기본값은 0.01, 즉 1%의 거짓 양성률이에요.

minmax-multi 연산자 클래스가 받는 파라미터는 다음과 같아요.

values_per_range

BRIN minmax 인덱스가 블록 범위를 요약할 때 저장하는 값의 최대 개수를 정의해요. 각 값은 점(point) 또는 구간의 경계(boundary)를 나타낼 수 있어요. 값은 8에서 256 사이여야 하고 기본값은 32예요.

확장성 (Extensibility)

BRIN 인터페이스는 추상화 수준이 높아서, 접근 메서드(access method) 구현자는 접근하려는 데이터 타입의 의미론(semantics)만 구현하면 돼요. BRIN 레이어 자체가 동시성(concurrency), 로깅, 인덱스 구조 검색을 처리해 주죠.

BRIN 접근 메서드를 동작시키기 위해 필요한 것은, 인덱스에 저장되는 요약 값의 동작과 스캔 키(scan key)와의 상호작용 방식을 정의하는 몇 가지 사용자 정의 메서드를 구현하는 것뿐이에요. 요컨대 BRIN은 확장성과 일반성, 코드 재사용, 그리고 깔끔한 인터페이스를 결합하고 있는 셈이죠.

BRIN 연산자 클래스가 반드시 제공해야 하는 메서드는 네 가지예요.

BrinOpcInfo *opcInfo(Oid type_oid)

인덱스된 컬럼의 요약 데이터에 대한 내부 정보를 반환해요. 반환 값은 palloc으로 할당된 BrinOpcInfo를 가리켜야 하는데, 정의는 다음과 같아요.

typedef struct BrinOpcInfo
{
    /* Number of columns stored in an index column of this opclass */
    uint16      oi_nstored;

    /* Opaque pointer for the opclass' private use */
    void       *oi_opaque;

    /* Type cache entries of the stored columns */
    TypeCacheEntry *oi_typcache[FLEXIBLE_ARRAY_MEMBER];
} BrinOpcInfo;

BrinOpcInfo.oi_opaque는 인덱스 스캔 중 지원 함수들이 서로 정보를 주고받을 때 연산자 클래스 루틴이 사용할 수 있어요.

bool consistent(BrinDesc *bdesc, BrinValues *column, ScanKey *keys, int nkeys)

주어진 범위에 대해 모든 ScanKey 엔트리가 주어진 인덱스된 값과 일치하는지 반환해요. 사용할 속성 번호는 스캔 키의 일부로 전달되고, 같은 속성에 대한 여러 스캔 키가 한 번에 전달될 수 있어요. 엔트리 수는 nkeys 파라미터로 결정됩니다.

bool consistent(BrinDesc *bdesc, BrinValues *column, ScanKey key)

주어진 범위에 대해 ScanKey가 주어진 인덱스된 값과 일치하는지 반환해요. 속성 번호는 스캔 키의 일부로 전달되죠. 이건 consistent 함수의 이전 버전으로, 하위 호환성을 위해 유지되는 형태예요.

bool addValue(BrinDesc *bdesc, BrinValues *column, Datum newval, bool isnull)

인덱스 튜플과 인덱스된 값을 받아, 튜플의 해당 속성을 새 값을 표현하도록 수정해요. 튜플에 어떤 수정이라도 가해졌다면 true를 반환합니다.

bool unionTuples(BrinDesc *bdesc, BrinValues *a, BrinValues *b)

두 인덱스 튜플을 통합해요. 두 인덱스 튜플을 받아 첫 번째 튜플의 해당 속성을 두 튜플을 모두 표현하도록 수정하죠. 두 번째 튜플은 수정되지 않아요.

BRIN 연산자 클래스는 다음 메서드를 선택적으로 지정할 수 있어요.

void options(local_relopts *relopts)

연산자 클래스 동작을 제어하는 사용자에게 보이는 파라미터 집합을 정의해요. options 함수는 local_relopts 구조체 포인터를 받는데, 이 구조체에 연산자 클래스 고유의 옵션 집합을 채우면 돼요. 이 옵션들은 PG_HAS_OPCLASS_OPTIONS()PG_GET_OPCLASS_OPTIONS() 매크로로 다른 지원 함수에서 접근할 수 있어요. 인덱스된 값의 키 추출과 BRIN에서의 키 표현이 모두 유연하므로, 이런 것들이 사용자 지정 파라미터에 의존할 수 있기 때문이에요.

핵심 배포판은 minmax, minmax-multi, inclusion, bloom 네 종류의 연산자 클래스를 지원해요. 이들을 사용하는 연산자 클래스 정의는 코어 데이터 타입에 맞게 제공되고, 사용자는 소스 코드를 한 줄도 작성하지 않고도 다른 데이터 타입에 대해 동등한 정의로 추가 연산자 클래스를 만들 수 있어요. 적절한 카탈로그 엔트리를 선언하는 것만으로 충분하죠. 다만 연산자 전략(operator strategy)의 의미에 대한 가정은 지원 함수의 소스 코드에 박혀 있다는 점을 알아두세요.

완전히 다른 의미론을 구현하는 연산자 클래스도 가능해요. 위에서 설명한 네 가지 주요 지원 함수의 구현만 작성하면 되거든요. 단, 메이저 릴리스 간 하위 호환성은 보장되지 않는다는 점을 유의하세요. 예를 들어 이후 릴리스에서 추가 지원 함수가 요구될 수도 있어요.

완전히 정렬된 집합(totally ordered set)을 구현하는 데이터 타입에 연산자 클래스를 작성하려면, Table 65.5 — Minmax Operator Classes에 나온 대로 minmax 지원 함수를 해당 연산자와 함께 쓰면 됩니다. 이때 모든 연산자 클래스 멤버(함수와 연산자)는 필수예요.

Table 65.5. Function and Support Numbers for Minmax Operator Classes

연산자 클래스 멤버 객체 (Object)
Support Function 1 내부 함수 brin_minmax_opcinfo()
Support Function 2 내부 함수 brin_minmax_add_value()
Support Function 3 내부 함수 brin_minmax_consistent()
Support Function 4 내부 함수 brin_minmax_union()
Operator Strategy 1 연산자 less-than
Operator Strategy 2 연산자 less-than-or-equal-to
Operator Strategy 3 연산자 equal-to
Operator Strategy 4 연산자 greater-than-or-equal-to
Operator Strategy 5 연산자 greater-than

어떤 타입에 포함되는 값을 갖는 복잡한 데이터 타입에 연산자 클래스를 작성하려면, Table 65.6 — Inclusion Operator Classes에 나온 대로 inclusion 지원 함수를 해당 연산자와 함께 사용할 수 있어요. 이 경우 단 하나의 추가 함수만 필요하며, 그 함수는 어떤 언어로든 작성할 수 있어요. 추가 기능을 위해 더 많은 함수를 정의할 수도 있고, 모든 연산자는 선택사항(optional)이며 일부 연산자는 표의 의존성(dependency)처럼 다른 연산자를 요구해요.

Table 65.6. Function and Support Numbers for Inclusion Operator Classes

연산자 클래스 멤버 객체 (Object) 의존성 (Dependency)
Support Function 1 내부 함수 brin_inclusion_opcinfo()
Support Function 2 내부 함수 brin_inclusion_add_value()
Support Function 3 내부 함수 brin_inclusion_consistent()
Support Function 4 내부 함수 brin_inclusion_union()
Support Function 11 두 요소를 병합하는 함수
Support Function 12 두 요소가 병합 가능한지 확인하는 선택 함수
Support Function 13 한 요소가 다른 요소에 포함되는지 확인하는 선택 함수
Support Function 14 요소가 비어 있는지 확인하는 선택 함수
Operator Strategy 1 연산자 left-of Operator Strategy 4
Operator Strategy 2 연산자 does-not-extend-to-the-right-of Operator Strategy 5
Operator Strategy 3 연산자 overlaps
Operator Strategy 4 연산자 does-not-extend-to-the-left-of Operator Strategy 1
Operator Strategy 5 연산자 right-of Operator Strategy 2
Operator Strategy 6, 18 연산자 same-as-or-equal-to Operator Strategy 7
Operator Strategy 7, 16, 24, 25 연산자 contains-or-equal-to
Operator Strategy 8, 26, 27 연산자 is-contained-by-or-equal-to Operator Strategy 3
Operator Strategy 9 연산자 does-not-extend-above Operator Strategy 11
Operator Strategy 10 연산자 is-below Operator Strategy 12
Operator Strategy 11 연산자 is-above Operator Strategy 9
Operator Strategy 12 연산자 does-not-extend-below Operator Strategy 10
Operator Strategy 20 연산자 less-than Operator Strategy 5
Operator Strategy 21 연산자 less-than-or-equal-to Operator Strategy 5
Operator Strategy 22 연산자 greater-than Operator Strategy 1
Operator Strategy 23 연산자 greater-than-or-equal-to Operator Strategy 1

지원 함수 번호 1부터 10은 BRIN 내부 함수용으로 예약돼 있어서, SQL 레벨 함수는 11번부터 시작해요. Support Function 11은 인덱스를 만드는 데 필요한 주요 함수로, 연산자 클래스와 같은 데이터 타입의 두 인자를 받아 둘의 합집합을 반환해야 해요. inclusion 연산자 클래스는 STORAGE 파라미터로 정의하면 다른 데이터 타입의 합집합 값도 저장할 수 있는데, 이때 합집합 함수의 반환 값은 STORAGE 데이터 타입과 일치해야 합니다.

Support Function 12와 14는 내장 데이터 타입의 불규칙성을 지원하기 위해 제공돼요. 함수 12는 병합할 수 없는 서로 다른 패밀리의 네트워크 주소를 지원하는 데 쓰이고, 함수 14는 빈 범위(empty range)를 지원해요. 함수 13은 선택사항이지만 권장되는데, 새 값을 합집합 함수에 전달하기 전에 확인할 수 있게 해줘요. BRIN 프레임워크는 합집합이 바뀌지 않을 때 일부 연산을 건너뛸 수 있기 때문에, 이 함수를 쓰면 인덱스 성능을 개선할 수 있어요.

오직 동등 연산자(equality)만 구현하고 해싱(hashing)을 지원하는 데이터 타입에 연산자 클래스를 작성하려면, Table 65.7 — Bloom Operator Classes에 나온 대로 bloom 지원 프로시저를 해당 연산자와 함께 사용할 수 있어요. 이때 모든 연산자 클래스 멤버(프로시저와 연산자)는 필수입니다.

Table 65.7. Procedure and Support Numbers for Bloom Operator Classes

연산자 클래스 멤버 객체 (Object)
Support Procedure 1 내부 함수 brin_bloom_opcinfo()
Support Procedure 2 내부 함수 brin_bloom_add_value()
Support Procedure 3 내부 함수 brin_bloom_consistent()
Support Procedure 4 내부 함수 brin_bloom_union()
Support Procedure 5 내부 함수 brin_bloom_options()
Support Procedure 11 요소의 해시를 계산하는 함수
Operator Strategy 1 연산자 equal-to

지원 프로시저 번호 1-10은 BRIN 내부 함수용으로 예약돼 있으므로 SQL 레벨 함수는 11번부터 시작해요. Support Function 11은 인덱스를 만드는 데 필요한 주요 함수로, 연산자 클래스와 같은 데이터 타입의 인자를 하나 받아 해당 값의 해시를 반환해야 해요.

minmax-multi 연산자 클래스 역시 완전히 정렬된 집합을 구현하는 데이터 타입을 대상으로 하며, minmax 연산자 클래스의 단순한 확장으로 볼 수 있어요. minmax 연산자 클래스가 각 블록 범위의 값을 하나의 연속된 구간으로 요약하는 반면, minmax-multi는 여러 개의 더 작은 구간으로 요약해서 이상치(outlier) 값을 더 잘 처리할 수 있게 해주죠. Table 65.8 — minmax-multi Operator Classes에 나온 대로 minmax-multi 지원 프로시저를 해당 연산자와 함께 사용할 수 있어요. 이때 모든 연산자 클래스 멤버(프로시저와 연산자)는 필수입니다.

Table 65.8. Procedure and Support Numbers for minmax-multi Operator Classes

연산자 클래스 멤버 객체 (Object)
Support Procedure 1 내부 함수 brin_minmax_multi_opcinfo()
Support Procedure 2 내부 함수 brin_minmax_multi_add_value()
Support Procedure 3 내부 함수 brin_minmax_multi_consistent()
Support Procedure 4 내부 함수 brin_minmax_multi_union()
Support Procedure 5 내부 함수 brin_minmax_multi_options()
Support Procedure 11 두 값 사이의 거리(범위의 길이)를 계산하는 함수
Operator Strategy 1 연산자 less-than
Operator Strategy 2 연산자 less-than-or-equal-to
Operator Strategy 3 연산자 equal-to
Operator Strategy 4 연산자 greater-than-or-equal-to
Operator Strategy 5 연산자 greater-than

minmax와 inclusion 연산자 클래스 모두 데이터 타입 간(cross-data-type) 연산자를 지원하지만, 이 경우 의존성이 더 복잡해져요. minmax 연산자 클래스는 두 인자가 모두 같은 데이터 타입인 완전한 연산자 집합을 정의해야 하고, 추가 데이터 타입은 별도의 연산자 집합을 정의해 지원할 수 있어요. inclusion 연산자 클래스의 연산자 전략은 Table 65.6처럼 다른 연산자 전략에 의존하거나, 자기 자신과 같은 전략에 의존해요. 이들은 의존 연산자가 STORAGE 데이터 타입을 왼쪽 인자로, 지원되는 다른 데이터 타입을 오른쪽 인자로 갖도록 정의되어야 해요. minmax의 예로 float4_minmax_ops, inclusion의 예로 box_inclusion_ops를 참고하면 좋아요.

더 알아보기 (Learn more)