Overview

Oracle Database 23ai ships with two approximate nearest neighbour (ANN) vector index types:

  • HNSW (Hierarchical Navigable Small World) — an in-memory graph-based index built for speed
  • IVF (Inverted File Flat) — a disk-based partitioned index built for large datasets with limited memory

Both replace exhaustive similarity search — which computes VECTOR_DISTANCE against every row — with an approximate search that checks only a fraction of the data while staying within a configurable accuracy target.

According to Oracle benchmarks, exact search on 50,000 vectors took 1.50 seconds versus 0.47 seconds with an HNSW index — over 3x faster with the same top-10 results. At millions of vectors the gap grows dramatically.


How HNSW Works

HNSW builds a multi-layer proximity graph where the topmost layer contains the fewest points and the bottom layer contains the most. When a query comes in, the search starts at the top layer and traverses downward, running an ANN algorithm at each layer to find the closest point before descending further.

Graph-based indexing is very efficient because it allows searching through a high-dimensional space by progressively narrowing down the location at each layer. However, re-indexing can be challenging because the entire graph may need to be recreated when new vectors are inserted.

The key characteristic: HNSW is an in-memory only index and can require a lot of memory for large datasets.

Creating an HNSW Index

-- Configure vector pool memory FIRST (required for HNSW)
ALTER SYSTEM SET vector_memory_size = 1G SCOPE=SPFILE;
-- Restart the database to apply

-- Create the HNSW index
CREATE VECTOR INDEX docs_hnsw_idx ON documents (embedding)
  ORGANIZATION INMEMORY NEIGHBOR GRAPH
  DISTANCE COSINE
  WITH TARGET ACCURACY 95;

HNSW Tuning Parameters

Parameter Default Effect
TARGET ACCURACY 95 Higher = more accurate, slower build
NEIGHBORS 32 Max connections per vector per layer — higher improves recall, increases memory
EFCONSTRUCTION 200 Candidates considered during build — higher improves quality, slower build
-- Tuned HNSW index for high-recall workloads
CREATE VECTOR INDEX docs_hnsw_idx ON documents (embedding)
  ORGANIZATION INMEMORY NEIGHBOR GRAPH
  DISTANCE COSINE
  WITH TARGET ACCURACY 98
  PARAMETERS (NEIGHBORS 64, EFCONSTRUCTION 400);

HNSW Memory Sizing

Use this formula to estimate the vector pool size needed:

Memory = 1.3 × num_vectors × num_dimensions × bytes_per_dimension

For 1 million vectors at 1536 dimensions using FLOAT32 (4 bytes):

1.3 × 1,000,000 × 1536 × 4 = ~8 GB
-- Check current vector pool usage
SELECT pool, alloc_bytes, used_bytes, populate_status
FROM V$VECTOR_MEMORY_POOL;

-- Check per-index memory usage
SELECT index_name, allocated_bytes, used_bytes, num_vectors
FROM V$VECTOR_INDEX;

How IVF Works

The IVF vector index uses a different search technique than HNSW. It is designed to enhance search efficiency by narrowing the search area through the use of neighbor partitions or clusters. The dataset size determines the number of partitions in the index, and the center of each partition — the centroid — represents the average vector for each partition.

At query time, Oracle finds the nearest centroid to the query vector and searches only the vectors in that cluster — dramatically reducing the number of distance computations needed.

A significant advantage of IVF is that it is not constrained by the amount of memory available in the vector pool like HNSW. Although IVF won't be as fast as an equivalent HNSW index, it can be used for very large datasets and still provide excellent performance compared to exhaustive similarity search.

Creating an IVF Index

-- IVF index — no vector pool required
CREATE VECTOR INDEX docs_ivf_idx ON documents (embedding)
  ORGANIZATION NEIGHBOR PARTITIONS
  DISTANCE COSINE
  WITH TARGET ACCURACY 95;

IVF Tuning Parameters

Parameter Default Effect
TARGET ACCURACY 95 Higher = more partitions probed at query time
NEIGHBOR PARTITIONS √ num_vectors Number of clusters — more clusters = finer granularity
-- IVF index with explicit partition count
CREATE VECTOR INDEX docs_ivf_idx ON documents (embedding)
  ORGANIZATION NEIGHBOR PARTITIONS
  DISTANCE COSINE
  WITH TARGET ACCURACY 95
  PARAMETERS (NEIGHBOR PARTITIONS 512);

IVF vector indexes support global and local indexes on partitioned tables. For very large datasets that are partitioned, creating a locally partitioned IVF vector index means query performance can be further enhanced with partition pruning, avoiding a costly scan of the entire index.

-- Locally partitioned IVF for partitioned tables
CREATE VECTOR INDEX docs_ivf_local_idx ON documents (embedding)
  ORGANIZATION NEIGHBOR PARTITIONS LOCAL
  DISTANCE COSINE
  WITH TARGET ACCURACY 95;

Head-to-Head Comparison

Characteristic HNSW IVF
Storage In-memory (vector pool) Disk (buffer cache)
Query speed Faster Slightly slower
Memory requirement High Low
Build time Faster Slower (k-means clustering)
Dataset size Up to available memory Unlimited
Partitioned tables No Yes (local index)
Re-indexing on DML Full rebuild may be needed Incremental updates
Best for Real-time search, RAG pipelines Large datasets, batch workloads

When to Use Each Index

Use HNSW when you have 100K to 10M vectors, need sub-100ms latency, and have memory available. Use IVF when you have 10M+ vectors, limited memory, and can tolerate slightly higher latency.

Choose HNSW when:

  • Your dataset fits in the vector pool (check with V$VECTOR_MEMORY_POOL)
  • You need the lowest possible query latency (real-time search, user-facing RAG)
  • Your workload is read-heavy with infrequent inserts
  • You are building a semantic search or recommendation engine

Choose IVF when:

  • Your dataset is very large (tens of millions of vectors or more)
  • Memory is constrained — you cannot allocate a large vector pool
  • Your table is already partitioned and you want local index benefits
  • Your workload involves batch similarity searches rather than real-time queries
  • You need to support frequent DML (inserts, updates, deletes) without full index rebuilds

Using FETCH APPROXIMATE

Both index types require the FETCH APPROXIMATE clause to actually use the index. Without it, Oracle performs an exhaustive scan:

-- WITHOUT APPROXIMATE — performs exhaustive scan, ignores the index
SELECT id, content
FROM documents
ORDER BY VECTOR_DISTANCE(embedding, :query_vec, COSINE)
FETCH FIRST 10 ROWS ONLY;

-- WITH APPROXIMATE — uses the vector index
SELECT id, content
FROM documents
ORDER BY VECTOR_DISTANCE(embedding, :query_vec, COSINE)
FETCH APPROXIMATE FIRST 10 ROWS ONLY;

You can also specify accuracy at query time to override the index default:

-- Override accuracy at query time
SELECT id, content
FROM documents
ORDER BY VECTOR_DISTANCE(embedding, :query_vec, COSINE)
FETCH APPROXIMATE FIRST 10 ROWS ONLY
  WITH TARGET ACCURACY 90;

Checking Execution Plans

Verify your query is actually using the vector index by checking the execution plan:

EXPLAIN PLAN FOR
  SELECT id FROM documents
  ORDER BY VECTOR_DISTANCE(embedding, :query_vec, COSINE)
  FETCH APPROXIMATE FIRST 10 ROWS ONLY;

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

Look for these operations in the plan output:

Operation Meaning
VECTOR INDEX HNSW SCAN Using the HNSW in-memory graph index
VECTOR INDEX IVF SCAN Using the IVF partitioned index
TABLE ACCESS FULL No index used — exhaustive scan

Measuring Index Accuracy

Oracle 23ai provides a built-in accuracy reporting tool to validate how closely your approximate results match the exact results:

-- Check accuracy of your HNSW index
DECLARE
  report CLOB;
BEGIN
  DBMS_VECTOR.INDEX_ACCURACY_QUERY(
    owner_name   => 'YOUR_SCHEMA',
    index_name   => 'DOCS_HNSW_IDX',
    qv           => (SELECT embedding FROM documents WHERE id = 1),
    top_K        => 10,
    report       => report
  );
  DBMS_OUTPUT.PUT_LINE(report);
END;
/

Monitoring and Maintenance

-- Check index status and population progress
SELECT owner, index_name, index_organization,
       num_vectors, allocated_bytes, used_bytes
FROM V$VECTOR_INDEX;

-- Check vector pool health
SELECT pool, alloc_bytes, used_bytes, populate_status
FROM V$VECTOR_MEMORY_POOL;

-- Rebuild HNSW index after heavy DML
ALTER INDEX docs_hnsw_idx REBUILD;

-- Drop and recreate IVF index to refresh centroids
DROP INDEX docs_ivf_idx;
CREATE VECTOR INDEX docs_ivf_idx ON documents (embedding)
  ORGANIZATION NEIGHBOR PARTITIONS
  DISTANCE COSINE
  WITH TARGET ACCURACY 95;

Common Pitfalls

Distance metric mismatch

The distance metric used at query time must match the one used when the index was created — otherwise Oracle silently falls back to an exhaustive scan:

-- Index created with COSINE
CREATE VECTOR INDEX idx ON documents(embedding)
  ORGANIZATION INMEMORY NEIGHBOR GRAPH
  DISTANCE COSINE ...;

-- Query must also use COSINE — this uses the index
ORDER BY VECTOR_DISTANCE(embedding, :qv, COSINE)

-- This does NOT use the index (EUCLIDEAN ≠ COSINE)
ORDER BY VECTOR_DISTANCE(embedding, :qv, EUCLIDEAN)

Forgetting FETCH APPROXIMATE

The single most common reason a vector index is not used. Always include FETCH APPROXIMATE in your nearest-neighbour queries.

Insufficient vector pool for HNSW

If vector_memory_size is too small, HNSW index creation fails. Use the sizing formula above and monitor V$VECTOR_MEMORY_POOL to ensure the pool is large enough before creating the index.


Quick Decision Guide

Do you have memory for the full index?
├── YES → How large is your dataset?
│         ├── < 10M vectors → HNSW (fastest queries)
│         └── > 10M vectors → IVF with large partition count
└── NO  → IVF (disk-based, no vector pool needed)

Is your table partitioned?
└── YES → IVF with LOCAL option for partition pruning

Do you need sub-50ms latency?
└── YES → HNSW, size the vector pool generously

Do you have frequent inserts/updates?
└── YES → IVF (more DML-friendly than HNSW)