VECTOR INDEX はどこ?、見積もりサイズだとそれなりのサイズだったのに... の謎を探るべく、我々は洞窟の奥へ向かった!(前編) Tweet
Previously on Mac De Oracle
前回は、実行計画は, SQL文のレントゲン写真だ! No.66 / AI Vector Search - VECTOR INDEX HNSW SCANでした。
今日はそれに絡んで気づいたVECTOR INDEXの謎を追いかけてみることにします。
前回の最後の宿題というか謎の一つ目、覚えてますか? そう、VECTOR INDEXを作成後、まあよくある、USER_INDEXESを検索して作成されたVECTOR INDEXの名称を確認して、EXPLAIN PLAN FORで見積もった索引サイズとどの程度乖離しているのだろうと、手癖でUSER_SEGMENTSをアクセスしてみると、なな、なーーーーーんと、ない、無い!、なーーーい!。 (INMEMORYとはいっても元ネタがあってINMEMORY化されるわけで仕組み的に理解しにくいのと、これまでのディクショナリービューの使い方と異なっていてものすごく追いにくい)
なんで? という謎。
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
経過: 00:00:00.05
SCOTT@localhost:1521/freepdb1> select segment_name from user_segments where segment_name = 'SEARCH_DATA_HNSW_IX';
レコードが選択されませんでした。
その後、アッ!
いつもと違うビューがあったことを思い出し、覗いてみると、
作成したVECTOR INDEX (HNSW)の名称とともに、IDX_AUXILIARY_TABLES列(JSONデータ型)に、なにやらそれらしい値があるじゃありませんか!!!
そーなんだーーーーっ。VECTOR INDEXの名称は、VECTOR INDEXの補助表の集まりをまとめる為だけの存在?!!!
というところまででした。
SCOTT@localhost:1521/freepdb1> r
1* SELECT * FROM vecsys.vector$index
IDX_OBJN IDX_OBJD IDX_OWNER# IDX_NAME IDX_BASE_TABLE_OBJN IDX_BASE_TABLE_OWNER# IDX_PARAMS IDX_AUXILIARY_TABLES
---------- ---------- ---------- ------------------------------ ------------------- --------------------- ------------------------------ ------------------------------
78797 153 SEARCH_DATA_HNSW_IX 78074 153 {"type":"HNSW","num_neighbors" {"rowid_vid_map_objn":78798,"s
:32,"efConstruction":200,"dist hared_journal_transaction_comm
ance":"COSINE","accuracy":90," its_objn":78800,"shared_journa
vector_type":"FLOAT32","vector l_change_log_objn":78803,"rowi
_dimension":384,"degree_of_par d_vid_map_name":"VECTOR$SEARCH
allelism":1,"pdb_id":3,"indexe _DATA_HNSW_IX$78074_78797_0$HN
d_col":"VECTOR_DESC"} SW_ROWID_VID_MAP","shared_jour
nal_transaction_commits_name":
"VECTOR$SEARCH_DATA_HNSW_IX$78
074_78797_0$HNSW_SHARED_JOURNA
L_TRANSACTION_COMMITS","shared
_journal_change_log_name":"VEC
TOR$SEARCH_DATA_HNSW_IX$78074_
78797_0$HNSW_SHARED_JOURNAL_CH
ANGE_LOG"}
SCOTT@localhost:1521/freepdb1> r
1 SELECT
2 JSON_SERIALIZE(idx_params RETURNING VARCHAR2 PRETTY) AS "INDEX PARAM"
3 ,JSON_SERIALIZE(idx_auxiliary_tables RETURNING VARCHAR2 PRETTY) AS "INDEX AUX"
4* FROM vecsys.vector$index
INDEX PARAM INDEX AUX
-------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------
{ {
"type" : "HNSW", rowid_vid_map_objn" : 78798,
"num_neighbors" : 32, "shared_journal_transaction_commits_objn" : 78800,
"efConstruction" : 200, "shared_journal_change_log_objn" : 78803,
"distance" : "COSINE", "rowid_vid_map_name" : "VECTOR$SEARCH_DATA_HNSW_IX$78074_78797_0$HNSW_ROWID_VID_MAP",
"accuracy" : 90, "shared_journal_transaction_commits_name" : "VECTOR$SEARCH_DATA_HNSW_IX$78074_78797_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS",
"vector_type" : "FLOAT32", "shared_journal_change_log_name" : "VECTOR$SEARCH_DATA_HNSW_IX$78074_78797_0$HNSW_SHARED_JOURNAL_CHANGE_LOG"
"vector_dimension" : 384, }
"degree_of_parallelism" : 1,
"pdb_id" : 3,
"indexed_col" : "VECTOR_DESC"
}
一旦ここまでの情報を整理すると
VECTOR INDEX(この例ではHNSW)の実態は、複数の補助表から構成されており、それらをまとめているのが 作成時に指定したVECTOR INDEXの名称。
オブジェクトとしては存在しているが、それ自体は、セグメントを持たない!!!(IN MEMORY展開される索引だからとはいっても。。なにかしらあるのでは。。?)
現時点の23aiには、使い勝手の良いディクショナリービューは提供されていない。。。。まじか!
その代わりに、vecsys.vector$indexディクショナリー表(ビューではない)から詳細を追うことができる。(vecsysスキーマ)
さらに、補助表を見つけるためには、該当ディクショナリー表のIDX_AUXILIARY_TABLES列に格納されているJSONの*_objnや、補助表のオブジェクトID、*_nameから探れ!、と。
ようするに、このJSONに対象となるVECTOR INDEXとその実態の依存関係が入っている!!!!!
(ちなみに、表しかないが、おそらく、関連索引もありそうだ、というかあるだろうな。補助表の索引は補助表をキーにしてUSER_INDEXESから探すしかないだろうけども。)
ここまで見てきたところで、
めんどくせーーーーーーーーーーーーーっ! なぜ、依存関係をJSONに突っ込んだの?。。。。という顔をしているところwwwww
そしてもう一つの謎、EXPLAIN PLAN FOR CREATE VECTOR INDEX ...foo bar ..として見積もられたVECTOR INDEXの見積もりサイズって、こいつら補助表含めた合計値?
なのか? 。。。だよなw 精度的に微妙な気がしなくも無い
全部かき集めて、合計して、見積もりサイズとの差分を見てみよう!
B*Treeの索引見積もりって、結構いい感じのサイズを弾き出してくれるわけだが。。。果たして。。。こいつは、どうなんだ? SEARCH_DATA_HNSW_IXって実態持ってないし。。。
では、もっと探ってみなければw
そのまえに、CREATE VECTOR INDEXの謎を探るべくw
10046トレースで洞窟の奥に潜入することにします! ww (わからなくなったら、これしかない!)
SCOTT@localhost:1521/freepdb1> desc search_data
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NUMBER
PRIMARY_DESCRIPTION VARCHAR2(40)
DESCRIPTION VARCHAR2(100)
LOCATION_DESC VARCHAR2(100)
DISTRICT VARCHAR2(30)
WARD NUMBER
COMMUNITY VARCHAR2(30)
C_YEAR NUMBER
VECTOR_DESC VECTOR(*, *)
SCOTT@localhost:1521/freepdb1> select count(*) from search_data
COUNT(*)
----------
125000
作成済みVECTOR INDEX (HNSW)を削除しておきます。
SCOTT@localhost:1521/freepdb1> drop index search_data_hnsw_ix;
索引が削除されました。
経過: 00:00:01.77
SCOTT@localhost:1521/freepdb1> select index_name , table_name, index_type from user_indexes where table_name like '%SEARCH_DATA%';
INDEX_NAME TABLE_NAME INDEX_TYPE
------------------------------ ------------------------------ ---------------------------
SYS_IL0000078074C00009$$ SEARCH_DATA LOB
SCOTT@localhost:1521/freepdb1> select table_name,column_name,segment_name,index_name from user_lobs where table_name like '%SEARCH_DATA%';
TABLE_NAME COLUMN_NAME SEGMENT_NAME INDEX_NAME
------------------------------ ------------------------------ ------------------------------ ------------------------------
SEARCH_DATA VECTOR_DESC SYS_LOB0000078074C00009$$ SYS_IL0000078074C00009$$
経過: 00:00:00.07
10046トレースの準備ができたので、トレースを設定しCREATE VECTOR INDEX文を実行して洞窟の奥へw
SCOTT@localhost:1521/freepdb1> show parameter timed_statistics
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
timed_statistics boolean TRUE
SCOTT@localhost:1521/freepdb1> show parameter max_dump_file_size
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
max_dump_file_size string 32M
SCOTT@localhost:1521/freepdb1> alter session set max_dump_file_size = unlimited;
セッションが変更されました。
経過: 00:00:00.00
SCOTT@localhost:1521/freepdb1> show parameter tracefile_identifier
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
tracefile_identifier string
SCOTT@localhost:1521/freepdb1> alter session set tracefile_identifier = 'create_vector_index';
セッションが変更されました。
経過: 00:00:00.00
SCOTT@localhost:1521/freepdb1> alter session set events '10046 trace name context forever, level 12';
セッションが変更されました。
経過: 00:00:00.02
SCOTT@localhost:1521/freepdb1> l
1 CREATE VECTOR INDEX search_data_hnsw_ix ON search_data ( vector_desc )
2 ORGANIZATION
3 INMEMORY NEIGHBOR GRAPH
4 DISTANCE COSINE
5* WITH TARGET ACCURACY 90
SCOTT@localhost:1521/freepdb1> /
索引が作成されました。
経過: 00:00:39.24
SCOTT@localhost:1521/freepdb1> alter session set events '10046 trace name context off';
セッションが変更されました。
経過: 00:00:00.01
...略...
[oracle@localhost ~]$ echo $ORACLE_BASE
/opt/oracle
[oracle@localhost ~]$ cd $ORACLE_BASE/diag/rdbms/free/FREE/trace
[oracle@localhost trace]$ pwd
/opt/oracle/diag/rdbms/free/FREE/trace
[oracle@localhost trace]$
[oracle@localhost trace]$ ll *create_vector_index*
-rw-r-----. 1 oracle oinstall 2836064 6月 12 00:24 FREE_ora_4133_create_vector_index.trc
-rw-r-----. 1 oracle oinstall 602896 6月 12 00:24 FREE_ora_4133_create_vector_index.trm
[oracle@localhost trace]$
[oracle@localhost trace]$ tkprof FREE_ora_4133_create_vector_index.trc FREE_ora_4133_create_vector_index.txt waits=yes explain=scott@localhost:1521/freepdb1 sys=yes
...略...
[oracle@localhost trace]$ ll *create_vector_index*
-rw-r-----. 1 oracle oinstall 2836064 6月 12 00:24 FREE_ora_4133_create_vector_index.trc
-rw-r-----. 1 oracle oinstall 602896 6月 12 00:24 FREE_ora_4133_create_vector_index.trm
-rw-rw-r--. 1 oracle oracle 500986 6月 12 00:30 FREE_ora_4133_create_vector_index.txt
[oracle@localhost trace]$
[oracle@localhost trace]$ view FREE_ora_4133_create_vector_index.txt
ということで、整形したトレースファイルからCREATE文を抜き出してみた。
これは私が実行したDDL文なので参考程度に...
CREATE VECTOR INDEX search_data_hnsw_ix ON search_data ( vector_desc )
ORGANIZATION
INMEMORY NEIGHBOR GRAPH
DISTANCE COSINE
WITH TARGET ACCURACY 90
以降、トレースから抜き出した補助表及関連する索引を作成するDDL
VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_ROWID_VID_MAP表、および、主キー索引の作成(基本的にこの補助表が主役)
CREATE TABLE SCOTT.VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_ROWID_VID_MAP
(base_table_rowid ROWID PRIMARY KEY, vertex_id NUMBER)
CREATE UNIQUE INDEX "SCOTT"."SYS_C0013760" on
"SCOTT"."VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_ROWID_VID_MAP"("BASE_TABLE_ROWID")
NOPARALLEL
VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS表、および、主キー索引の作成
CREATE TABLE SCOTT.VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS
(usn NUMBER NOT NULL, slot NUMBER NOT NULL, seq
NUMBER NOT NULL, commit_scn NUMBER NOT NULL,
CONSTRAINT pk_xid_79461 PRIMARY KEY(usn, slot, seq))
PARTITION BY RANGE(commit_scn)
INTERVAL(100)
(PARTITION pdefault VALUES LESS THAN (0))
CREATE UNIQUE INDEX "SCOTT"."PK_XID_79461" on
"SCOTT"."VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS"("USN","SLOT","SEQ")
NOPARALLEL
VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_CHANGE_LOG表の作成(HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS表の参照パーティションになっている)
CREATE TABLE SCOTT.VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_CHANGE_LOG
(usn NUMBER NOT NULL, slot NUMBER NOT NULL, seq NUMBER NOT
NULL, xcn NUMBER, base_table_rowid ROWID, dml_op VARCHAR2(10),
data_vector vector(384, FLOAT32),
CONSTRAINT fk_xid_79461 FOREIGN KEY(usn, slot, seq)
REFERENCES
SCOTT.VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS(usn, slot, seq))
PARTITION BY REFERENCE(fk_xid_79461)
各表をdescribeしてみると...
VECTOR列を持つ表のROWIDとVERTEX_IDをマップしていますね、表を見てなんとなく想像できる列名で素敵w。RDBMSのモデリングで列からなにやってるか想像もできねー設計しているのを見たりしてると心が清くなった気がしますね。
これがVECTOR INDEXの主役といってもよいでしょうね。
SCOTT@localhost:1521/freepdb1> desc VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_ROWID_VID_MAP
名前 NULL? 型
----------------------------------------- -------- ----------------------------
BASE_TABLE_ROWID NOT NULL ROWID
VERTEX_ID NUMBER
次の2つの表は、元の表のVECTORに影響のある更新がトラックされているみたい。詳しい資料読んではいないのですが、変更をトラックしていく表っぽうので、変更のない状況では空なんじゃないだろうか。。。あとで確認します。
SCOTT@localhost:1521/freepdb1> desc VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS
名前 NULL? 型
----------------------------------------- -------- ----------------------------
USN NOT NULL NUMBER
SLOT NOT NULL NUMBER
SEQ NOT NULL NUMBER
COMMIT_SCN NOT NULL NUMBER
SCOTT@localhost:1521/freepdb1> desc VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_CHANGE_LOG
名前 NULL? 型
----------------------------------------- -------- ----------------------------
USN NOT NULL NUMBER
SLOT NOT NULL NUMBER
SEQ NOT NULL NUMBER
XCN NUMBER
BASE_TABLE_ROWID ROWID
DML_OP VARCHAR2(10)
DATA_VECTOR VECTOR(384, FLOAT32)
一応user_indexesビューから全体を見てみます。USER_INDEXESビューを通して見ると新たに7つのオブジェクトが作成されたことがわかります!!!!(赤字部分)
vecsys.vector$index表からは、補助表の存在しか見えませんでしたが、索引も作成されてます。実際にはこの表と索引のセグメントサイズの合計が、VECTOR INDEX作成時のセグメントサイズってことですよね?!(INMEMORYだからメモリーサイズって可能性もなくはないけど、どっちかわからんw) とはいえ、CREATE VECTOR INDEX ほげほげ索引で、指定したほげほげ索引自体はセグメントは持たない!(INMEMORYテーブルとは異なる持ち方をしているだけなのかもしれないが、わかりにくいw)
SCOTT@localhost:1521/freepdb1> select index_name , table_name from user_indexes where table_name like '%SEARCH_DATA%';
INDEX_NAME TABLE_NAME
------------------------------ ----------------------------------------------------------------------------------------------------
SYS_IL0000078074C00009$$ SEARCH_DATA
SEARCH_DATA_HNSW_IX SEARCH_DATA
SYS_C0013720 VECTOR$SEARCH_DATA_HNSW_IX$78074_79421_0$HNSW_ROWID_VID_MAP
PK_XID_79421 VECTOR$SEARCH_DATA_HNSW_IX$78074_79421_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS
SYS_IL0000079427C00007$$ VECTOR$SEARCH_DATA_HNSW_IX$78074_79421_0$HNSW_SHARED_JOURNAL_CHANGE_LOG
経過: 00:00:00.29
表名を中間一致検索すると関連する索引と表をリストすることができますが、これだとなんかイケてない感じが強いのでvecsys.vector$index表より扱いやすいビューを提供してもらいたいですね! まじで (苦笑
長くなってしまったので、一旦、謎の1つをまとめておきたいと思います。
VECTOR INDEX (HNSW)のセグメントサイズとEXPLAIN PLAN FOR CREATE VECTOR INDEX...の見積もりサイズの差分確認は次回のおたのしみということで。
CREATE VECTOR INDEX search_data_hnsw_ix ON search_data ( vector_desc )
ORGANIZATION
INMEMORY NEIGHBOR GRAPH
DISTANCE COSINE
WITH TARGET ACCURACY 90
作成されるベクター索引(今回の例では、search_data_hnsw_ix)は *_INDEXESにはVECTOR索引としてリストされるが実体は持たない(INMEMORYとはいっても何がどうINMEMORY化されるかディクショナリーから追いやすかったテーブルのINMEMORY化とは傾向が違いそう)。
補助表とその索引を含めたオブジェクト群をアクセスするためのキーのような存在。 ベクター索引(今回のHNSW索引の例では、マップ表とベクターの更新をトラックするパーティション表と参照パーティション表からなる3表と付随する3つの索引から構成されている。 ベクター索引の主役は、ベクター列をもつベース表のROWIDをVERTEX_IDをマップするマップ表とその索引。変更がない状態であれば、おそらくこの2つのオブジェクトがセベクター索引のセグメントサイズの総量になりそう。
また、
これらオブジェクト(索引も含め)を簡単に一覧するビューは無く、中間一致検索で無理やり検索するか、vecsys.vector$index表のJSONデータを使って他のビューと結合して取得する必要がありそう(めんどくせぇw)
ということまでは見えた。(以下は、洞窟の奥へ潜入したときの手書きメモww)
もうひとつ、前述の通り、search_data_hnsw_ixというベクター索引の便宜上のオブジェクトは同一名のセグメントは持っていません。しかし、以下のようなSELECT文では、該当ベクター索引が利用されていることを示すオブジェクトとして現れてくるというのも特徴的ですね。
SQL文と実行計画によってはMAP表が現われてくることもあるようなので、その例についてもいずれ書こうと思っています:) (それはそれで謎いですよね。突然裏の主役が実行計画に顔を出してくるわけなのでw)
SCOTT@localhost:1521/freepdb1> r
1 SELECT
2 id
3 , description
4 , community
5 , location_desc
6 , district
7 , TO_NUMBER( v_distance ) AS v_distance
8 FROM
9 (
10 SELECT
11 id
12 , description
13 , community
14 , location_desc
15 , district
16 , VECTOR_DISTANCE
17 (
18 vector_desc
19 , VECTOR_EMBEDDING
20 (
21 all_minilm_l6 USING 'Incident in which someone may have been murdered' AS data
22 )
23 , COSINE
24 ) v_distance
25 FROM
26 search_data
27 ORDER BY
28 v_distance
29 FETCH APPROX FIRST 10 ROWS ONLY
30* )
10行が選択されました。
経過: 00:00:00.32
実行計画
----------------------------------------------------------
Plan hash value: 2333665681
-------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 1570 | 2 (50)| 00:00:01 |
| 1 | VIEW | | 10 | 1570 | 2 (50)| 00:00:01 |
|* 2 | COUNT STOPKEY | | | | | |
| 3 | VIEW | | 10 | 1570 | 2 (50)| 00:00:01 |
|* 4 | SORT ORDER BY STOPKEY | | 10 | 16400 | 2 (50)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| SEARCH_DATA | 10 | 16400 | 1 (0)| 00:00:01 |
| 6 | VECTOR INDEX HNSW SCAN | SEARCH_DATA_HNSW_IX | 10 | 16400 | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------
なお、今回利用している Oracle Database 23.4は最近の更新ペースからするとすでに古いリリースになっていますw
VECTOR関連追加など含め結構な差分がりそうですね。例えば、DBMS_VECTOR.INDEX_VECTOR_MEMORY_ADVISOR()プロシージャが無いなど。(マニュアルには記載されているんですが、リリースいくつから有効というような表記は見当たらず)
また、VirtualBox Applienceだと、現時点では、23.7 が最新みたいなので入れ替えないとな。。。
23って、機能的にいつ頃落ち着くんだろうなぁ〜っ。
次回へ、つづく!
Enjoy SQL! and JSON?!
・Oracle Database 23ai freeで試すVector Search - ONNXモデル準備編
・Oracle Database 23ai freeで試すVector Search - データ準備編
・実行計画は, SQL文のレントゲン写真だ! No.66 / AI Vector Search - VECTOR INDEX HNSW SCAN
| 固定リンク | 0



コメント