Vector Indexes in Oracle AI Database 26ai — How HNSW and IVF Work Under the Hood

Oracle AI Database 26ai vector index comparison showing HNSW 
graph layers and IVF partition clusters for semantic similarity 
search

In Post -2 of this series, We had a vector search query running clean in development — ten milliseconds, HNSW index in the plan, results looking right. The moment the same query hit a table with eight million rows in staging, response time jumped to forty seconds. The execution plan had quietly switched to TABLE ACCESS FULL. The HNSW index existed, status was VALID, but Oracle was not using it.

The root cause was vector_memory_size — zero. Oracle had built the HNSW graph but had nowhere to hold it in memory. Without the Vector Memory Pool, the graph falls back to disk-based traversal and the optimizer correctly decides a full scan is cheaper.

That is the kind of detail that does not surface in a development environment with a hundred rows. It surfaces in staging at 2 AM. Before we build vector indexes in production, we need to understand exactly what Oracle is doing under the hood — HNSW, IVF, memory, and how to verify every step.

Environment: Oracle AI Database 26ai Free Developer Release on-premises. We continue with the ora_errors dataset from Post 2.


First — Check vector_memory_size Before Anything Else

SELECT name, value
FROM   v$parameter
WHERE  name = 'vector_memory_size';
NAME                 VALUE
-------------------  -----
vector_memory_size   0

Zero means Oracle has not allocated a dedicated Vector Memory Pool. HNSW graphs will attempt to use shared pool memory — creating contention with SQL parsing and PL/SQL on any busy system. Size it before building any HNSW index:

-- Sizing formula:
-- dimensions × 4 bytes × row_count × 1.3 (overhead factor)
-- Production example: 1536-dim × 10M rows × 4 bytes × 1.3 ≈ 80 GB
-- For our demo: trivial — 512M is more than sufficient

ALTER SYSTEM SET vector_memory_size = 512M SCOPE = SPFILE;
-- Bounce required — plan this before go-live, not after

Set this in SPFILE. It does not take effect dynamically. This is the parameter that silently kills HNSW index usage in production when it is missing — and it will not throw an error, it will just fall back to full scan.


HNSW — The Graph-Based Algorithm

HNSW stands for Hierarchical Navigable Small World. During index build, Oracle constructs a multi-layer graph — each vector is a node, edges connect it to its nearest neighbors. Upper layers are sparse for fast long-range navigation, lower layers are dense for precise neighborhood resolution.

At query time Oracle enters at the top layer, navigates downward following edges toward the query vector, and surfaces the approximate nearest neighbors at the bottom.

The entire graph lives in the Vector Memory Pool. That is why the parameter matters.

CREATE VECTOR INDEX ora_errors_hnsw_idx
ON ora_errors (embedding)
ORGANIZATION INMEMORY NEIGHBOR GRAPH
DISTANCE COSINE
WITH TARGET ACCURACY 95
PARAMETERS (type HNSW, neighbors 16, efconstruction 100);

What these parameters control:

  • NEIGHBORS 16 — how many nearest neighbors each node connects to during graph construction. Higher values improve recall but increase both build time and memory. Production default is 32.
  • EFCONSTRUCTION 100 — the candidate list size during graph build. Higher values build a better graph, slower build time. 100 is a solid starting point.
  • TARGET ACCURACY 95 — Oracle targets 95% recall. It may miss up to 5% of true nearest neighbors in exchange for query speed. Raise to 98 or 99 if business requirements demand higher precision.

Verify:

SELECT index_name, index_type, status, num_rows
FROM   user_indexes
WHERE  table_name = 'ORA_ERRORS';
INDEX_NAME             INDEX_TYPE  STATUS  NUM_ROWS
---------------------  ----------  ------  --------
ORA_ERRORS_HNSW_IDX    VECTOR      VALID         10
SYS_C008471            NORMAL      VALID         10

Now confirm the execution plan actually uses it:

EXPLAIN PLAN FOR
SELECT error_code, error_title,
       VECTOR_DISTANCE(embedding,
       VECTOR('[0.90, 0.87, 0.11, 0.09, 0.06, 0.05, 0.10, 0.08]', 8, FLOAT32),
       COSINE) AS distance
FROM   ora_errors
ORDER  BY distance
FETCH  FIRST 3 ROWS ONLY;

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
Plan hash value: 3194827401

---------------------------------------------------------------------------
| Id | Operation                      | Name               | Rows | Cost |
---------------------------------------------------------------------------
|  0 | SELECT STATEMENT               |                    |    3 |    3 |
|  1 |  COUNT STOPKEY                 |                    |      |      |
|  2 |   VIEW                         |                    |    3 |    3 |
|  3 |    SORT ORDER BY STOPKEY       |                    |    3 |    3 |
|  4 |     VECTOR INDEX HNSW SCAN     | ORA_ERRORS_HNSW_IDX|    3 |    2 |
---------------------------------------------------------------------------

VECTOR INDEX HNSW SCAN — that is the confirmation we need. If this shows TABLE ACCESS FULL instead, the first thing to check is vector_memory_size. Second thing — index STATUS in user_indexes.


IVF — The Partition-Based Algorithm

IVF stands for Inverted File Index. During build, Oracle clusters the vector space into partitions using k-means. Each vector is assigned to its nearest centroid. The index records which vectors belong to which partition.

At query time Oracle identifies the closest centroids to the query vector and searches only those partitions — skipping everything else.

Our four semantic clusters map directly to four IVF partitions:

Partition 1 — Memory:        ORA-04031, ORA-00823
Partition 2 — Space/Undo:    ORA-01555, ORA-00257, ORA-01654
Partition 3 — Locking:       ORA-00060, ORA-04021
Partition 4 — Connectivity:  ORA-12541, ORA-12170

A query for “database memory allocation failure” identifies Partition 1 as closest and searches only those two rows — not the full table.

DROP INDEX ora_errors_hnsw_idx;

CREATE VECTOR INDEX ora_errors_ivf_idx
ON ora_errors (embedding)
ORGANIZATION NEIGHBOR PARTITIONS
DISTANCE COSINE
WITH TARGET ACCURACY 90
PARAMETERS (type IVF, neighbor partitions 4);

Key differences from HNSW syntax:

  • ORGANIZATION NEIGHBOR PARTITIONS — IVF keyword, contrasted with INMEMORY NEIGHBOR GRAPH for HNSW
  • NEIGHBOR PARTITIONS 4 — number of centroids. Production rule of thumb: square root of total row count. One million rows → 1000 partitions as a starting point
  • No Vector Memory Pool dependency — IVF is disk-based. It works without vector_memory_size

Verify and check the plan:

SELECT index_name, index_type, status
FROM   user_indexes
WHERE  table_name = 'ORA_ERRORS';
INDEX_NAME            INDEX_TYPE  STATUS
--------------------  ----------  ------
ORA_ERRORS_IVF_IDX    VECTOR      VALID
SYS_C008471           NORMAL      VALID
|  4 |     VECTOR INDEX IVF SCAN      | ORA_ERRORS_IVF_IDX |    3 |    2 |

VECTOR INDEX IVF SCAN confirms IVF is active.


HNSW vs IVF — The Decision That Matters

FactorHNSWIVF
Query speedVery fast — graph traversalFast — partition-scoped
Recall accuracyHigherSlightly lower at partition boundaries
Memory requirementHigh — full graph in SGANone — disk-based
Index build timeSlowerFaster — k-means is parallelizable
DML sensitivitySensitive — inserts degrade graph over timeTolerant — vectors assigned to nearest centroid on insert
Best workloadRead-heavy, stable datasetsWrite-heavy, large datasets, memory-constrained

Our production starting point: HNSW for RAG implementations and semantic search — these are typically read-heavy with stable embedding tables. IVF when the table exceeds 50 million rows or when Vector Memory Pool sizing becomes a constraint.

One thing we have seen in practice — teams that skip the Vector Memory Pool sizing conversation before go-live and then wonder why HNSW is not being used in production. Set vector_memory_size in SPFILE, bounce the instance during the maintenance window, then build the HNSW index. In that order, not the reverse.


Verifying Recall — Exact vs Approximate Side by Side

TARGET ACCURACY 95 means Oracle may miss up to 5% of true nearest neighbors. We can measure that directly by comparing approximate search output against exact search output on the same query.

-- Exact search baseline (temporarily drop the index or use NO_VECTOR_INDEX hint)
SELECT /*+ NO_VECTOR_INDEX(e ora_errors_ivf_idx) */
       error_code,
       ROUND(VECTOR_DISTANCE(embedding,
             VECTOR('[0.90, 0.87, 0.11, 0.09, 0.06, 0.05, 0.10, 0.08]', 8, FLOAT32),
             COSINE), 6) AS exact_distance
FROM   ora_errors e
ORDER  BY exact_distance
FETCH  FIRST 5 ROWS ONLY;
ERROR_CODE  EXACT_DISTANCE
----------  --------------
ORA-04031        0.000187
ORA-00823        0.000423
ORA-01555        0.089341
ORA-00257        0.091204
ORA-01654        0.093817
-- Approximate search with index active
SELECT error_code,
       ROUND(VECTOR_DISTANCE(embedding,
             VECTOR('[0.90, 0.87, 0.11, 0.09, 0.06, 0.05, 0.10, 0.08]', 8, FLOAT32),
             COSINE), 6) AS approx_distance
FROM   ora_errors
ORDER  BY approx_distance
FETCH  FIRST 5 ROWS ONLY;
ERROR_CODE  APPROX_DISTANCE
----------  ---------------
ORA-04031         0.000187
ORA-00823         0.000423
ORA-01555         0.089341
ORA-00257         0.091204
ORA-01654         0.093817

Results are identical on our ten-row dataset — expected. At scale with millions of vectors, positions 4 and 5 will occasionally diverge. That divergence is what TARGET ACCURACY 95 budgets for. If the use case demands higher precision — medical records similarity, legal document search, fraud pattern matching — push TARGET ACCURACY to 98 or 99. Oracle widens the search beam at query time, slightly higher CPU per query in exchange for better recall.


Cleanup

DROP INDEX ora_errors_ivf_idx;
DROP TABLE ora_errors PURGE;

What Is Next

We now have the index engine covered — HNSW, IVF, memory sizing, plan verification, recall measurement.

Post 4 moves to the query layer — Hybrid Search. Pure vector similarity alone is rarely enough in production. We almost always need to combine semantic search with relational filters — find the most semantically similar errors, but only within a specific error category, or above a severity threshold, or within a date window. That combination is where Oracle AI Database 26ai’s native SQL integration delivers its biggest practical advantage over standalone vector databases.

The ora_errors dataset carries forward into Post 4 with an expanded schema to support the hybrid query pattern.


Where We Go From Here

Post 3 goes deeper into the index layer itself.

The Pillar 1 series goes hands-on, in this order:

If you are running Oracle 19c in production today and trying to understand what the 23ai upgrade actually requires of the DBA team — not the developers, not the architects, the DBA team — this series is built for that conversation.


Sizing HNSW Vector Memory Pool on Exadata vs commodity hardware? Drop your numbers in the comments — real-world sizing data from production environments is what the DBA community actually needs.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.