はじめに
ベクトル検索における完全ベクトル検索 (Exact Nearest Neighbor/ENN) と 近似ベクトル検索 (Approximate Nearest Neighbor/ANN) は、OracleDB では vector_distance()の後のFETCH FIRST n ROWS ONLYでEXACTかAPPROXIMATEを指定すると制御可能です。
どの程度検索速度に影響があるか確認してみます。
前提
4,096次元のベクトルデータを 25万件用意します。
SQL> CREATE TABLE testtab (
2 vec VECTOR(4096, FLOAT32) NOT NULL
3* );
Table TESTTAB created.
SQL> set timing on
SQL> INSERT /*+ APPEND */ INTO testtab (vec)
2 SELECT v.vec
3 FROM (
4 SELECT LEVEL AS rn
5 FROM dual
6 CONNECT BY LEVEL <= 50000
7 ) t
8 CROSS APPLY (
9 SELECT TO_VECTOR(
10 JSON_ARRAYAGG(val ORDER BY dim RETURNING CLOB),
11 4096,
12 FLOAT32
13 ) AS vec
14 FROM (
15 SELECT LEVEL AS dim,
16 ROUND(DBMS_RANDOM.VALUE(0, 1) + 0 * t.rn, 6) AS val
17 FROM dual
18 CONNECT BY LEVEL <= 4096
19 )
20* ) v;
50,000 rows inserted.
Elapsed: 00:05:04.817
SQL> commit;
Commit complete.
Elapsed: 00:00:00.033
(snip)
SQL>
SQL> select count(1) from testtab;
COUNT(1)
___________
250000
Elapsed: 00:00:00.101
SQL> info testtab
TABLE: TESTTAB
LAST ANALYZED:2026-04-03 01:30:45.0
ROWS :250000
SAMPLE SIZE :250000
INMEMORY :DISABLED
COMMENTS :
Columns
NAME DATA TYPE NULL DEFAULT COMMENTS
VEC VECTOR(4096,FLOAT32,DENSE) No
SQL>
次のような ENN の SQL と ANN の SQL を交互に実行し、実行時間を記録していきます。
(set timing onで表示される Elapsed を記録していきます)
VARIABLE qvec CLOB
BEGIN
SELECT JSON_ARRAYAGG(
ROUND(DBMS_RANDOM.VALUE(-1, 1), 6)
ORDER BY dim
RETURNING CLOB
)
INTO :qvec
FROM (
SELECT LEVEL AS dim
FROM dual
CONNECT BY LEVEL <= 4096
);
END;
/
SELECT /*+ NO_RESULT_CACHE */ vec
FROM testtab
ORDER BY VECTOR_DISTANCE(vec, TO_VECTOR(:qvec, 4096, FLOAT32))
FETCH FIRST 10000 ROWS ONLY;
VARIABLE qvec CLOB
BEGIN
SELECT JSON_ARRAYAGG(
ROUND(DBMS_RANDOM.VALUE(-1, 1), 6)
ORDER BY dim
RETURNING CLOB
)
INTO :qvec
FROM (
SELECT LEVEL AS dim
FROM dual
CONNECT BY LEVEL <= 4096
);
END;
/
SELECT /*+ NO_RESULT_CACHE */ vec
FROM testtab
ORDER BY VECTOR_DISTANCE(vec, TO_VECTOR(:qvec, 4096, FLOAT32))
FETCH APPROXIMATE FIRST 10000 ROWS ONLY;
環境は Autonomous AI Database (26ai) の Always Free です。
結果
結果は下記の通りとなりました。
念のためset autotrace traceonlyも設定していましたが、PHV に変化はありませんでした。
| Elapsed | SQLID | PHV | |
|---|---|---|---|
| 1回目 (ENN) | 00:01:30.456 | fk3244cvb8kj6 | 3860506451 |
| 2回目 (ANN) | 00:01:13.689 | dsjuzpwyv3p6a | 同上 |
| 3回目 (ENN) | 00:01:27.028 | fk3244cvb8kj6 | 同上 |
| 4回目 (ANN) | 00:01:24.803 | dsjuzpwyv3p6a | 同上 |
| 5回目 (ENN) | 00:01:29.389 | fk3244cvb8kj6 | 同上 |
| 6回目 (ANN) | 00:01:25.808 | dsjuzpwyv3p6a | 同上 |
| 7回目 (ENN) | 00:01:29.345 | fk3244cvb8kj6 | 同上 |
| 8回目 (ANN) | 00:01:29.377 | dsjuzpwyv3p6a | 同上 |
| 9回目 (ENN) | 00:01:27.563 | fk3244cvb8kj6 | 同上 |
| 10回目 (ANN) | 00:01:25.902 | dsjuzpwyv3p6a | 同上 |
| 11回目 (ENN) | 00:01:26.004 | fk3244cvb8kj6 | 同上 |
| 12回目 (ANN) | 00:01:25.760 | dsjuzpwyv3p6a | 同上 |
| 13回目 (ENN) | 00:01:23.380 | fk3244cvb8kj6 | 同上 |
| 14回目 (ANN) | 00:01:27.537 | dsjuzpwyv3p6a | 同上 |
参考までに、実行計画は下記のようになりました。
Plan hash value: 3860506451
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | E-Rows | OMem | 1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | | |
| 1 | RESULT CACHE | 3kq35c2ds225753ga3b7622pcc | | 65536 | 1024 |65536 (0)|
|* 2 | COUNT STOPKEY | | | | | |
| 3 | PX COORDINATOR | | | 73728 | 73728 | |
| 4 | PX SEND QC (ORDER) | :TQ10001 | 250K| | | |
| 5 | VIEW | | 250K| | | |
|* 6 | SORT ORDER BY STOPKEY | | 250K| 4410M| 20M| |
| 7 | PX RECEIVE | | 10000 | | | |
| 8 | PX SEND RANGE | :TQ10000 | 10000 | | | |
|* 9 | SORT ORDER BY STOPKEY| | 10000 | 176M| 4464K| |
| 10 | PX BLOCK ITERATOR | | 250K| | | |
|* 11 | TABLE ACCESS FULL | TESTTAB | 250K| | | |
--------------------------------------------------------------------------------------------------------
ENN と ANN の平均値は下記のようになりました。(1回目 (ENN) のみ除外して算出)
データにもよるとは思いますが、ANN の方が 2秒ほど早い結果となりました。
オンライントランザクションなど、数ミリ~数秒を争う世界でベクトル検索を利用する際には、ANN が良さそうです。(ENN で始めて、速度に問題があれば ANN を検討しましょう)
| 検索手法 | 平均値 |
|---|---|
| ENN | 01:27.118 |
| ANN | 01:24.697 |