Возьмём два набора данных с одинаковым общим объёмом. Первый — один архив на 2 ГБ. Второй — папка, где те же 2 ГБ разложены на сто тысяч файлов по несколько десятков килобайт. Если Nextcloud принимает архив за считанные минуты, а папку мусолит часами, канал до сервера почти наверняка не главный подозреваемый: байты в обоих случаях одни и те же. Различается другое — число отдельных операций. Каждый маленький файл требует собственного запроса, проверки прав, записи в базу данных, создания записи в файловом кеше и, возможно, работы приложений, подписанных на события. Большой файл платит за эти операции один раз, мелкие файлы платят сто тысяч раз.

Поэтому процент загрузки канала или «скорость интернета» объясняют задачу хуже, чем сравнение двух контролируемых прогонов. Ниже — порядок действий, при котором каждое наблюдение отсекает часть гипотез, а не добавляет новых догадок. Если вам нужна более общая картина причин вялой работы, она разобрана в материале Nextcloud: медленно работает — причины и решение; здесь мы сосредоточены на одном симптоме.

Два набора, которые можно повторить

Прежде чем смотреть на сервер, нужно создать эталон. Без него любые «стало быстрее» превращаются в ощущения. Подготовьте на клиентской машине два каталога:

  • каталог A: один файл около 2 ГБ (подойдёт любой несжимаемый файл, например случайные данные);
  • каталог B: около 2 ГБ в виде десятков тысяч файлов разного размера, от нескольких килобайт до сотен килобайт.

Числа здесь условные: важно, чтобы общий объём совпадал по порядку величины, а набор B можно было воссоздать в точности — так же, как он был создан в первый раз. Лучше сгенерировать его скриптом с фиксированным начальным значением генератора случайных чисел либо сохранить архив-образец и каждый раз распаковывать заново. Каждый прогон должен начинаться с пустой целевой папки в аккаунте тестового пользователя, чтобы сервер не тратил время на сравнение с уже загруженным.

Для каждого прогона заведите журнал. Достаточно таблицы с такими колонками:

Журнал одного прогона
ПолеЧто записать
НаборA (один файл) или B (много файлов)
Число файлов и объёмнапример, 1 файл и 2 ГБ либо 100 000 файлов и 2 ГБ
Время начала и концапо часам клиента, до секунды
Среднее число файлов в секундучисло файлов, делённое на длительность
Среднее МБ/собъём, делённый на длительность
Наблюдения на серверепроцессор, диск, RAM, swap, ведущие процессы
Что изменилось с прошлого прогонаодна строка, ровно одно изменение

Последняя колонка важнее остальных. Если между двумя прогонами вы поменяли и кеш, и параметры PHP, и режим фоновых задач, а передача ускорилась, то причина ускорения останется неизвестной, и в следующий раз придётся гадать заново. Одно изменение — один прогон.

Что покажет сравнение A и B само по себе

Посчитайте две метрики. Для набора A: мегабайты в секунду. Для набора B: файлы в секунду и те же мегабайты в секунду. Рассмотрим условный пример. Допустим, архив на 2 ГБ ушёл за 4 минуты (240 секунд), это около 8,5 МБ/с. Папка из 100 000 файлов тех же 2 ГБ ушла за 100 минут (6000 секунд): около 0,34 МБ/с и около 17 файлов в секунду. Разница в 25 раз при той же сети и том же клиенте. Это не измерение вашего сервера, а иллюстрация логики: если по набору A сеть спокойно даёт 8,5 МБ/с, то на наборе B она заведомо простаивает, а время уходит на что-то с пропорциональной файлам ценой — примерно 60 миллисекунд на файл в этом примере.

Теперь полезно перевернуть вопрос: чему равна фиксированная цена одного файла? Запустите ещё один маленький прогон — 1000 файлов по 1 КБ. Объём ничтожен, сеть не может быть узким местом по пропускной способности, а значит, время прогона почти целиком состоит из накладных расходов. Поделите время на число файлов и получите оценку цены одной операции. Если это 50–100 мс на файл, вы смотрите на задержки на каждый запрос (сеть с большим временем отклика, сервер, база). Если 5 мс, то накладные расходы невелики, и стоит искать проблему в другом месте — например, в том, что клиент вообще долго собирает список изменений.

Отдельно обратите внимание на роль задержки сети. Передача большого файла сглаживает время отклика: данные идут потоком. Для множества мелких файлов каждый запрос ждёт ответа, и кругового времени до сервера (RTT) уже достаточно, чтобы заметно замедлить передачу, даже когда канал почти пуст. Простая проверка: сделайте тот же прогон B с машины в той же сети, где находится сервер или рядом с ним. Если скорость в файлах в секунду выросла в разы, значит, существенную долю времени съедали круговые задержки. Если не изменилась, задержки сети не главная причина, и стоит смотреть на сам сервер. Здесь же стоит помнить, что клиент синхронизации передаёт файлы частями, а ограничения вроде upload_max_filesize и post_max_size могут не применяться к WebDAV PUT и chunked uploads так же, как к обычной веб-форме (Nextcloud: Uploading big files). То есть «поднять лимиты PHP» для синхронизации мелочи почти никогда не лекарство: проблема не в размере файла.

Смотрим на сервер во время передачи

Документация Nextcloud советует начинать настройку с определения процесса или диска, который создаёт нагрузку, и с наблюдения за RAM и swap (Nextcloud: Server tuning). Для нашей задачи это превращается в понятный ритуал: запускаете прогон B и параллельно в нескольких сессиях SSH смотрите, что происходит.

Минимальный набор наблюдений:

  • top или htop — какие процессы держат процессор: веб-сервер, PHP-FPM, процесс базы данных;
  • iostat -x 5 (пакет sysstat) — загрузка диска и время ожидания операций;
  • vmstat 5 и free -m — свободная память и активность swap;
  • pidstat либо колонки в top — какой именно процесс создаёт обращения к диску.

Теперь о том, как читать картину. Приведём типичные сочетания и то, что они значат.

Сочетания наблюдений и вероятные причины
Что видно во время прогона BЧто это означает
Процессор сервера почти свободен, диск спокоен, процессы простаивают, при этом файлы идут медленноВремя уходит на ожидание: задержки сети, ограничение числа параллельных передач на стороне клиента, синхронные обращения без нагрузки на сервер
Процессы PHP-FPM (или Apache с PHP) занимают все ядраКаждый запрос тяжёл: много кода на запрос, включённые приложения, подробное логирование, отсутствие кеша кода
Процесс базы данных занимает много процессора или ждёт дискУзкое место — операции с файловым кешем и блокировками в базе
Диск показывает высокую загрузку и большое время ожидания, а процессор ждёт ввод-выводХранилище не справляется с большим числом мелких операций записи (файлы, журналы базы, временные данные)
Свободная RAM исчерпана, растёт активность swapПроцессы выдавлены на диск; любое число файлов в секунду будет падать не из-за сети, а из-за подкачки

Теперь проведите тот же набор наблюдений на прогоне A (один большой файл). Картина должна отличаться. Для большого файла характерна непрерывная запись на диск и высокая загрузка канала при небольшой нагрузке на процессор. Для множества мелких — наоборот: пакетная активность процессора и базы, множество коротких записей, а загрузка канала невелика. Если на прогоне A тоже заметна нагрузка на базу и процессор, возможно, дело в чём-то общем (например, в включённом отладочном логировании, о котором ниже).

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

Следите и за временным хранилищем. Для параллельных загрузок временное место нужно учитывать не только под один максимальный файл, но и под все одновременно идущие передачи (Nextcloud: Uploading big files). Если клиент отправляет много файлов параллельно, а временный каталог лежит на медленном или почти заполненном разделе, он превращается в скрытое узкое место. В журнал стоит записать, где находится временный каталог и сколько на нём свободно.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.

АРЕНДОВАТЬ СЕРВЕР

База данных и кеширование под тысячами операций

Если наблюдения указали на процесс базы, стоит разобраться, какая именно база стоит под Nextcloud. Документация рекомендует для высокой конкуренции MariaDB/MySQL или PostgreSQL вместо SQLite (Nextcloud: Server tuning). Причина в природе нагрузки: при синхронизации тысяч файлов одновременно идут десятки запросов на запись, и SQLite, которая блокирует файл базы при записи, плохо подходит для такой параллельной работы. Если ваша установка работает на SQLite, прогон B покажет это очень явно: процессор PHP и диск не загружены, но запросы выстраиваются в очередь на блокировке. Это единственное изменение, которое здесь стоит делать в первую очередь, и оно оправдано самой логикой нагрузки, а не желанием «подкрутить настройки».

Если база уже серверная, проверьте несколько вещей:

  • хватает ли серверу базы оперативной памяти для рабочих данных, и нет ли активной подкачки;
  • лежит ли каталог данных базы на том же диске, что и пользовательские файлы: тогда мелкие файлы конкурируют с журналами базы за одни и те же операции ввода-вывода;
  • не копятся ли блокировки и долгие запросы (медленный журнал запросов базы покажет это без догадок).

С кешированием история похожая. Документация подчёркивает, что кеширование требует установленных компонентов, то есть сама по себе настройка в конфигурации ничего не даёт, если нужного расширения или службы нет (Nextcloud: Server tuning). Для нашей задачи рассуждение простое: на каждый запрос сервер повторно читает одни и те же данные — настройки, права, структуру. Без кеша это каждый раз обращение к базе или файловой системе; при ста тысячах файлов повторные чтения складываются в заметное время. Поэтому в журнал нужно записать, какой кеш подключён и работает ли он по факту: наличие строки в конфигурации — не доказательство, а вот отсутствие обращений к кешу или ошибки в журнале — признак того, что механизм не работает.

Ставить эксперимент нужно по тому же принципу. Допустим, вы выяснили, что кеш не подключён. Подключите его, ничего больше не меняя, и повторите прогон B на пустой целевой папке. Если скорость в файлах в секунду выросла, вы получили доказанный эффект именно этого изменения. Если нет — вернитесь к журналу и переходите к следующей гипотезе, не накапливая «на всякий случай» изменений, которые потом придётся разбирать.

Фоновые задачи: когда они мешают, а когда нет

Фоновые задачи Nextcloud выполняют служебные операции, включая очистку временных данных и задачи приложений (Nextcloud: Background jobs). Они могут пересекаться с массовой загрузкой по времени и конкурировать за процессор, диск и базу. Но чтобы это утверждение стало диагнозом, его нужно подтвердить наблюдением: во время прогона B видно, что процессы запуска фоновых задач действительно активны и их пик совпадает с провалом скорости передачи. Если провалов нет, фоновые задачи не виновны, и гипотезу можно закрыть.

Режимов запуска фоновых задач три: AJAX, Webcron и cron. Cron рекомендован; AJAX запускает задачу при посещении страницы, то есть зависит от визитов пользователей (Nextcloud: Background jobs). Для диагностики это имеет два следствия.

  1. При режиме AJAX момент выполнения служебных операций определяется активностью пользователей, поэтому поведение системы плохо повторяется от прогона к прогону. Один и тот же набор B может отработать быстро в одном запуске и медленно в другом просто потому, что кто-то в это время открывал веб-интерфейс. Для сравнимых измерений это неудобно: расписание должно быть предсказуемым.
  2. При cron запуск идёт по расписанию, независимо от визитов. Такой режим проще наблюдать: время запусков известно, их можно сопоставить с графиками нагрузки и со временем прогона.

Если вы работаете в режиме AJAX, переключение на cron — это то самое «одно изменение» между прогонами. Его стоит делать отдельно, а затем повторить B на пустой целевой папке и посмотреть, изменился ли разброс времени между повторами. Иногда средняя скорость почти не меняется, но исчезают случайные провалы — это тоже результат, хотя и менее эффектный.

Теперь о соблазне «просто остановить фоновые задачи на время загрузки». Тут нужна аккуратность. Окно обслуживания действует в режиме cron для задач, которые заявляют себя как нечувствительные ко времени; оно не останавливает все фоновые операции (Nextcloud: Background jobs). Значит, если вы назначили окно обслуживания на ночь и запускаете в этот момент массовую загрузку, нельзя рассчитывать на полностью свободный сервер: часть задач может продолжать выполняться. Проверять нужно не настройку, а факт — по списку процессов и нагрузке во время прогона. Если окно обслуживания помогло и провалы исчезли, прекрасно; если картина не изменилась, значит, нагрузку создают задачи, которые не подпадают под окно, либо дело вообще не в фоновых операциях.

Отладочный режим как скрытая причина

Отдельный подозреваемый, который легко забыть, — подробное логирование. Документация предупреждает, что DEBUG-логирование и debug mode могут ухудшать производительность и предназначены для диагностики (Nextcloud: Server tuning). Для задачи со множеством мелких файлов это особенно ощутимо по простой арифметике: если на каждый файл приходится хотя бы несколько строк журнала, то на сто тысяч файлов получаются сотни тысяч записей. Каждая запись — это операция ввода-вывода и немного процессорного времени, а журнал растёт и сам занимает место на диске, на котором лежат и данные.

Есть и обратная ловушка. Чтобы разобраться в проблеме, вы включаете подробный уровень, и после этого передача идёт медленнее, чем до диагностики. Тогда ваши измерения искажены самим инструментом наблюдения. Поэтому порядок такой: сначала сделайте прогоны A и B при обычном уровне логирования и зафиксируйте базовые числа; затем при необходимости включите DEBUG, повторите B, посмотрите разницу и найденные в журнале ошибки; после этого верните прежний уровень. В журнале экспериментов это отдельная строка: «включён DEBUG — время такое-то, выключен — время такое-то». Если разница заметна, вы получили точную цену диагностики и одновременно подтверждение, что в эксплуатации отладочный режим включать не стоит.

Как выглядит завершённая диагностика

Последний шаг — выключить всё, что включали для измерений, и сделать контрольный прогон. Проверьте, что уровень логирования вернулся к рабочему, debug mode выключен, расписание фоновых задач соответствует выбранному, а набор B снова разворачивается на пустой целевой папке. Контрольный прогон должен дать число, которое сравнивается с базовым в журнале: файлов в секунду и МБ/с при одинаковых условиях. Если число лучше базового, а единственное изменение известно, вы знаете причину. Если число не изменилось, значит, проверенная гипотеза не работала, и вы вычеркнули её без ущерба для остального.

Покажем, как может выглядеть журнал. Числа условные.

Пример журнала диагностики (условные значения)
ПрогонИзменениеФайлов/сНаблюдение
1 (A)базовый, один файл 2 ГБ—канал загружен, процессор спокоен
2 (B)базовый, 100 000 файлов17процесс базы на пределе, диск спокоен
3 (B)подключён кеш19почти без изменений
4 (B)переход с AJAX на cron20разброс между повторами уменьшился
5 (B)перенос с SQLite на серверную базу55очередь на блокировке исчезла

В таком условном примере вывод очевиден: главным узким местом была база, а не канал, не фоновые задачи и не нехватка ресурсов. Из пяти шагов сработал один, и именно потому, что менялась одна вещь за раз. Остальные изменения не принесли заметного ускорения, но тоже не были бесполезны: они сняли подозрения и сделали поведение предсказуемым.

Когда действительно стоит смотреть на ресурсы VPS

Лишь после диагностики имеет смысл говорить о размере сервера. Признаки того, что упёрлись именно в ресурсы, такие: свободная оперативная память заканчивается и растёт swap; процессор постоянно занят всеми ядрами при нормально настроенных базе и кеше; диск показывает высокое время ожидания на обычной нагрузке. В каждом из этих случаев увеличение ресурса адресно лечит найденную причину. Если же главным оказалось ожидание на блокировке SQLite или отсутствие кеша, большой тариф только дороже покажет ту же очередь.

Если вы разворачиваете Nextcloud на VPS, полезно помнить, что на мелких файлах важна не только скорость последовательной записи, но и способность хранилища обрабатывать множество мелких операций. На VPS MAATRIX используются NVMe-диски, процессоры AMD EPYC и root-доступ, поэтому всё описанное выше — наблюдение за процессами, дисками и памятью, настройка кеша, режима фоновых задач и базы данных — можно выполнять самостоятельно. Тарифы различаются по ресурсам: от Ba (1 vCPU, 2 ГБ RAM, 40 ГБ NVMe) до Djed (16 vCPU, 32 ГБ, 320 ГБ), а между ними Uab, Heka и Maat; сами по себе эти цифры не гарантируют конкретного числа пользователей или файлов в секунду — это показывают только замеры вашей нагрузки. Подробности предложения и актуальное наличие Nextcloud с установкой смотрите на странице аренды VPS MAATRIX: точное состояние образов и ОС проверяется в форме заказа.

Что менять после измерений

Вернёмся к началу: архив на 2 ГБ идёт быстро, потому что платит за операции один раз, а сто тысяч файлов платят каждый. Поэтому правильный вопрос не «насколько быстрый у нас интернет», а «сколько стоит один файл и из чего эта цена складывается». Журнал с числом файлов, временем и показаниями ресурсов превращает этот вопрос в набор проверяемых гипотез: ожидание сети, тяжёлые запросы PHP, база данных, диск, подкачка, фоновые задачи, отладочное логирование. Каждая отсекается одним прогоном и одним изменением.

Итог диагностики — не список «улучшений», а одно узкое место и одно обоснованное изменение. Если им оказалась SQLite под параллельной записью, меняйте базу. Если не работал кеш, подключайте его. Если провалы совпадали с запуском фоновых задач при режиме AJAX, переходите на cron. Если упёрлись в подкачку, добавляйте оперативную память. Выключите отладку, повторите контрольный прогон и запишите разницу. Тарифный план нужно менять только тогда, когда измерения показали, какого именно ресурса не хватает.