Hybrid Vector Index

2025. 6. 28.·Oracle/Vector

Oracle 23ai(23.6)에 하이브리드 벡터 인덱스 기능이 추가되었습니다. 하이브리드 벡터 인덱스를 사용하하면 전체 텍스트 검색(full-text search)과 벡터 유사도 검색(vector similarity search)을 조합하여 데이터를 조회할 수 있습니다.

 

테스트를 위해 예제 데이터를 다운로드하고 아래와 같이 압축을 해제하겠습니다.

$ unzip /home/oracle/wiki/vector_database_wikipedia_articles_embedded.zip
Archive:  vector_database_wikipedia_articles_embedded.zip
  inflating: vector_database_wikipedia_articles_embedded.csv
  inflating: __MACOSX/._vector_database_wikipedia_articles_embedded.csv

 

아래와 같이 External 테이블를 사용하여 vector_database_wikipedia_articles_embedded.csv 파일을 wiki 테이블로 적재하겠습니다. 적재된 로우 수는 25,000 건입니다.

-- 1-1
DROP DIRECTORY dir_wiki;

CREATE DIRECTORY dir_wiki AS '/home/oracle/wiki';

-- 1-2
DROP TABLE ext_wiki PURGE;

CREATE TABLE ext_wiki (
    id             NUMBER
  , url            VARCHAR2(200)
  , title          VARCHAR2(100)
  , text           CLOB
  , title_vector   CLOB
  , content_vector CLOB
  , vector_id      NUMBER
)
ORGANIZATION EXTERNAL (
    TYPE ORACLE_LOADER
    DEFAULT DIRECTORY dir_wiki
    ACCESS PARAMETERS (
        RECORDS
        SKIP 1
        FIELDS CSV WITH EMBEDDED
        MISSING FIELD VALUES ARE NULL (
            id
          , url
          , title
          , text           CHAR(1000000000)
          , title_vector   CHAR(1000000000)
          , content_vector CHAR(1000000000)
          , vector_id
        )
    )
    LOCATION ('vector_database_wikipedia_articles_embedded.csv')
);

-- 1-3
DROP TABLE wiki PURGE;

CREATE TABLE wiki AS SELECT id, url, title, text FROM ext_wiki;

-- 1-4
SELECT COUNT (*) AS cnt FROM wiki;

  CNT
-----
25000

1 row selected.

 

하이브리드 벡터 인덱스는 벡터 검색, 텍스트 검색, 인덱스 유지 관리에 대한 설정을 할 수 있습니다. 아래와 같이 DBMS_VECTOR_CHAIN.CREATE_PREFERENCE 프로시저로 하이브리드 벡터 인덱스에 대한 vectorizer preference를 생성하고, CREATE HYBRID VECTOR INDEX 문으로 하이브리드 벡터 인덱스를 생성하겠습니다. (25,000 건에 대해 64 DOP로 7분이 소요됨) 예제에 사용한 all_minilm_l12_v2 모델은 ONNX Model 글을 참고하세요.

-- 2-1
-- CREATE HYBRID VECTOR INDEX wiki_x1 ON wiki (text) PARAMETERS ('MODEL all_minilm_l12_v2');

-- 2-2: set as default
BEGIN
    DBMS_VECTOR_CHAIN.CREATE_PREFERENCE (
        pref_name => 'WIKI_PREF'
      , pref_type => DBMS_VECTOR_CHAIN.VECTORIZER
      , params    => JSON (
            '{
                 "model": "all_minilm_l12_v2",
                 "by": "words",
                 "max": 100,
                 "split": "recursively",
                 "overlap": 0,
                 "distance": "cosine",
                 "accuracy": 95,
                 "vector_idxtype": "ivf"
             }'
        ));
END;
/

-- 2-3
DROP INDEX wiki_x1 FORCE;

CREATE HYBRID VECTOR INDEX wiki_x1 ON wiki (text) PARAMETERS ('VECTORIZER wiki_pref');

 

하이브리드 벡터 인덱스는 도메인 인덱스로 생성되며 다수의 보조 테이블이 자동 생성됩니다.

-- 3-1
SELECT table_name, index_name, index_type, index_subtype
  FROM user_indexes
 WHERE index_name LIKE '%WIKI%';

TABLE_NAME    INDEX_NAME     INDEX_TYPE INDEX_SUBTYPE
------------- -------------- ---------- -----------------------
WIKI          WIKI_X1        DOMAIN
DR$WIKI_X1$D  DR$WIKI_X1$DI  NORMAL
DR$WIKI_X1$G  DR$WIKI_X1$H   NORMAL
DR$WIKI_X1$I  DR$WIKI_X1$X   NORMAL
DR$WIKI_X1$K  DR$WIKI_X1$KD  NORMAL
DR$WIKI_X1$K  DR$WIKI_X1$KR  NORMAL
DR$WIKI_X1$N  DR$WIKI_X1$NI  NORMAL
DR$WIKI_X1$U  DR$WIKI_X1$UI  NORMAL
DR$WIKI_X1$VR DR$WIKI_X1$VI  VECTOR     NEIGHBOR_PARTITIONS_IVF
DR$WIKI_X1$VR DR$WIKI_X1$VRI NORMAL

10 rows selected.

-- 3-2
SELECT table_name, partitioned, secondary
  FROM user_tables
 WHERE table_name LIKE '%WIKI%';

TABLE_NAME                                                      PARTITIONED SECONDARY
--------------------------------------------------------------- ----------- ---------
EXT_WIKI                                                        NO          N
WIKI                                                            NO          N
DR$WIKI_X1$B                                                    NO          Y
DR$WIKI_X1$C                                                    NO          Y
DR$WIKI_X1$D                                                    NO          Y
DR$WIKI_X1$G                                                    NO          Y
DR$WIKI_X1$I                                                    NO          Y
DR$WIKI_X1$K                                                    NO          Y
DR$WIKI_X1$N                                                    NO          Y
DR$WIKI_X1$Q                                                    NO          Y
DR$WIKI_X1$U                                                    NO          Y
DR$WIKI_X1$VR                                                   NO          Y
VECTOR$DR$WIKI_X1$VI$65402_65413_0$IVF_FLAT_CENTROIDS           NO          N
VECTOR$DR$WIKI_X1$VI$65402_65413_0$IVF_FLAT_CENTROID_PARTITIONS YES         N

14 rows selected.

-- 3-3
SELECT view_name
  FROM user_views
 WHERE view_name LIKE '%WIKI%';

VIEW_NAME
---------------
WIKI_X1$VECTORS

1 row selected.

 

wiki_x1$vectors 뷰를 조회하면 청크 정보를 조회할 수 있습니다.

-- 4-1
SELECT *
  FROM wiki
 WHERE id = 1;

ID URL                                     TITLE TEXT
-- --------------------------------------- ----- --------------------------------------------------------------------------------
 1 https://simple.wikipedia.org/wiki/April April April is the fourth month of the year in the Julian and Gregorian calendars, ...

1 row selected.

-- 4-2
SELECT *
  FROM wiki_x1$vectors
 WHERE DOC_ROWID = (SELECT ROWID FROM wiki WHERE id = 1);

DOC_ROWID          DOC_CHUNK_ID DOC_CHUNK_COUNT DOC_CHUNK_OFFSET DOC_CHUNK_LENGTH DOC_CHUNK_TEXT                   DOC_CHUNK_EMBEDDING
------------------ ------------ --------------- ---------------- ---------------- -------------------------------- ---------------------
AAAP9lAAAAABAArAAA            1              40                1              437 April is the fourth month of ... [-8.13307762E-002,...
AAAP9lAAAAABAArAAA            2              40              438              205 April comes between March and... [2.67323982E-002,...
...
40 rows selected.

---------------------------------------------------------
| Id  | Operation                       | Name          |
---------------------------------------------------------
|   0 | SELECT STATEMENT                |               |
|*  1 |  HASH JOIN RIGHT ANTI NA        |               |
|   2 |   VIEW                          | VW_NSO_1      |
|*  3 |    FILTER                       |               |
|   4 |     SORT GROUP BY               |               |
|*  5 |      VIEW                       |               |
|   6 |       WINDOW SORT               |               |
|   7 |        TABLE ACCESS STORAGE FULL| DR$WIKI_X1$C  |
|*  8 |   HASH JOIN                     |               |
|   9 |    TABLE ACCESS STORAGE FULL    | DR$WIKI_X1$K  |
|  10 |    TABLE ACCESS STORAGE FULL    | DR$WIKI_X1$VR |
---------------------------------------------------------

 

아래 예제는 DBMS_HYBRID_VECTOR.SEARCH 함수로 What have scientists discovered? 검색 키워드와 elements 텍스트가 포함된 상위 5개의 로우를 조회합니다. ROWID, 점수, 벡터 점수, 텍스트 점수가 포함된 JSON 값이 반환됩니다.

-- 5
SELECT JSON_SERIALIZE (DBMS_HYBRID_VECTOR.SEARCH (JSON (
           '{
                "hybrid_index_name": "wiki_x1",
                "search_scorer": "rsf",
                "search_fusion": "INTERSECT",
                "vector": {
                    "search_text": "What have scientists discovered?",
                    "search_mode": "DOCUMENT",
                    "aggregator": "MAX",
                    "score_weight": 1
                },
                "text": {
                    "contains": "$elements",
                    "score_weight": 1
                },
                "return": {
                    "values": ["rowid", "score", "vector_score", "text_score"],
                    "topN": 5
                }
            }'
       )) RETURNING CLOB PRETTY) AS result;

RESULT
-----------------------------------
[
  {
    "rowid" : "AAAP9lAAAAABAAuAAE",
    "score" : 56.7,
    "vector_score" : 64.4,
    "text_score" : 49
  },
  {
    "rowid" : "AAAP9lAAAAABAA0AAI",
    "score" : 53.24,
    "vector_score" : 65.48,
    "text_score" : 41
  },
  {
    "rowid" : "AAAP9lAAAAABAB3AAC",
    "score" : 53.16,
    "vector_score" : 65.32,
    "text_score" : 41
  },
  {
    "rowid" : "AAAP9lAAAAABAGUAAA",
    "score" : 47.83,
    "vector_score" : 71.66,
    "text_score" : 24
  },
  {
    "rowid" : "AAAP9lAAAAABABGAAH",
    "score" : 45.82,
    "vector_score" : 67.64,
    "text_score" : 24
  }
]

1 row selected.

 

아래 예제는 JSON_TABLE 함수를 사용하여 하이브리드 벡터 인덱스 검색 결과와 wiki 테이블을 조인한 결과를 보여줍니다. cnt 열은 text 칼럼에 elements 텍스트가 포함된 개수를 나타냅니다.

-- 6
SELECT b.score, b.vector_score, b.text_score, c.id, c.url, c.title, REGEXP_COUNT (c.text, 'elements') AS cnt
  FROM (SELECT DBMS_HYBRID_VECTOR.SEARCH (JSON (
                   '{
                        "hybrid_index_name": "wiki_x1",
                        "search_scorer": "rsf",
                        "search_fusion": "INTERSECT",
                        "vector": {
                            "search_text": "What have scientists discovered?",
                            "search_mode": "DOCUMENT",
                            "aggregator": "MAX",
                            "score_weight": 1
                        },
                        "text": {
                            "contains": "$elements",
                            "score_weight": 1
                        },
                        "return": {
                            "values": ["rowid", "score", "vector_score", "text_score"],
                            "topN": 5
                        }
                    }'
               )) AS result) a
     , JSON_TABLE (a.result, '$[*]' COLUMNS (
                       rid          VARCHAR2(18) PATH '$.rowid'
                     , score        NUMBER       PATH '$.score'
                     , vector_score NUMBER       PATH '$.vector_score'
                     , text_score   NUMBER       PATH '$.text_score'
                   )) b
     , wiki c
 WHERE c.rowid = b.rid;

SCORE VECTOR_SCORE TEXT_SCORE   ID URL                                         TITLE     CNT
----- ------------ ---------- ---- ------------------------------------------- --------- ---
56.70        64.40         49   47 https://simple.wikipedia.org/wiki/Atom      Atom       10
53.24        65.48         41  108 https://simple.wikipedia.org/wiki/Chemistry Chemistry   8
53.16        65.32         41  858 https://simple.wikipedia.org/wiki/Universe  Universe    6
47.83        71.66         24 2949 https://simple.wikipedia.org/wiki/Oxygen    Oxygen      5
45.82        67.64         24  357 https://simple.wikipedia.org/wiki/Helium    Helium      3

5 rows selected.

 

아래 예제는 What have scientists discovered? 검색 키워드만 포함된 상위 5개의 로우를 조회합니다.

-- 7
SELECT JSON_SERIALIZE (DBMS_HYBRID_VECTOR.SEARCH (JSON (
           '{
                "hybrid_index_name": "wiki_x1",
                "search_scorer": "rsf",
                "search_fusion": "INTERSECT",
                "vector": {
                    "search_text": "What have scientists discovered?",
                    "search_mode": "DOCUMENT",
                    "aggregator": "MAX",
                    "score_weight": 1
                },
                "return": {
                    "values": ["rowid", "score", "vector_score"],
                    "topN": 5
                }
            }'
       )) RETURNING CLOB PRETTY) AS result;

RESULT
-----------------------------------
[
  {
    "rowid" : "AAAP9lAAAAABAKoAAC",
    "score" : 74.61,
    "vector_score" : 74.61
  },
  {
    "rowid" : "AAAP9lAAAAABBVqAAF",
    "score" : 74.61,
    "vector_score" : 74.61
  },
  {
    "rowid" : "AAAP9lAAAAABAVTAAA",
    "score" : 71.96,
    "vector_score" : 71.96
  },
  {
    "rowid" : "AAAP9lAAAAABBUCAAB",
    "score" : 71.66,
    "vector_score" : 71.66
  },
  {
    "rowid" : "AAAP9lAAAAABAB3AAF",
    "score" : 71.66,
    "vector_score" : 71.66
  }
]

1 row selected.

 

id가 882인 로우의 text 칼럼에만 elements 텍스트가 포함된 것을 볼 수 있습니다.

-- 8
SELECT b.score, b.vector_score, c.id, c.url, c.title, REGEXP_COUNT (c.text, 'elements') AS cnt
  FROM (SELECT DBMS_HYBRID_VECTOR.SEARCH (JSON (
                   '{
                        "hybrid_index_name": "wiki_x1",
                        "search_scorer": "rsf",
                        "search_fusion": "INTERSECT",
                        "vector": {
                            "search_text": "What have scientists discovered?",
                            "search_mode": "DOCUMENT",
                            "aggregator": "MAX",
                            "score_weight": 1
                        },
                        "return": {
                            "values": ["rowid", "score", "vector_score"],
                            "topN": 5
                        }
                    }'
               )) AS result) a
     , JSON_TABLE (a.result, '$[*]' COLUMNS (
                       rid          VARCHAR2(18) PATH '$.rowid'
                     , score        NUMBER       PATH '$.score'
                     , vector_score NUMBER       PATH '$.vector_score'
                   )) b
     , wiki c
 WHERE c.rowid = b.rid;

SCORE VECTOR_SCORE    ID URL                                                TITLE            CNT
----- ------------ ----- -------------------------------------------------- ---------------- ---
74.61        74.61  5757 https://simple.wikipedia.org/wiki/Virus            Virus              0
74.61        74.61 57419 https://simple.wikipedia.org/wiki/Caecilian        Caecilian          0
71.96        71.96 18125 https://simple.wikipedia.org/wiki/Phenomenon       Phenomenon         0
71.66        71.66 55347 https://simple.wikipedia.org/wiki/Neurotransmitter Neurotransmitter   0
71.66        71.66   882 https://simple.wikipedia.org/wiki/Uranus           Uranus             1

5 rows selected.

 

아래는 5번 예제와 7번 예제의 세션 통계를 비교한 결과입니다. 텍스트 검색의 유무에 따라 세션 통계에 차이가 발생한 것을 볼 수 있습니다.

-- 9
+----------------------+--------+--------+-------+------+------+
|NAME                  |      #5|      #7|   DIFF| RATIO|CLASS |
+----------------------+--------+--------+-------+------+------+
|recursive calls       |      23|      13|    -10|  0.57|User  |
|session logical reads |   2,694|   1,987|   -707|  0.74|User  |
|user calls            |      27|      24|     -3|  0.89|User  |
|execute count         |      22|      16|     -6|  0.73|SQL   |
|parse count (total)   |      10|       6|     -4|  0.60|SQL   |
|sorts (rows)          |   8,312|   6,500| -1,812|  0.78|SQL   |
+----------------------+--------+--------+-------+------+------+

 

관련 링크

  • Oracle Blog - Using Hybrid Vector Indexes in AI Vector Search
  • Oracle Blog - Hybrid Vector Index - a combination of AI Vector Search with Text Search
  • Oracle Blog - More Examples on Hybrid Vector Search (2025-08-13)
저작자표시 비영리 변경금지 (새창열림)
'Oracle/Vector' 카테고리의 다른 글
  • Relational Data Vectorization
  • FLOAT32 Vector Generator
  • ONNX Model
  • AI Vector Search #3 - IVF 벡터 인덱스
정희락
정희락
2007년부터 Oracle Database 성능 최적화에 관심을 가져왔습니다. 현재 한국오라클 Engineered Systems Solution Engineering 팀에서 Solution Engineer로 근무하고 있습니다. 이 블로그는 개인적인 연구 목적으로 운영되며 Oracle 사의 공식적인 입장을 대변하지 않습니다.
  • 정희락
    TunA
    정희락
  • 전체
    오늘
    어제
    • 분류 전체보기 (206)
      • Oracle (177)
        • SQL (36)
        • PLSQL (10)
        • Performance (75)
        • Administration (37)
        • Installation (3)
        • Utilities (1)
        • JSON (8)
        • Vector (7)
      • Exadata (16)
      • SQL*Plus (2)
      • Linux (5)
      • Resources (6)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 도서

    • 불친절한 SQL 프로그래밍
    • 불친절한 PL/SQL 프로그래밍
  • 링크

    • Connor McDonald
    • Frits Hoogland
    • Jonathan Lewis
    • Julian Dontcheff
    • Julian Dyke
    • Kun Sun
    • Maria Colgan
    • Martin Bach
    • Mike Dietrich
    • Tanel Poder
  • 공지사항

  • 인기 글

  • 태그

    12c
    19c
    21c
    23ai
    case study
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
정희락
Hybrid Vector Index
상단으로

티스토리툴바