У Cloudflare D1 есть важная особенность: расход считается не количеством SQL-команд, а количеством строк, которые запрос прочитал или записал. Поэтому небольшой Worker с неудачным запросом способен израсходовать дневную квоту быстрее, чем проект с большим числом хорошо индексированных обращений.
На Workers Free официальная документация Cloudflare указывает 5 миллионов rows read в день, 100 000 rows written в день и 5 GB суммарного хранилища. После достижения дневного лимита чтения или записи запросы к D1 больше не выполняются: API возвращает ошибку до сброса квоты. Бесплатные дневные лимиты сбрасываются в 00:00 UTC.
Ниже разберём, что это означает для ботов, webhook, каталогов и API-интеграций на Workers, как найти дорогие запросы и когда оптимизации достаточно, а когда разумнее перейти на Workers Paid.
Какие лимиты действуют на D1 Free
| Метрика | Workers Free | Что считается |
|---|---|---|
| Rows read | 5 млн в день | Строки, просмотренные запросами |
| Rows written | 100 000 в день | INSERT, UPDATE, DELETE и связанные записи индексов |
| Storage | 5 GB всего | Таблицы и индексы во всех D1 базах аккаунта |
Источник значений — официальная документация Cloudflare D1 Pricing. Cloudflare отдельно поясняет, что размер строки и количество столбцов не меняют сам принцип подсчёта: важна строка, которую движок прочитал или записал.
Почему SELECT может оказаться дорогим
Представим таблицу из 50 000 товаров. Worker выполняет запрос по полю external_id, но индекс на этом поле не создан. Пользователь получает одну карточку, однако база может просканировать десятки тысяч строк. В метрике это отражается как rows read, даже если наружу вернулась одна запись.
Если такой запрос вызывается ботом, webhook или публичным API много раз в течение дня, лимит расходуется неочевидно. Поэтому первое действие при росте rows read — не добавлять кэш вслепую, а проверить SQL и индексы.
Индекс уменьшает чтение
Cloudflare прямо рекомендует индексы для часто фильтруемых полей. Индекс позволяет базе читать меньший диапазон вместо полного сканирования таблицы. При этом запись в индекс тоже учитывается в rows written, поэтому индексировать каждый столбец подряд не стоит.
CREATE INDEX IF NOT EXISTS idx_products_external_id
ON products(external_id);
Для каталога часто полезны индексы по внешнему ID, статусу, времени обновления и составным ключам, которые действительно используются в WHERE и JOIN. Решение должно исходить из реальных запросов, а не из количества столбцов.
Что происходит после превышения лимита
На Free-плане это не модель «немного превысил и доплатил». Согласно FAQ Cloudflare, после достижения дневного лимита read или write D1 не позволяет выполнять запросы и возвращает клиенту ошибку о превышении квоты. Рабочий бот или endpoint, который зависитим от D1, в этот момент может перестать выполнять бизнес-операции.
Именно поэтому production-сценарию нужна обработка ошибок. Нельзя считать, что любой сбой D1 — временный сетевой таймаут, и бесконечно повторять запрос: повторные попытки способны только увеличить нагрузку.
Как увидеть реальный расход D1
Cloudflare предлагает три основных источника: объект meta в результате запроса, D1 Metrics в dashboard и GraphQL Analytics API. В meta доступны в том числе rows_read и rows_written.
const result = await env.DB
.prepare('SELECT id, name FROM products WHERE external_id = ?1')
.bind(externalId)
.all();
console.log(result.meta.rows_read);
В production лучше не писать в лог пользовательские payload и секреты ради диагностики. Достаточно агрегировать название операции, длительность, rows read, rows written и код результата.
Чек-лист оптимизации перед переходом на Paid
- Найдите full table scan. Особенно запросы по неиндексированным полям и выборки без ограничений.
- Уберите SELECT * там, где он не нужен. Это не меняет число прочитанных строк само по себе, но уменьшает объём результата и делает контракт API понятнее.
- Добавьте индексы под реальные WHERE и JOIN. После этого сравните rows_read до и после.
- Не опрашивайте D1 без необходимости. Статические настройки можно держать в памяти одного запроса или другом подходящем кэше.
- Синхронизируйте изменения пакетами. Для каталога из внешней системы не обязательно переписывать все записи, если изменилось пять.
- Используйте idempotency. Повторный webhook не должен повторно создавать одни и те же записи.
- Поставьте наблюдение за квотой. Рост rows read лучше увидеть до того, как база начнёт возвращать ошибки.
Пример: бот с каталогом
Допустим, бот получает товары из внешней системы, а D1 используется как быстрая рабочая база. На каждом сообщении бот ищет настройки сценария, состояние пользователя и несколько товаров. Если каталог синхронизируется полной перезаписью каждые пять минут, rows written расходуются на данные, которые фактически не менялись.
Более устойчивый вариант — передавать только изменения по стабильному external ID и выполнять upsert только для изменившихся записей. Горячие SELECT должны идти по индексированным ключам. Такая архитектура одновременно уменьшает задержки и расход квоты.
Подробную схему Worker → D1 → внешние API я уже разбирал в материале Cloudflare Workers + D1: архитектура API-интеграции без VPS. Эта статья дополняет её именно эксплуатационной частью — лимитами и контролем нагрузки.
Когда Free-плана достаточно
Free подходит для прототипов, небольших внутренних инструментов, умеренных webhook-нагрузок и проектов, где запросы хорошо индексированы и есть запас по дневной квоте. Важно оценивать не только средний день, но и всплески: импорт, массовая синхронизация или ошибочный цикл способны резко изменить профиль нагрузки.
Когда лучше Workers Paid
Если D1 находится в критическом пути заказов, сообщений или заявок и остановка до следующего сброса квоты неприемлема, платный план может быть частью требований к надёжности. На Paid вместо жёстких дневных read/write лимитов действует включённый месячный объём с оплатой превышения по опубликованным тарифам.
Переход на Paid не отменяет оптимизацию SQL. Неиндексированный запрос остаётся медленным и дорогим, просто проблема превращается из дневного ограничения в расход.
Безопасность и надёжность
Лимиты — только один слой. Секреты внешних API следует хранить в Workers Secrets, входящие webhook проверять, а повторную доставку обрабатывать идемпотентно. Для операций записи полезно валидировать payload до обращения к базе, чтобы мусорные запросы не расходовали write-квоту.
Итог
Главное правило D1 Free в 2026 году простое: считайте строки, а не SQL-команды. Контролируйте rows_read и rows_written, индексируйте реальные точки поиска, не пересинхронизируйте неизменившиеся данные и заранее решите, что приложение будет делать при исчерпании квоты.
Если нужно разобрать существующую Worker/D1-интеграцию, убрать лишние обращения к базе или перенести медленный webhook-контур на serverless-архитектуру, это можно сделать в рамках автоматизации бизнес-процессов. Для оценки конкретной схемы можно написать A.S Groups.
FAQ
Сколько строк можно читать в D1 Free?
Официальный лимит Workers Free — 5 миллионов rows read в день.
Сколько строк можно записывать?
100 000 rows written в день. Индексы также могут добавлять учитываемые записи при изменении индексируемых данных.
Что будет после превышения?
D1 перестанет выполнять запросы, затронутые дневным лимитом, и будет возвращать ошибку до сброса квоты. Free-квоты сбрасываются ежедневно в 00:00 UTC.
Помогают ли индексы?
Да. Для запросов с фильтрацией индекс может существенно уменьшить число прочитанных строк и одновременно ускорить запрос.
Обсуждение
Вопросы и комментарии
Можно уточнить детали статьи или поделиться своим опытом. Первый комментарий проходит проверку.