Skip to content

[feature](ann-index) Support IVF on-disk index type for ANN vector search - #61160

Merged
airborne12 merged 12 commits into
apache:masterfrom
zhiqiang-hhhh:feat-ivf-on-disk
Apr 1, 2026
Merged

[feature](ann-index) Support IVF on-disk index type for ANN vector search#61160
airborne12 merged 12 commits into
apache:masterfrom
zhiqiang-hhhh:feat-ivf-on-disk

Conversation

@zhiqiang-hhhh

@zhiqiang-hhhh zhiqiang-hhhh commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR introduces a new ANN index type ivf_on_disk that stores IVF inverted list data on disk instead of fully loading it into memory. This enables vector search on datasets that exceed available memory, with a dedicated LRU cache for frequently accessed IVF list pages.

Motivation

The existing ivf index type loads the entire IVF index (including all inverted list codes and IDs) into memory during search. For large-scale vector datasets (billions of vectors), this makes the memory footprint prohibitively expensive. The ivf_on_disk approach stores the inverted list data in a separate file (ann.ivfdata) and reads only the lists needed for each query, backed by an LRU cache for hot data.

Changes

BE - Core IVF On-Disk Implementation

New index type IVF_ON_DISK:

  • Extended AnnIndexType enum with IVF_ON_DISK and added string conversion support (be/src/storage/index/ann/ann_index.h, ann_index.cpp)
  • Extended FaissBuildParameter::IndexType with IVF_ON_DISK (be/src/storage/index/ann/faiss_ann_index.h)
  • Added faiss_ivfdata_file_name constant (ann.ivfdata) for the separate data file (be/src/storage/index/ann/ann_index_files.h)
    On-disk save/load in FaissVectorIndex (faiss_ann_index.cpp):
  • Save path: Converts in-memory ArrayInvertedLists to OnDiskInvertedLists format, writes list data to ann.ivfdata and index metadata to ann.faiss
  • Load path: Reads ann.ivfdata via a CachedRandomAccessReader backed by an LRU cache; replaces the deserialized PreadInvertedLists with a cached reader that provides zero-copy borrow() for repeated list accesses
  • Introduced CachedRandomAccessReader implementing faiss::RandomAccessReader with per-range LRU caching keyed by (file-prefix, file-size, byte-offset)
    Dedicated IVF list cache (be/src/storage/cache/ann_index_ivf_list_cache.h/cpp):
  • New AnnIndexIVFListCache class — a dedicated LRU cache separated from StoragePageCache to avoid contention with column data pages
  • Configurable capacity via ann_index_ivf_list_cache_limit (default: 70% of physical memory)
  • Registered in CachePolicy as ANN_INDEX_IVF_LIST_CACHE
    Runtime environment integration:
  • ExecEnv now creates/destroys the AnnIndexIVFListCache singleton
  • Config entries: ann_index_ivf_list_cache_limit, ann_index_ivf_list_cache_stale_sweep_time_sec
    Metrics & profiling:
  • Added 6 new metrics: ann_ivf_on_disk_fetch_page_costs_ms, ann_ivf_on_disk_fetch_page_cnt, ann_ivf_on_disk_search_costs_ms, ann_ivf_on_disk_search_cnt, ann_ivf_on_disk_cache_hit_cnt, ann_ivf_on_disk_cache_miss_cnt
  • Extended AnnIndexStats and OlapReaderStatistics with IVF on-disk counters
  • Propagated stats through AnnIndexReader::query() and range_search() to SegmentIterator
    Search execution:
  • AnnIndexReader now handles IVF_ON_DISK alongside IVF for both top-N and range search paths
  • ScopedIoCtxBinding propagates IOContext via thread_local so CachedRandomAccessReader can attribute file-cache stats to the correct query
  • ScopedOmpThreadBudget now uses condition_variable to properly block waiting index builders instead of silently degrading
    Index file writer fix:
  • IndexFileWriter::add_into_searcher_cache() now correctly skips ANN indexes (both single-file HNSW/IVF and two-file IVF_ON_DISK) by checking for ann.faiss/ann.ivfdata file names
    Compound directory lifetime:
  • AnnIndexReader now holds _compound_dir alive to prevent use-after-free when CachedRandomAccessReader holds a cloned CSIndexInput whose base pointer references the compound reader's stream

FE - DDL Validation

  • AnnIndexPropertiesChecker.java: Accept ivf_on_disk as a valid index type; require nlist for both ivf and ivf_on_disk

FAISS Submodule

  • Updated contrib/faiss submodule to a version supporting PreadInvertedLists with RandomAccessReader/borrow() interface

Regression Tests

  • ivf_on_disk_index_test.groovy: Tests for L2 distance, inner product, missing nlist error, insufficient training points, larger datasets, range search
  • create_ann_index_test.groovy: Added ivf_on_disk CREATE INDEX test case
  • create_tbl_with_ann_index_test.groovy: Added CREATE TABLE with ivf_on_disk (L2, IP, missing nlist error)
  • Test data files: ivf_on_disk_stream_load.csv, ivf_on_disk_stream_load.json, ivf_on_disk_index_test.out

Usage

CREATE TABLE tbl (
    id INT NOT NULL,
    embedding ARRAY<FLOAT> NOT NULL,
    INDEX idx_emb (`embedding`) USING ANN PROPERTIES(
        "index_type" = "ivf_on_disk",
        "metric_type" = "l2_distance",
        "dim" = "128",
        "nlist" = "128"
    )
) ENGINE=OLAP
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 2
PROPERTIES ("replication_num" = "1");
-- Approximate nearest neighbor search
SELECT id, l2_distance_approximate(embedding, [1.0, 2.0, 3.0]) AS dist
FROM tbl ORDER BY dist LIMIT 10;
Known Limitations
- Stream load to ivf_on_disk tables currently fails during index building (the FulltextIndexSearcherBuilder path does not support the two-file format yet). This is covered by a regression test that asserts the current failure behavior.

@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@zhiqiang-hhhh zhiqiang-hhhh changed the title [feat](ann) support ivf on disk [feature](ann-index) Support IVF on-disk index type for ANN vector search Mar 22, 2026
@zhiqiang-hhhh

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 0.00% (0/4) 🎉
Increment coverage report
Complete coverage report

@doris-robot

Copy link
Copy Markdown
TPC-H: Total hot run time: 27028 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 6e0ec32cd12e5bdd0a2aac9268fbf36ae439b0ac, data reload: false

------ Round 1 ----------------------------------
orders	Doris	NULL	NULL	0	0	0	NULL	0	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	17622	4761	4392	4392
q2	q3	10639	781	527	527
q4	4686	368	252	252
q5	7567	1213	1004	1004
q6	174	174	145	145
q7	778	864	662	662
q8	9295	1514	1336	1336
q9	4962	4820	4779	4779
q10	6250	1965	1654	1654
q11	484	250	257	250
q12	693	652	468	468
q13	18039	2987	2181	2181
q14	230	237	210	210
q15	q16	748	756	667	667
q17	754	861	453	453
q18	6302	5415	5234	5234
q19	1100	1008	610	610
q20	536	495	380	380
q21	4359	1891	1572	1572
q22	430	378	252	252
Total cold run time: 95648 ms
Total hot run time: 27028 ms

----- Round 2, with runtime_filter_mode=off -----
orders	Doris	NULL	NULL	150000000	42	6422171781	NULL	22778155	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	4794	4737	4660	4660
q2	q3	3941	4416	3854	3854
q4	885	1210	774	774
q5	4088	4487	4332	4332
q6	198	177	144	144
q7	1795	1661	1563	1563
q8	2503	2773	2598	2598
q9	7775	7448	7453	7448
q10	3800	4014	3591	3591
q11	504	437	436	436
q12	500	599	453	453
q13	2738	3126	2367	2367
q14	296	304	283	283
q15	q16	769	795	711	711
q17	1203	1353	1338	1338
q18	7181	6861	6673	6673
q19	1018	968	928	928
q20	2065	2173	2119	2119
q21	4017	3520	3345	3345
q22	470	442	374	374
Total cold run time: 50540 ms
Total hot run time: 47991 ms

@doris-robot

Copy link
Copy Markdown
TPC-DS: Total hot run time: 168656 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 6e0ec32cd12e5bdd0a2aac9268fbf36ae439b0ac, data reload: false

query5	4345	645	516	516
query6	340	238	215	215
query7	4220	469	272	272
query8	344	248	232	232
query9	8762	2725	2717	2717
query10	525	365	358	358
query11	6940	5121	4901	4901
query12	185	129	125	125
query13	1288	451	347	347
query14	5720	3798	3505	3505
query14_1	2825	2834	2790	2790
query15	214	214	181	181
query16	981	471	475	471
query17	911	739	626	626
query18	2446	457	352	352
query19	215	224	190	190
query20	137	131	125	125
query21	213	134	112	112
query22	13184	13957	14898	13957
query23	16238	15936	15840	15840
query23_1	15642	15611	15290	15290
query24	7170	1632	1235	1235
query24_1	1246	1234	1238	1234
query25	545	464	403	403
query26	1249	262	154	154
query27	2788	475	298	298
query28	4496	1827	1841	1827
query29	834	579	492	492
query30	307	228	191	191
query31	996	942	880	880
query32	82	72	71	71
query33	513	332	289	289
query34	914	906	520	520
query35	652	669	589	589
query36	1085	1129	1008	1008
query37	137	93	83	83
query38	2971	2945	2919	2919
query39	854	819	805	805
query39_1	796	803	809	803
query40	237	157	133	133
query41	61	60	58	58
query42	270	259	253	253
query43	248	250	219	219
query44	
query45	197	192	180	180
query46	872	974	600	600
query47	2107	2680	2057	2057
query48	297	311	249	249
query49	630	457	379	379
query50	687	274	211	211
query51	4060	4033	4084	4033
query52	265	282	256	256
query53	293	332	286	286
query54	290	267	261	261
query55	92	86	87	86
query56	310	321	315	315
query57	1931	1748	1758	1748
query58	282	271	274	271
query59	2803	2942	2724	2724
query60	331	343	325	325
query61	187	156	169	156
query62	625	586	545	545
query63	307	281	277	277
query64	5044	1270	998	998
query65	
query66	1464	454	346	346
query67	24337	24296	24191	24191
query68	
query69	424	309	286	286
query70	960	961	940	940
query71	336	303	297	297
query72	2769	2664	2439	2439
query73	539	548	320	320
query74	9620	9569	9456	9456
query75	2872	2772	2476	2476
query76	2281	1049	698	698
query77	362	405	325	325
query78	10908	11062	10486	10486
query79	1102	790	564	564
query80	720	653	577	577
query81	482	264	226	226
query82	1353	159	124	124
query83	372	273	248	248
query84	303	128	100	100
query85	931	578	500	500
query86	364	306	297	297
query87	3146	3117	3069	3069
query88	3546	2640	2644	2640
query89	418	362	344	344
query90	1972	183	180	180
query91	172	167	140	140
query92	79	75	74	74
query93	903	862	499	499
query94	459	321	282	282
query95	589	335	317	317
query96	652	536	231	231
query97	2466	2499	2412	2412
query98	234	219	220	219
query99	1025	1008	915	915
Total cold run time: 248544 ms
Total hot run time: 168656 ms

@doris-robot

Copy link
Copy Markdown

BE UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 52.74% (19856/37648)
Line Coverage 36.26% (185371/511158)
Region Coverage 32.53% (143564/441395)
Branch Coverage 33.74% (62881/186355)

@zhiqiang-hhhh

Copy link
Copy Markdown
Contributor Author

run buildall

@doris-robot

Copy link
Copy Markdown
TPC-H: Total hot run time: 27092 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 71e25448b93c0e8aaa111be818b77706c2b811f8, data reload: false

------ Round 1 ----------------------------------
orders	Doris	NULL	NULL	0	0	0	NULL	0	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	17611	4545	4315	4315
q2	q3	10647	773	523	523
q4	4682	359	245	245
q5	7563	1238	1020	1020
q6	179	172	145	145
q7	778	859	660	660
q8	9307	1493	1357	1357
q9	4904	4831	4693	4693
q10	6251	1938	1651	1651
q11	497	276	258	258
q12	688	584	462	462
q13	18051	2953	2174	2174
q14	227	235	214	214
q15	q16	748	753	661	661
q17	743	848	461	461
q18	6079	5508	5276	5276
q19	1253	987	631	631
q20	545	487	379	379
q21	4743	1897	1664	1664
q22	439	374	303	303
Total cold run time: 95935 ms
Total hot run time: 27092 ms

----- Round 2, with runtime_filter_mode=off -----
orders	Doris	NULL	NULL	150000000	42	6422171781	NULL	22778155	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	4750	4605	4608	4605
q2	q3	3855	4330	3836	3836
q4	882	1204	834	834
q5	4104	4456	4332	4332
q6	221	184	144	144
q7	1758	1700	1506	1506
q8	2489	2741	2708	2708
q9	7357	7498	7465	7465
q10	3840	4025	3584	3584
q11	511	441	432	432
q12	538	591	464	464
q13	2741	3157	2353	2353
q14	284	292	278	278
q15	q16	713	753	730	730
q17	1161	1348	1420	1348
q18	7311	6890	6681	6681
q19	928	940	1008	940
q20	2147	2184	2216	2184
q21	4071	3518	3334	3334
q22	468	428	383	383
Total cold run time: 50129 ms
Total hot run time: 48141 ms

@doris-robot

Copy link
Copy Markdown
TPC-DS: Total hot run time: 168033 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 71e25448b93c0e8aaa111be818b77706c2b811f8, data reload: false

query5	4326	648	531	531
query6	336	225	206	206
query7	4222	479	267	267
query8	350	250	231	231
query9	8710	2755	2760	2755
query10	535	389	354	354
query11	7028	5094	4901	4901
query12	188	135	125	125
query13	1272	518	350	350
query14	5779	3664	3470	3470
query14_1	2875	2825	2810	2810
query15	207	196	174	174
query16	977	485	455	455
query17	905	752	628	628
query18	2446	456	364	364
query19	225	220	191	191
query20	140	132	126	126
query21	215	138	116	116
query22	13274	13292	13080	13080
query23	15766	15518	15692	15518
query23_1	15730	15884	15990	15884
query24	7565	1700	1276	1276
query24_1	1351	1352	1294	1294
query25	627	513	519	513
query26	1277	285	162	162
query27	2874	484	296	296
query28	4506	1847	1847	1847
query29	837	578	488	488
query30	302	235	185	185
query31	1016	949	880	880
query32	78	77	69	69
query33	523	339	285	285
query34	923	894	534	534
query35	657	687	604	604
query36	1030	1108	997	997
query37	142	91	86	86
query38	2966	2970	2908	2908
query39	865	833	806	806
query39_1	812	780	822	780
query40	240	151	142	142
query41	63	59	59	59
query42	263	257	256	256
query43	242	259	219	219
query44	
query45	205	186	184	184
query46	909	994	617	617
query47	2115	2125	2024	2024
query48	322	324	231	231
query49	633	470	384	384
query50	725	280	211	211
query51	4056	4052	4007	4007
query52	265	266	257	257
query53	293	336	292	292
query54	309	273	274	273
query55	93	86	82	82
query56	313	311	303	303
query57	1947	1787	1818	1787
query58	293	284	272	272
query59	2790	2945	2747	2747
query60	351	348	326	326
query61	155	151	152	151
query62	635	590	546	546
query63	310	293	284	284
query64	4975	1279	1027	1027
query65	
query66	1471	469	354	354
query67	24217	24339	24140	24140
query68	
query69	410	304	287	287
query70	999	947	926	926
query71	359	320	308	308
query72	2932	2668	2383	2383
query73	559	569	315	315
query74	9611	9573	9446	9446
query75	2861	2774	2471	2471
query76	2271	1045	695	695
query77	374	401	314	314
query78	10963	11194	10482	10482
query79	1148	787	582	582
query80	1546	629	534	534
query81	551	267	228	228
query82	1024	161	125	125
query83	349	269	251	251
query84	295	120	99	99
query85	1193	501	438	438
query86	428	302	294	294
query87	3135	3094	2977	2977
query88	3606	2684	2678	2678
query89	435	374	353	353
query90	1888	183	182	182
query91	177	164	143	143
query92	83	77	73	73
query93	1134	859	496	496
query94	636	286	285	285
query95	597	399	327	327
query96	648	530	238	238
query97	2456	2457	2360	2360
query98	242	230	222	222
query99	1019	992	926	926
Total cold run time: 250508 ms
Total hot run time: 168033 ms

@doris-robot

Copy link
Copy Markdown

BE UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 52.74% (19856/37648)
Line Coverage 36.26% (185366/511158)
Region Coverage 32.55% (143660/441402)
Branch Coverage 33.76% (62908/186359)

@zhiqiang-hhhh
zhiqiang-hhhh marked this pull request as ready for review March 23, 2026 08:47
@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100% (0/0) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.23% (26987/36850)
Line Coverage 56.69% (288817/509448)
Region Coverage 54.11% (241014/445453)
Branch Coverage 55.72% (104113/186853)

@zhiqiang-hhhh

Copy link
Copy Markdown
Contributor Author

run buildall

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: Update the ivf_on_disk regression case to assert successful stream load behavior now that the underlying path succeeds.

### Release note

None

### Check List (For Author)

- Test: No need to test (test expectation update only; no local test run in this commit)
- Behavior changed: No (regression expectation only)
- Does this need documentation: No
### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: Clarify that ann_index_ivf_list_cache_limit="70%" is based on process-available memory, and with default mem_limit="90%" it is effectively about 63% of process-visible physical memory (including cgroup constraints).

### Release note

None

### Check List (For Author)

- Test: No need to test (comment-only change)

- Behavior changed: No

- Does this need documentation: No
@zhiqiang-hhhh

Copy link
Copy Markdown
Contributor Author

run buildall

@doris-robot

Copy link
Copy Markdown
TPC-H: Total hot run time: 27093 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 5e49415b342d47d1c4170c2ff3151ebe93157779, data reload: false

------ Round 1 ----------------------------------
orders	Doris	NULL	NULL	0	0	0	NULL	0	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	17604	4562	4310	4310
q2	q3	10652	805	550	550
q4	4678	373	262	262
q5	7588	1205	1023	1023
q6	184	179	148	148
q7	810	862	695	695
q8	9314	1565	1410	1410
q9	4989	4829	4784	4784
q10	6266	1944	1655	1655
q11	474	271	257	257
q12	690	588	484	484
q13	18027	2720	1967	1967
q14	231	229	219	219
q15	q16	757	751	685	685
q17	748	857	462	462
q18	6023	5484	5338	5338
q19	1132	988	626	626
q20	550	502	385	385
q21	4362	1898	1509	1509
q22	507	389	324	324
Total cold run time: 95586 ms
Total hot run time: 27093 ms

----- Round 2, with runtime_filter_mode=off -----
orders	Doris	NULL	NULL	150000000	42	6422171781	NULL	22778155	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	4794	4590	4557	4557
q2	q3	3903	4372	3868	3868
q4	861	1231	809	809
q5	4103	4420	4426	4420
q6	193	176	148	148
q7	1811	1670	1558	1558
q8	2558	2743	2642	2642
q9	7809	7551	7555	7551
q10	3767	4046	3577	3577
q11	530	437	426	426
q12	529	639	486	486
q13	2467	2892	2135	2135
q14	283	305	269	269
q15	q16	719	749	719	719
q17	1190	1419	1384	1384
q18	7439	6925	6812	6812
q19	946	991	970	970
q20	2075	2137	2038	2038
q21	3988	3775	3481	3481
q22	494	430	376	376
Total cold run time: 50459 ms
Total hot run time: 48226 ms

@doris-robot

Copy link
Copy Markdown
TPC-DS: Total hot run time: 169049 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 5e49415b342d47d1c4170c2ff3151ebe93157779, data reload: false

query5	4365	675	498	498
query6	339	248	203	203
query7	4203	473	277	277
query8	328	234	226	226
query9	8701	2771	2769	2769
query10	513	396	340	340
query11	6943	5106	4897	4897
query12	184	126	139	126
query13	1291	455	336	336
query14	5775	3698	3455	3455
query14_1	2892	2843	2778	2778
query15	201	193	171	171
query16	958	502	456	456
query17	872	720	613	613
query18	2440	441	350	350
query19	217	209	185	185
query20	138	127	126	126
query21	212	131	109	109
query22	13273	14207	14627	14207
query23	16734	16234	16004	16004
query23_1	15954	15727	15755	15727
query24	7158	1626	1217	1217
query24_1	1249	1242	1245	1242
query25	585	526	443	443
query26	1247	261	156	156
query27	2776	482	299	299
query28	4481	1862	1877	1862
query29	852	599	509	509
query30	306	218	192	192
query31	1031	942	887	887
query32	92	72	77	72
query33	547	361	302	302
query34	879	877	548	548
query35	651	693	600	600
query36	1106	1127	954	954
query37	144	101	90	90
query38	2887	2970	2879	2879
query39	874	871	825	825
query39_1	784	824	798	798
query40	236	159	146	146
query41	70	65	64	64
query42	267	256	270	256
query43	245	253	227	227
query44	
query45	206	195	186	186
query46	884	988	627	627
query47	2135	2135	2072	2072
query48	326	331	238	238
query49	655	465	401	401
query50	697	281	221	221
query51	4085	4090	4046	4046
query52	270	270	257	257
query53	287	340	287	287
query54	306	282	272	272
query55	91	85	79	79
query56	331	315	335	315
query57	1787	1728	1505	1505
query58	276	272	272	272
query59	2804	2947	2743	2743
query60	338	347	329	329
query61	150	150	154	150
query62	599	593	531	531
query63	315	284	287	284
query64	4983	1306	1013	1013
query65	
query66	1464	468	361	361
query67	24313	24333	24194	24194
query68	
query69	406	316	285	285
query70	941	957	908	908
query71	352	317	304	304
query72	2903	2736	2441	2441
query73	539	550	321	321
query74	9587	9551	9380	9380
query75	2885	2804	2454	2454
query76	2376	1039	675	675
query77	366	397	310	310
query78	10979	11125	10487	10487
query79	1109	773	593	593
query80	1338	618	527	527
query81	555	267	227	227
query82	1009	156	123	123
query83	352	277	239	239
query84	296	118	93	93
query85	939	512	452	452
query86	423	297	282	282
query87	3116	3136	3004	3004
query88	3569	2650	2668	2650
query89	429	383	348	348
query90	2003	176	182	176
query91	173	159	144	144
query92	76	71	70	70
query93	989	842	493	493
query94	623	319	312	312
query95	585	404	320	320
query96	647	512	225	225
query97	2465	2486	2391	2391
query98	242	222	218	218
query99	1026	998	923	923
Total cold run time: 250152 ms
Total hot run time: 169049 ms

@doris-robot

Copy link
Copy Markdown

BE UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 52.80% (19888/37670)
Line Coverage 36.32% (185833/511605)
Region Coverage 32.53% (143752/441894)
Branch Coverage 33.78% (63006/186504)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100% (0/0) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.46% (27096/36886)
Line Coverage 56.91% (290266/510033)
Region Coverage 54.08% (241216/446012)
Branch Coverage 55.88% (104538/187066)

@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Mar 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by at least one committer and no changes requested.

@github-actions

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@yiguolei

Copy link
Copy Markdown
Contributor

run buildall

1 similar comment
@zhiqiang-hhhh

Copy link
Copy Markdown
Contributor Author

run buildall

@doris-robot

Copy link
Copy Markdown
TPC-H: Total hot run time: 26458 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 2cb4bb58e6f98382df2206eec6ddc977f9b0028a, data reload: false

------ Round 1 ----------------------------------
orders	Doris	NULL	NULL	0	0	0	NULL	0	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	17591	4522	4305	4305
q2	q3	10592	795	533	533
q4	4680	363	253	253
q5	7600	1225	1023	1023
q6	174	179	144	144
q7	785	839	674	674
q8	9642	1518	1343	1343
q9	5632	4986	4742	4742
q10	6330	1964	1653	1653
q11	480	258	238	238
q12	763	598	466	466
q13	18043	2719	1930	1930
q14	226	238	209	209
q15	q16	742	750	667	667
q17	738	818	466	466
q18	5859	5433	5146	5146
q19	1111	976	614	614
q20	532	483	383	383
q21	4500	1856	1422	1422
q22	346	291	247	247
Total cold run time: 96366 ms
Total hot run time: 26458 ms

----- Round 2, with runtime_filter_mode=off -----
orders	Doris	NULL	NULL	150000000	42	6422171781	NULL	22778155	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	4423	4372	4374	4372
q2	q3	3828	4304	3780	3780
q4	852	1170	776	776
q5	4051	4326	4343	4326
q6	178	175	138	138
q7	1734	1615	1522	1522
q8	2445	2662	2512	2512
q9	7874	7706	7376	7376
q10	3761	4016	3651	3651
q11	532	433	433	433
q12	496	598	475	475
q13	2534	2885	2079	2079
q14	299	301	278	278
q15	q16	883	784	725	725
q17	1183	1458	1405	1405
q18	7338	6826	6645	6645
q19	954	919	929	919
q20	2150	2126	2003	2003
q21	4250	3555	3424	3424
q22	475	432	379	379
Total cold run time: 50240 ms
Total hot run time: 47218 ms

@doris-robot

Copy link
Copy Markdown
TPC-DS: Total hot run time: 168874 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 2cb4bb58e6f98382df2206eec6ddc977f9b0028a, data reload: false

query5	4323	637	507	507
query6	336	220	210	210
query7	4225	469	276	276
query8	346	246	230	230
query9	8756	2717	2739	2717
query10	507	398	361	361
query11	6959	5058	4861	4861
query12	182	125	121	121
query13	1291	451	361	361
query14	5792	3717	3469	3469
query14_1	2852	2774	2747	2747
query15	199	193	183	183
query16	971	489	448	448
query17	1088	697	588	588
query18	2452	448	338	338
query19	213	208	180	180
query20	131	126	120	120
query21	215	137	107	107
query22	13218	13338	13177	13177
query23	16266	15866	16274	15866
query23_1	16242	15979	16251	15979
query24	7702	1687	1239	1239
query24_1	1281	1314	1273	1273
query25	568	502	453	453
query26	1286	400	179	179
query27	2789	513	314	314
query28	4977	1904	1907	1904
query29	906	588	499	499
query30	312	242	201	201
query31	1040	1000	908	908
query32	93	80	72	72
query33	541	341	306	306
query34	1157	930	574	574
query35	698	702	623	623
query36	1200	1251	1114	1114
query37	154	104	87	87
query38	3115	3158	3024	3024
query39	871	835	807	807
query39_1	801	804	815	804
query40	229	150	136	136
query41	62	59	57	57
query42	255	259	251	251
query43	241	249	225	225
query44	
query45	191	184	174	174
query46	888	990	602	602
query47	2731	2143	2036	2036
query48	307	321	224	224
query49	633	461	383	383
query50	728	273	213	213
query51	4065	4087	4013	4013
query52	260	265	256	256
query53	290	336	283	283
query54	294	284	259	259
query55	93	91	83	83
query56	309	323	312	312
query57	1924	1793	1644	1644
query58	280	271	275	271
query59	2770	2941	2802	2802
query60	338	355	307	307
query61	155	153	153	153
query62	619	593	574	574
query63	315	274	274	274
query64	5053	1279	1002	1002
query65	
query66	1463	451	360	360
query67	24183	24229	24185	24185
query68	
query69	406	325	280	280
query70	892	936	962	936
query71	328	302	281	281
query72	2880	2886	2691	2691
query73	539	544	324	324
query74	9648	9574	9404	9404
query75	2880	2761	2480	2480
query76	2314	1048	675	675
query77	373	389	311	311
query78	11013	11135	10537	10537
query79	1098	823	575	575
query80	736	654	576	576
query81	485	264	225	225
query82	1367	156	124	124
query83	387	275	253	253
query84	259	126	103	103
query85	986	499	453	453
query86	411	333	267	267
query87	3105	3107	2966	2966
query88	3540	2646	2627	2627
query89	414	366	342	342
query90	1978	179	172	172
query91	173	159	135	135
query92	89	75	69	69
query93	900	872	495	495
query94	454	329	297	297
query95	585	398	313	313
query96	645	518	223	223
query97	2455	2524	2401	2401
query98	243	221	220	220
query99	1041	1002	960	960
Total cold run time: 251955 ms
Total hot run time: 168874 ms

@hello-stephen

Copy link
Copy Markdown
Contributor

Cloud UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 78.48% (1798/2291)
Line Coverage 64.15% (32267/50296)
Region Coverage 65.04% (16181/24878)
Branch Coverage 55.49% (8621/15536)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 52.87% (19994/37819)
Line Coverage 36.44% (187649/514948)
Region Coverage 32.66% (145314/444890)
Branch Coverage 33.88% (63813/188355)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100% (0/0) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.52% (27230/37036)
Line Coverage 57.13% (293307/513394)
Region Coverage 54.24% (243548/449001)
Branch Coverage 56.08% (105949/188921)

@airborne12 airborne12 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Well-designed feature that enables IVF vector search on datasets exceeding available memory, with clean architecture and thorough documentation.

Key strengths:

  • CachedRandomAccessReader with zero-copy borrow() and per-range LRU caching aligns perfectly with IVF access patterns
  • Excellent lifetime management: _compound_dir declared before _vector_index with clear comment explaining C++ reverse destruction order, preventing use-after-free
  • FAISS exception handling added to both search() and range_search() paths
  • IO_FLAG_SKIP_PRECOMPUTE_TABLE comment provides detailed per-segment memory calculation (512 MiB per segment × 146 segments = 73 GiB)
  • Comprehensive regression tests covering L2, inner product, error cases, stream load, and range search

Behavior changes to document (non-blocking):

  • Default ivf_nprobe changed from 1 to 32 — improves recall but increases search cost for existing IVF users
  • IO_FLAG_SKIP_PRECOMPUTE_TABLE now applied to in-memory IVF load as well — saves significant RSS but may affect search latency
  • Consider adding a config flag if users need the precomputed table for latency-sensitive in-memory IVF workloads

Minor suggestions:

  • Unify config.h/config.cpp comments on cache limit percentage base
  • Add comment noting thread-local cache stats assumes single-threaded search (nq=1, parallel_mode=0)
  • Write path could chunk large list data similar to read path (kMaxChunk pattern)

@airborne12
airborne12 merged commit 62d4ab4 into apache:master Apr 1, 2026
27 of 30 checks passed
@zhiqiang-hhhh
zhiqiang-hhhh deleted the feat-ivf-on-disk branch April 1, 2026 06:20
morningman pushed a commit that referenced this pull request Apr 2, 2026
…arch (#61160)

## Summary
This PR introduces a new ANN index type `ivf_on_disk` that stores IVF
inverted list data on disk instead of fully loading it into memory. This
enables vector search on datasets that exceed available memory, with a
dedicated LRU cache for frequently accessed IVF list pages.
## Motivation
The existing `ivf` index type loads the entire IVF index (including all
inverted list codes and IDs) into memory during search. For large-scale
vector datasets (billions of vectors), this makes the memory footprint
prohibitively expensive. The `ivf_on_disk` approach stores the inverted
list data in a separate file (`ann.ivfdata`) and reads only the lists
needed for each query, backed by an LRU cache for hot data.
## Changes
### BE - Core IVF On-Disk Implementation
**New index type `IVF_ON_DISK`:**
- Extended `AnnIndexType` enum with `IVF_ON_DISK` and added string
conversion support (`be/src/storage/index/ann/ann_index.h`,
`ann_index.cpp`)
- Extended `FaissBuildParameter::IndexType` with `IVF_ON_DISK`
(`be/src/storage/index/ann/faiss_ann_index.h`)
- Added `faiss_ivfdata_file_name` constant (`ann.ivfdata`) for the
separate data file (`be/src/storage/index/ann/ann_index_files.h`)
**On-disk save/load in `FaissVectorIndex` (`faiss_ann_index.cpp`):**
- **Save path**: Converts in-memory `ArrayInvertedLists` to
`OnDiskInvertedLists` format, writes list data to `ann.ivfdata` and
index metadata to `ann.faiss`
- **Load path**: Reads `ann.ivfdata` via a `CachedRandomAccessReader`
backed by an LRU cache; replaces the deserialized `PreadInvertedLists`
with a cached reader that provides zero-copy `borrow()` for repeated
list accesses
- Introduced `CachedRandomAccessReader` implementing
`faiss::RandomAccessReader` with per-range LRU caching keyed by
`(file-prefix, file-size, byte-offset)`
**Dedicated IVF list cache
(`be/src/storage/cache/ann_index_ivf_list_cache.h/cpp`):**
- New `AnnIndexIVFListCache` class — a dedicated LRU cache separated
from `StoragePageCache` to avoid contention with column data pages
- Configurable capacity via `ann_index_ivf_list_cache_limit` (default:
70% of physical memory)
- Registered in `CachePolicy` as `ANN_INDEX_IVF_LIST_CACHE`
**Runtime environment integration:**
- `ExecEnv` now creates/destroys the `AnnIndexIVFListCache` singleton
- Config entries: `ann_index_ivf_list_cache_limit`,
`ann_index_ivf_list_cache_stale_sweep_time_sec`
**Metrics & profiling:**
- Added 6 new metrics: `ann_ivf_on_disk_fetch_page_costs_ms`,
`ann_ivf_on_disk_fetch_page_cnt`, `ann_ivf_on_disk_search_costs_ms`,
`ann_ivf_on_disk_search_cnt`, `ann_ivf_on_disk_cache_hit_cnt`,
`ann_ivf_on_disk_cache_miss_cnt`
- Extended `AnnIndexStats` and `OlapReaderStatistics` with IVF on-disk
counters
- Propagated stats through `AnnIndexReader::query()` and
`range_search()` to `SegmentIterator`
**Search execution:**
- `AnnIndexReader` now handles `IVF_ON_DISK` alongside `IVF` for both
top-N and range search paths
- `ScopedIoCtxBinding` propagates `IOContext` via `thread_local` so
`CachedRandomAccessReader` can attribute file-cache stats to the correct
query
- `ScopedOmpThreadBudget` now uses `condition_variable` to properly
block waiting index builders instead of silently degrading
**Index file writer fix:**
- `IndexFileWriter::add_into_searcher_cache()` now correctly skips ANN
indexes (both single-file HNSW/IVF and two-file IVF_ON_DISK) by checking
for `ann.faiss`/`ann.ivfdata` file names
**Compound directory lifetime:**
- `AnnIndexReader` now holds `_compound_dir` alive to prevent
use-after-free when `CachedRandomAccessReader` holds a cloned
`CSIndexInput` whose base pointer references the compound reader's
stream
### FE - DDL Validation
- `AnnIndexPropertiesChecker.java`: Accept `ivf_on_disk` as a valid
index type; require `nlist` for both `ivf` and `ivf_on_disk`
### FAISS Submodule
- Updated `contrib/faiss` submodule to a version supporting
`PreadInvertedLists` with `RandomAccessReader`/`borrow()` interface
### Regression Tests
- `ivf_on_disk_index_test.groovy`: Tests for L2 distance, inner product,
missing nlist error, insufficient training points, larger datasets,
range search
- `create_ann_index_test.groovy`: Added `ivf_on_disk` CREATE INDEX test
case
- `create_tbl_with_ann_index_test.groovy`: Added CREATE TABLE with
`ivf_on_disk` (L2, IP, missing nlist error)
- Test data files: `ivf_on_disk_stream_load.csv`,
`ivf_on_disk_stream_load.json`, `ivf_on_disk_index_test.out`
## Usage
```sql
CREATE TABLE tbl (
    id INT NOT NULL,
    embedding ARRAY<FLOAT> NOT NULL,
    INDEX idx_emb (`embedding`) USING ANN PROPERTIES(
        "index_type" = "ivf_on_disk",
        "metric_type" = "l2_distance",
        "dim" = "128",
        "nlist" = "128"
    )
) ENGINE=OLAP
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 2
PROPERTIES ("replication_num" = "1");
-- Approximate nearest neighbor search
SELECT id, l2_distance_approximate(embedding, [1.0, 2.0, 3.0]) AS dist
FROM tbl ORDER BY dist LIMIT 10;
Known Limitations
- Stream load to ivf_on_disk tables currently fails during index building (the FulltextIndexSearcherBuilder path does not support the two-file format yet). This is covered by a regression test that asserts the current failure behavior.
zhiqiang-hhhh added a commit to zhiqiang-hhhh/doris that referenced this pull request Apr 8, 2026
…arch (apache#61160)

This PR introduces a new ANN index type `ivf_on_disk` that stores IVF
inverted list data on disk instead of fully loading it into memory. This
enables vector search on datasets that exceed available memory, with a
dedicated LRU cache for frequently accessed IVF list pages.
The existing `ivf` index type loads the entire IVF index (including all
inverted list codes and IDs) into memory during search. For large-scale
vector datasets (billions of vectors), this makes the memory footprint
prohibitively expensive. The `ivf_on_disk` approach stores the inverted
list data in a separate file (`ann.ivfdata`) and reads only the lists
needed for each query, backed by an LRU cache for hot data.
**New index type `IVF_ON_DISK`:**
- Extended `AnnIndexType` enum with `IVF_ON_DISK` and added string
conversion support (`be/src/storage/index/ann/ann_index.h`,
`ann_index.cpp`)
- Extended `FaissBuildParameter::IndexType` with `IVF_ON_DISK`
(`be/src/storage/index/ann/faiss_ann_index.h`)
- Added `faiss_ivfdata_file_name` constant (`ann.ivfdata`) for the
separate data file (`be/src/storage/index/ann/ann_index_files.h`)
**On-disk save/load in `FaissVectorIndex` (`faiss_ann_index.cpp`):**
- **Save path**: Converts in-memory `ArrayInvertedLists` to
`OnDiskInvertedLists` format, writes list data to `ann.ivfdata` and
index metadata to `ann.faiss`
- **Load path**: Reads `ann.ivfdata` via a `CachedRandomAccessReader`
backed by an LRU cache; replaces the deserialized `PreadInvertedLists`
with a cached reader that provides zero-copy `borrow()` for repeated
list accesses
- Introduced `CachedRandomAccessReader` implementing
`faiss::RandomAccessReader` with per-range LRU caching keyed by
`(file-prefix, file-size, byte-offset)`
**Dedicated IVF list cache
(`be/src/storage/cache/ann_index_ivf_list_cache.h/cpp`):**
- New `AnnIndexIVFListCache` class — a dedicated LRU cache separated
from `StoragePageCache` to avoid contention with column data pages
- Configurable capacity via `ann_index_ivf_list_cache_limit` (default:
70% of physical memory)
- Registered in `CachePolicy` as `ANN_INDEX_IVF_LIST_CACHE`
**Runtime environment integration:**
- `ExecEnv` now creates/destroys the `AnnIndexIVFListCache` singleton
- Config entries: `ann_index_ivf_list_cache_limit`,
`ann_index_ivf_list_cache_stale_sweep_time_sec`
**Metrics & profiling:**
- Added 6 new metrics: `ann_ivf_on_disk_fetch_page_costs_ms`,
`ann_ivf_on_disk_fetch_page_cnt`, `ann_ivf_on_disk_search_costs_ms`,
`ann_ivf_on_disk_search_cnt`, `ann_ivf_on_disk_cache_hit_cnt`,
`ann_ivf_on_disk_cache_miss_cnt`
- Extended `AnnIndexStats` and `OlapReaderStatistics` with IVF on-disk
counters
- Propagated stats through `AnnIndexReader::query()` and
`range_search()` to `SegmentIterator`
**Search execution:**
- `AnnIndexReader` now handles `IVF_ON_DISK` alongside `IVF` for both
top-N and range search paths
- `ScopedIoCtxBinding` propagates `IOContext` via `thread_local` so
`CachedRandomAccessReader` can attribute file-cache stats to the correct
query
- `ScopedOmpThreadBudget` now uses `condition_variable` to properly
block waiting index builders instead of silently degrading
**Index file writer fix:**
- `IndexFileWriter::add_into_searcher_cache()` now correctly skips ANN
indexes (both single-file HNSW/IVF and two-file IVF_ON_DISK) by checking
for `ann.faiss`/`ann.ivfdata` file names
**Compound directory lifetime:**
- `AnnIndexReader` now holds `_compound_dir` alive to prevent
use-after-free when `CachedRandomAccessReader` holds a cloned
`CSIndexInput` whose base pointer references the compound reader's
stream
- `AnnIndexPropertiesChecker.java`: Accept `ivf_on_disk` as a valid
index type; require `nlist` for both `ivf` and `ivf_on_disk`
- Updated `contrib/faiss` submodule to a version supporting
`PreadInvertedLists` with `RandomAccessReader`/`borrow()` interface
- `ivf_on_disk_index_test.groovy`: Tests for L2 distance, inner product,
missing nlist error, insufficient training points, larger datasets,
range search
- `create_ann_index_test.groovy`: Added `ivf_on_disk` CREATE INDEX test
case
- `create_tbl_with_ann_index_test.groovy`: Added CREATE TABLE with
`ivf_on_disk` (L2, IP, missing nlist error)
- Test data files: `ivf_on_disk_stream_load.csv`,
`ivf_on_disk_stream_load.json`, `ivf_on_disk_index_test.out`
```sql
CREATE TABLE tbl (
    id INT NOT NULL,
    embedding ARRAY<FLOAT> NOT NULL,
    INDEX idx_emb (`embedding`) USING ANN PROPERTIES(
        "index_type" = "ivf_on_disk",
        "metric_type" = "l2_distance",
        "dim" = "128",
        "nlist" = "128"
    )
) ENGINE=OLAP
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 2
PROPERTIES ("replication_num" = "1");
-- Approximate nearest neighbor search
SELECT id, l2_distance_approximate(embedding, [1.0, 2.0, 3.0]) AS dist
FROM tbl ORDER BY dist LIMIT 10;
Known Limitations
- Stream load to ivf_on_disk tables currently fails during index building (the FulltextIndexSearcherBuilder path does not support the two-file format yet). This is covered by a regression test that asserts the current failure behavior.
zhiqiang-hhhh added a commit to zhiqiang-hhhh/doris that referenced this pull request Apr 8, 2026
yiguolei pushed a commit that referenced this pull request Apr 9, 2026
…es (#62215)

## Summary
- backport PR #60358, #61160 and #62178 into branch-4.1 as a single
commit
- add IVF on-disk ANN index support, related cache/runtime changes, and
FE session/property updates
- bring over ANN regression coverage updates for IVF, IVF on-disk,
small-segment and min-train-rows scenarios
@morningman morningman mentioned this pull request Jul 3, 2026
76 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by one committer. dev/4.1.0-merged reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants