2025, Oct 18 09:16

Как обойти лимит 16384 при экспорте из Milvus: миграция с итератором и батч-вставкой

Как перенести данные из Milvus и обойти лимит 16384: потоковый экспорт итератором, батч-вставка в новую коллекцию с динамическими полями. Альтернатива — VTS.

Экспорт данных из Milvus бывает необходим, когда коллекцию нужно перестроить — например, чтобы включить динамические поля. Очевидный путь — выгрузить всё и вставить в заново созданную коллекцию. Однако многие упираются в жёсткое ограничение при экспорте по запросу: потолок 16384, который прерывает процесс на полпути. Если ваш первичный ключ — строка, разбивка по диапазонам не подходит. Ниже — практичный способ провести миграцию и не наткнуться на этот барьер.

Problem overview

Milvus по умолчанию не поддерживает добавление полей, поэтому данные нужно перенести в новую коллекцию, настроенную с динамическими полями. Прямой экспорт через запрос упирается в ограничение 16384. При строковом первичном ключе пагинация по интервалам не сработает.

Naive approach that triggers the limit

Самый прямой способ — выполнить один запрос и попытаться выгрузить все записи разом. Обычно именно это и приводит к срабатыванию ограничения 16384.

def dump_all_via_query(store, coll_name, filter_expr):
    records = store.query(collection=coll_name, where=filter_expr)
    return records

Такой шаблон пытается вернуть весь набор данных одним махом. Когда данных много, процесс натыкается на потолок 16384 и обрывается, оставляя экспорт незаконченным.

Why the limit bites here

Путь через query ограничен по количеству результатов, поэтому большие выгрузки одним вызовом блокируются после 16384 записей. Поскольку первичный ключ строковый, перейти на разбивку по числовым диапазонам для пагинации нельзя. В результате полноценный экспорт через обычный запрос упирается в тупик.

Practical way out

Есть три рабочих направления. Во‑первых, в 2.6 доступна «add field». Во‑вторых, можно использовать итератор для экспорта данных — он обходит лимит одного запроса, передавая записи потоково, батчами. В‑третьих, можно воспользоваться инструментом VTS, который поможет выполнить задачу. Подход с итератором — самый прямой, если вы хотите оставить всё в коде и по мере выгрузки отправлять данные в новую коллекцию.

Схема простая: создайте итератор для исходной коллекции, забирайте порции до конца и записывайте их в целевую. Так вы обходите ограничение 16384, потому что не полагаетесь на единичный ответ запроса.

def export_with_iterator(src_handle, src_coll, cond):
    it = src_handle.iterator(collection=src_coll, where=cond)
    while True:
        batch = it.next()
        if not batch:
            break
        for row in batch:
            yield row
def migrate_to_target(dst_handle, dst_coll, rows_iterable):
    buffer = []
    for row in rows_iterable:
        buffer.append(row)
        if len(buffer) >= 1000:
            dst_handle.insert(collection=dst_coll, data=buffer)
            buffer.clear()
    if buffer:
        dst_handle.insert(collection=dst_coll, data=buffer)

Такой подход держит потребление памяти предсказуемым и переносит данные частями. Размер батча можно настроить; смысл в потоковой передаче, а не в накоплении всего в одном огромном ответе.

Where the version and tooling fit in

Если вы на 2.6, учтите, что в 2.6 доступна «add field». Если не хочется писать логику экспорта, можно воспользоваться VTS. Маршрут с итератором — это подход «сначала код» и он хорошо работает, когда у вас уже есть конвейер вставки в новую коллекцию с динамическими полями.

Why this matters

Миграции данных нередки, когда схема эволюционирует. Столкновение с жёстким лимитом запроса посреди миграции может задержать релиз. Знание о том, что экспорт на итераторе обходит потолок 16384 и что доступен VTS, даёт понятный путь вперёд — даже когда первичный ключ строковый и пагинация по интервалам не подходит.

Conclusion

При переносе данных в коллекцию с динамическими полями избегайте единичных выгрузок по запросу, которые упираются в лимит 16384. Передавайте набор данных потоково: используйте итератор для экспорта и вставляйте записи в целевую коллекцию батчами. Если вы на 2.6, помните, что в 2.6 доступна «add field», а если не хотите писать код — поможет VTS. Планируйте экспорт как потоковую операцию, и миграция станет предсказуемой и устойчивой.

Статья основана на вопросе на StackOverflow от Jade Roy и ответе от James Luan.