VECTOR INDEX はどこ?、見積もりサイズだとそれなりのサイズだったのに... の謎を探るべく、我々は洞窟の奥へ向かった!(完結編)
Previously on Mac De Oracle
前回はVECTOR INDEX はどこ?、見積もりサイズだとそれなりのサイズだったのに... の謎を探るべく、我々は洞窟の奥へ向かった!(後編) でした。
謎は、ほぼ解決したかな?と、思ったのですが、モヤモヤは晴れずwww
ということで、完結編的な何か。という位置付けのまとめを書いておくことにしました。(なお、23.5以降VECTOR INDEX関連も機能追加があったり。。。まあ、追いかけるの大変です)
注)ちょい古いリリースなので最新リリースとは挙動など異なる点がある可能性がある点、ご了承ください
SCOTT@localhost:1521/freepdb1> select banner_full from v$version;
BANNER_FULL
--------------------------------------------------------------------------------
Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.4.0.24.05
インメモリー近傍グラフ・ベクトル索引の特徴まとめ (的なもの)
インメモリー近傍グラフ・ベクトル索引は、ディクショナリービューからは、OBJECTでもSEGMENTも無い扱いになっているよう見えてしまう。
しかし、vecsys.vector$indexにリストされるOBJECT_IDは、*_OBJECTSと共通で、vecsys.vector$indexにはあるが、*_OBJECTS上はIDが欠番のように見える(後述)。無いけどあるみたいな不思議な扱いになっている。(今のところ)
なぜそうしたのだろう?....
- Oraclerならお馴染みの *_OBJECTS や *_SEGMENTS には現れないが、in-memory onlyのオブジェクトとして専用のプール上にポピューレートされた場合に存在している in-memory indexである。
(ここテストにでますよ。嘘w)また、in-memory vector indexの補助表及び関連する索引はリストされる(セグメントサイズは、deferred segment creationであるものも含むため状況次第)
メモリー上とは言っても他と同じ扱いで、属性としてin-memory onlyのような列を追加すればよかったのでは?と、個人的には思っていたりする。。。
参考)
(上記の時事検証 23ai 23.4で行ったため、最新のリリースとは差異がある可能性があり、気づいた差分について随時補足追記してあります)
作成したin-memory vector index ( SEARCH_DATA_HNSW_IX ) の IDX_OBJN(列名の短縮系も*_OBJECTSで利用されているOBJECT_IDとな異なり、IDX_OBJN ( N は Numberの略と思われる) という、いわゆるシノニムになってしまっている(この点も状況をわかりにくくしている原因でしょうね。Oraclerじゃない方が作ったような違和感を感じる)
SCOTT@localhost:1521/freepdb1> select idx_objn, idx_name from vecsys.vector$index;
IDX_OBJN IDX_NAME
---------- -------------------------------
80113 SEARCH_DATA_HNSW_IX
で、この IDX_OBJN = OBJECT_ID ( *_OBJECTS で使われている )、いきなりシノニムになってる! と仮説をたて UESR_OBJECTSを検索してみると、存在しない! (何ぃ〜〜〜っ!)
ちがうのか?(*_OBJECTSにはリストされないだけで、採番元は同じなので、はやり仮説は正しかったw。後述)
SCOTT@localhost:1521/freepdb1> select object_name,object_type from user_objects where object_id = 80113;
レコードが選択されませんでした。
念の為、data_object_idでも検索してみる(こちらにもないですよねーw)
SCOTT@localhost:1521/freepdb1> select object_name,object_type from user_objects where data_object_id = 80113;
レコードが選択されませんでした。
USER_SEGMENTSをVECTOR INDEX NAMEで検索してみるがやはり無い。ストレージ上のデータをそのままインメモリー化しているわけではないいうことだろうと想像するが。ではメモリー上の情報は。。。
SCOTT@localhost:1521/freepdb1> select segment_name from user_segments where segment_name = 'SEARCH_DATA_HNSW_IX';
レコードが選択されませんでした。
VECTOR INDEXとしては、 *_INDEXES から確認は可能であるが”!。。。
SCOTT@localhost:1521/freepdb1> select index_name,index_type,table_name from user_indexes where table_name='SEARCH_DATA';
INDEX_NAME INDEX_TYPE TABLE_NAME
------------------------------ --------------------------- ------------------------------
SYS_IL0000078074C00009$$ LOB SEARCH_DATA
SEARCH_DATA_HNSW_IX VECTOR SEARCH_DATA
in-memory vector indexはSGA上の専用メモリープール上に造られる。
では、v$vector_memory_poolからポピュレートされていることを確認。
SCOTT@localhost:1521/freepdb1> l
1 SELECT
2 pool
3 , alloc_bytes
4 , used_bytes
5 , populate_status
6 FROM
7* v$vector_memory_pool
SCOTT@localhost:1521/freepdb1> /
POOL ALLOC_BYTES USED_BYTES POPULATE_STATUS
-------------------------- ----------- ---------- --------------------------
1MB POOL 369098752 236978176 DONE
64KB POOL 150994944 2686976 DONE
IM POOL METADATA 16777216 16777216 DONE
ちなみに、vector indexがポピュレートされる前の状態は以下。1MB/64KB Poolが消費されたことがわかります。つまり、236,978,176 + 2,686,976 = 239,665,152 = 228.6MB
POOL ALLOC_BYTES USED_BYTES POPULATE_STATUS
-------------------------- ----------- ---------- --------------------------
1MB POOL 369098752 0 DONE
64KB POOL 150994944 0 DONE
IM POOL METADATA 16777216 16777216 DONE
in-memory vector indexのメモリセグメントの詳細を確認するには v$vector_mem_segments_detail を参照する。vecsys.vector$indexのidx_objnにリストされていた 80113 が 239MB 程度使用されていることがわかる。
(このサイズ、覚えておいてくださいね。後で使います。 239,534,080 = 228.4MB)
SCOTT@localhost:1521/freepdb1> l
1 SELECT
2 obj
3 , membytes
4 FROM
5* v$vector_mem_segments_detail
SCOTT@localhost:1521/freepdb1> /
OBJ MEMBYTES
---------- ----------
0 131072
80113 239534080
in-memory vector index (HNSW)の補助表と関連索引の object_id を見てみましょう。
vecsys.vector$indexでは、idx_objn となっていた列の値と連続していること点が確認できます。つまり、object_id を採番しているシーケンスを利用している。。。in-memory vector index (HNSW) も *_OBJECTSに含まれている扱い。。。だが、*_objectsには含まれていません!!!!
CREATE VECTOR INDEXで作成された、in-memory vector index( SEARCH_DATA_HNSW_IX )の idx_objn = 80113 , その後に作成される補助表及び索引の *_objects.object_id は、80114 以降が採番されています。
かつ、80113 という object_id は、*_objects にはリストされず。
妙ですよね。内部的には *_objectsに含まれていてもおかしくないと思われる扱いにであるかのように見えますよね。うーーむ。
SCOTT@localhost:1521/freepdb1> l
1 WITH
2 vector_idx_auxiliary_tables
3 AS (
4 SELECT
5 idx_name AS vector_index_name
6 , REPLACE(aux_table_name,'"','') AS aux_table_name
7 FROM
8 (
9 SELECT
10 vvi.idx_name AS idx_name
11 ,vvi.idx_auxiliary_tables.rowid_vid_map_name
12 ,vvi.idx_auxiliary_tables.shared_journal_transaction_commits_name
13 ,vvi.idx_auxiliary_tables.shared_journal_change_log_name
14 FROM
15 vecsys.vector$index vvi
16 ) objs
17 UNPIVOT (
18 aux_table_name FOR related_obj_name IN
19 (
20 rowid_vid_map_name
21 , shared_journal_transaction_commits_name
22 , shared_journal_change_log_name
23 )
24 )
25 )
26 SELECT
27 object_id
28 ,object_name
29 ,object_type
30 FROM
31 user_objects
32 WHERE
33 object_name in (
34 SELECT
35 aux_table_name AS segment_name
36 FROM
37 vector_idx_auxiliary_tables
38 WHERE
39 vector_idx_auxiliary_tables.vector_index_name = 'SEARCH_DATA_HNSW_IX'
40 UNION ALL
41 SELECT
42 index_name AS segment_name
43 FROM
44 user_indexes
45 WHERE
46 EXISTS
47 (
48 SELECT
49 1
50 FROM
51 vector_idx_auxiliary_tables
52 WHERE
53 vector_idx_auxiliary_tables.aux_table_name = user_indexes.table_name
54 AND vector_idx_auxiliary_tables.vector_index_name = 'SEARCH_DATA_HNSW_IX'
55 )
56* )
SCOTT@localhost:1521/freepdb1> /
OBJECT_ID OBJECT_NAME OBJECT_TYPE
---------- -------------------------------------------------------------------------------------------------------------------------------- -----------------------
80114 VECTOR$SEARCH_DATA_HNSW_IX$78074_80113_0$HNSW_ROWID_VID_MAP TABLE
80115 SYS_C0013840 INDEX
80117 VECTOR$SEARCH_DATA_HNSW_IX$78074_80113_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS TABLE PARTITION
80116 VECTOR$SEARCH_DATA_HNSW_IX$78074_80113_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS TABLE
80118 PK_XID_80113 INDEX
80120 VECTOR$SEARCH_DATA_HNSW_IX$78074_80113_0$HNSW_SHARED_JOURNAL_CHANGE_LOG TABLE PARTITION
80119 VECTOR$SEARCH_DATA_HNSW_IX$78074_80113_0$HNSW_SHARED_JOURNAL_CHANGE_LOG TABLE
80124 SYS_IL0000080119C00007$$ INDEX PARTITION
80123 SYS_IL0000080119C00007$$ INDEX
9行が選択されました。
SCOTT@localhost:1521/freepdb1> l
1 select object_id, data_object_id from user_objects where object_name = 'SEARCH_DATA'
SCOTT@localhost:1521/freepdb1> /
OBJECT_ID DATA_OBJECT_ID
---------- --------------
78074 78796
ここまでで、in-memory vector indexはどこ?
って話の場所的なところは見切れたかなと思いますが、
もう一つ、explain plan for create vector index及び、dbms_space.create_index_costプロシージャでの見積もりサイズは、信じて良いのか?、よくないのか?
エラーにはなってないが、信用してよいのだろうか。。。
ちなみに、23.6以降だが、INDEX_VECTOR_MEMORY_ADVISORプシージャによりインメモリーサイズを見積もることができるようになった。とか。。。すぐには用意できないのでw 23.4で一旦確認してみる
- CREATE VECTOR INDEXで作成されるINMEMORYの索引オブジェクトに加え、補助表とよばれる表が複数存在する。未検証だが変更をトラックするタイプの補助表は更新量によってはセグメントサイズが一時的に増加する可能性あり
変更をトラッキングするのための2つのパーティション表と関連索引に関しては変更が無い限りセグメントは作成されない。はず。(deferred segment creationになっているようなので)
つまり新規作成時にはオブジェクトとしては存在するものの、セグメントは存在しない。
ということは、残る一つの補助表(仮でMAP表と呼ぶことにする)とその索引はある程度のセグメントを保持することになるだろうと予想。
また、インメモリー索引自体はベース表のVECTOR列のサイズに依存するのではないだろうか。。。と。
とりあえず、補助表のセグメントサイズを改めて確認してみる(in-memory vector index を何度かdrop/createしていためセグメント名に含まれるobject_idが異なりますが気になさらず。。)
今回のケース 125,000行のデータでは合計で 9MB 程度です。誤差の範囲程度のサイズではあります。ではメモリ上のサイズを占めるデータのベースとなる表のセグメントサイズも今一度確認しておきましょう。
SEGMENT_NAME SEGMENT_TYPE MB
-------------------------------------------------------------------------------- ------------------ ----------
VECTOR$SEARCH_DATA_HNSW_IX$78074_79519_0$HNSW_ROWID_VID_MAP TABLE 3
SYS_C0013800 INDEX 6
VECTORデータ型の列以外もありますが、ベース表のセグメントサイズは、 249MB あります。。。おやおや、サイズ、近似してますよね。。
SCOTT@localhost:1521/freepdb1> r
1* select segment_name,segment_type,bytes / 1024 /1024 "MB" from user_segments where segment_name = 'SEARCH_DATA'
SEGMENT_NAME SEGMENT_TYPE MB
------------------------------ ------------------ ----------
SEARCH_DATA TABLE 248
もう少しベース表のセグメントサイズを調べてみましょう。
SCOTT@localhost:1521/freepdb1> l
1 create table search_data_without_vector_desc
2 as
3 select
4 id
5 ,primary_description
6 ,description
7 ,location_desc
8 ,district
9 ,ward
10 ,community
11 ,c_year
12* from search_data
SCOTT@localhost:1521/freepdb1> /
表が作成されました。
SCOTT@localhost:1521/freepdb1> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>upper('search_data_without_vector_desc'),cascade=>true,no_invalidate=>false);
PL/SQLプロシージャが正常に完了しました。
おお、10MBなので、VECTORデータ型の列を含むSEARCH_DATA表のVECTORデータ型部分のサイズは、238MB 程度みたいですね。むむむ。。。
SCOTT@localhost:1521/freepdb1> r
1 select
2 segment_name
3 ,segment_type
4 ,bytes / 1024 /1024 "MB"
5 from
6 user_segments
7 where
8* segment_name = upper('search_data_without_vector_desc')
SEGMENT_NAME SEGMENT_TYPE MB
------------------------------- ------------------ ----------
SEARCH_DATA_WITHOUT_VECTOR_DESC TABLE 10
in-memory vector index (HNSW)のメモリー消費サイズは、 228.4MB
(これに、補助表のMAP表と索引のセグメントサイズ 9MB を加算すると、237.4MB おおおおおおおおーっ)
ベース表のVECTORデータ型だけのセグメントサイズは、238MB
EXPLAIN PLAN FOR CREATE VECTOR INDEX ...
- estimated index size: 293M bytes
DBMS_SPACE.CREATE_INDEX_COSTプロシージャによるセグメントサイズ見積もり...
Segment Size (MB) :280
という結果になりました。偶然近い値だったの??。。ということでもなさそうな気はします。。
みなさんは、どう思います????
おまけの疑問というか違和感(これまでも少しだけ触れたが、妙な違和感がある)
- これまでのIN-MEMORYとはことなり、VECTOR INDEX (HNSW)の場合、実行計画のオペレーション ( VECTOR INDEX HNSW SCAN ) には、INMEMORYというワードが含まれず勘違いしやすい気がするのに、
なぜ? INMEMORYを含めなかったのだろう。また、*_OBJECTS/*_SEGMENTSを使わず、OBJECT_IDをIDX_OBJNというシノニムまで作り、vecsys.vector$index で別管理かつ、補助表をJSONに書き込んでいる。
SQL文面倒なんすけど。。というのとこれまでの*_OBJECTSの存在と、vecsys.vector$indexのidx_objnの分離(*_objectsのobject_idの採番と同じなのに。。)といい、これまでと違う空気感が強い。
少し調べたのですが、
以下メッセージからも読み取れるように INMEMORY というワードは、Oracle Database上、In-Memory Column Store Architectureをイメージさせるものではないことは確かようだ、
では、なぜ、INMEMORYというキーワードを実行計画のオペレーション名に含めなかった理由は少々理解しにくい。INMEMORY VECTOR INDEX HNSW SCANのほうがイメージしやすいと思うのは私だけだろうか。。。
このフワフワしてるところが違和感の原因でもあるな。。。
ORA-51815
INMEMORY NEIGHBOR GRAPH HNSW vector index snapshot is too old.
Oracle Database 23ai / Error Messages Oracle Database / Database Error Messages / ORA-51815
最後に、vector_index_neighbor_graph_reload を restartにすると再ロードされるよ(scope=bothでspfileにも同時に反映できるよ!)
- 23.4までは初期化パラメータ vector_index_neighbor_graph_reload のデフォルトが OFF であった。
インスタンス再起動で VECTOR INDEX (HNSW) がインメモリー上に再作成されない!!!w(なんだとーーー!)
23.6以降では、デフォルトが変更になり、 restart がデフォルトになっています。ここ試験にでるよ(知らんけどw
おまけに、ADB-Sだと、V$VECTOR_INDEXなんてビューが提供されていたり。。。なぜ全てで提供しないのだろう。。。23.4だからって話でもなさそうで。。
- 関連するディクショナリービューなどが整備されてないように見える(今後整備されるような気はする)。対応しきれていない部分はJSON化して回避しているようにも見える(やめて〜〜w)
ということで、最終的な私の理解のビジュアル化w 2025/6/25時点、かつ、Oracle Database 23ai 23.4 を元にした理解は以下の通り(まだ理解不足な箇所はあるかもしれない)

なお、
DBMS_VECTOR.GET_INDEX_STATUSプロシージャが返すステータスをみると興味深い内容が載っている
Oracle Database 23 / Oracle AI Vector Search User's Guide / Vector Index Status, Checkpoint, and Advisor Procedures / GET_INDEX_STATUS
CREATE VECTOR INDEX (HNSW)に関する状態を取得するプロシージャが返すステータスには以下のように記載されている。In-memory vector index (HNSW)のDDL発行からvector memory poolにロードされ、multi-layered HNSW graphができあがるまでのステータスがわかります!!!
そしてここでも、なんでプロシージャ必要だったのだろう。。。。と、言う素朴な疑問が!!!
Oracle Databaseには昔から、V$SESSION_LONGOPSってビューがあって。。。。
長時間操作のステータスがわかるようになっているのだが.....
ますます、なぜ、これまでの機能やビューを有効に再利用していないのだろう。。。という点が気になる...。が、現状は見切れたかな、と。
(もう一つのVector Indexのタイプと更新トラッキングと反映、いくつかの実行計画パターンの確認を除く)
- HNSW Index Initialization
Initialization phase for the HNSW vector index creation - HNSW Index Auxiliary Tables Creation
Creation of the internal auxiliary tables for the HNSW Neighbor Graph vector index - HNSW Index Graph Allocation
Allocation of memory from the vector memory pool for the HNSW graph - HNSW Index Loading Vectors
Loading of the base table vectors into the vector pool memory - HNSW Index Graph Construction
Creation of the multi-layered HNSW graph with the previously loaded vectors - HNSW Index Creation Completed
HNSW vector index creation finished
では、また。
Enjoy SQL! and AI vector search!
・Oracle Database 23ai freeで試すVector Search - ONNXモデル準備編
・Oracle Database 23ai freeで試すVector Search - データ準備編
・実行計画は, SQL文のレントゲン写真だ! No.66 / AI Vector Search - VECTOR INDEX HNSW SCAN
・VECTOR INDEX はどこ?、見積もりサイズだとそれなりのサイズだったのに... の謎を探るべく、我々は洞窟の奥へ向かった!(前編)
・VECTOR INDEX はどこ?、見積もりサイズだとそれなりのサイズだったのに... の謎を探るべく、我々は洞窟の奥へ向かった!(後編)



最近のコメント