Перейти к содержанию

Кэш DNS-резолвера под микроскопом: 100 ТБ у Cloudflare и дефолт в 4 МБ у вас

Опубликовано
  • Админы
953 байта на одну запись в DNS-кэше
Размер одной записи кэша до и после пяти правок

Инженерная команда Cloudflare рассказала 27 августа, чем занималась с мая по июль: переупаковкой того, как записи DNS-кэша 1.1.1.1 лежат в оперативной памяти. Новых возможностей у резолвера не прибавилось ни одной. Прибавилось свободной памяти — порядка 100 терабайт по всему парку серверов, потому что одна запись похудела с 953 байт до 420.

Само по себе это чужая инфраструктурная история. Но подход к ней применим и в масштабе одной VPS: у Unbound или dnsmasq на домашнем сервере действуют те же законы, разница лишь в количестве нулей. С одной поправкой не в вашу пользу — лимиты своего кэша вы выставляете вручную, и стоят там, как правило, значения из коробки, к которым никто не возвращался с момента установки.

ℹ️ Справка

Материал пригодится тем, у кого крутится собственный рекурсивный резолвер — Unbound, dnsmasq, AdGuard Home, CoreDNS — и особенно тем, у кого он живёт на машине со скромной памятью. Ближе к концу разобрано, как снять фактический расход и что означают полученные числа.

Байт, который стоит 250 гигабайт

Платформа Big Pineapple обслуживает 1.1.1.1, Gateway DNS, DNS Firewall и AS112. По данным компании, в кэше единовременно лежит свыше 250 миллиардов записей. Из этой цифры следует главный тезис отчёта, и он не про красивое сравнение, а про обычное умножение: каждый лишний байт в структуре записи обходится парку более чем в 250 гигабайт.

Здесь же стоит разобрать расхождение, которое иначе выглядит как ошибка в тексте. Если 533 сэкономленных байта умножить на 250 миллиардов, выйдет за 130 ТБ, тогда как в заголовке отчёта стоит около 100. Никакого противоречия: сотня терабайт получена замером резидентной памяти процессов, а не перемножением на бумаге. Расчётный потолок и то, что удалось увидеть в мониторинге, сходиться не обязаны.

Где именно пряталась лишняя память

С точки зрения логики в записи не было ничего избыточного. Ключ (CacheKey) складывался из четырёх полей — qname, qtype, authenticated, tag. Значение (CacheEntry) насчитывало восемь: timestamp, inception, ttl, hits, answers, authority, additional, errors. Избыточной была раскладка этих полей в памяти.

Пять изменений структуры кэша
Пять правок в порядке, в котором их перечисляет отчёт

Первая статья расхода — контейнер Vec под списки записей: он всегда носит с собой поле capacity размером в восемь байт, и носит его даже тогда, когда запись давно собрана и меняться не будет. Вторая — разбиение ответа на три независимых списка (answer, authority, additional), каждый со своим указателем и своей длиной. Третья, самая наглядная, — перечисление типов записей: компилятор выравнивал его до 144 байт по самому объёмному варианту, хотя львиную долю трафика (свыше 80 %) составляют A и AAAA, которым нужны единицы байт.

Проволочный формат — приём с наибольшей отдачей

Больше всех дала пятая правка: записи стали храниться в проволочном формате (wire format). Типизированные структуры уступили место единственному байтовому буферу, внутри которого записи идут одна за другой, и перед каждой стоит двухбайтовый префикс длины. То есть ровно так, как эти же записи выглядят внутри DNS-пакета.

Выигрышей тут два. Накладные расходы на варианты перечисления просто исчезли — хранить стало нечего, кроме байтов. А отдача клиенту в большинстве случаев превратилась в копирование как есть: обратный разбор нужен лишь типам, содержащим доменные имена, то есть CNAME, NS, MX и SOA. Третьим эффектом идёт локальность — процессорному кэшу подряд лежащие байты обходятся дешевле, чем блуждание по указателям.

Замеры проводились на наборе, повторяющем продовое распределение типов: A — 56 %, AAAA — 25 %, TXT — 19 %. Про методику это говорит важное: там, где в трафике заметная доля SVCB или подписей DNSSEC, схема «мелкие типы держим внутри, крупные выносим в кучу» отработает иначе.

Что получилось в цифрах

МетрикаБылоСталоРазница
Память на запись953 Б420 Б−56 %
Аллокаций на запись1,1 КБ461 Б−58 %
Резидентная память, p999,3 ГБ5,3 ГБ−43 %
Резидентная память, p906,5 ГБ3,8 ГБ−42 %

Компактность потянула за собой и скорость: вставка в кэш стала быстрее на 43 %, поиск — на 19 %. Выкатывали изменения не одним махом, а постепенно: старт пришёлся на 18 мая 2026 года, полное покрытие всех сервисов — на 6 июля.

❗ Главное

Из этих процентов легко вывести неверное правило вида «поменяй Vec на Box<[T]> — получишь ускорение». Ускорила не смена типа сама по себе, а компактная последовательная раскладка данных при вполне определённом характере нагрузки. Другой паттерн доступа — и тот же приём не даст ничего. Приведённые числа описывают кэш Cloudflare, а не программы вообще.

Свой резолвер: что и чем померить

Искать у себя те же 953 байта смысла нет — ваш демон написан на другом языке и устроен по-другому. Зато лимит памяти под кэш у вас задан явным числом, и с высокой вероятностью это число никто не трогал.

Unbound

Из коробки параметры msg-cache-size, rrset-cache-size и key-cache-size выставлены в 4 МБ каждый. Фактическое потребление снимается так:

unbound-control stats_noreset | grep '^mem\.'

Смотреть надо на две строки: mem.cache.message — это байты, занятые кэшем сообщений, и mem.cache.rrset — то же для кэша RRset. Когда значение подобралось вплотную к лимиту, записи вытесняются, не дожив до конца своего TTL, и часть запросов напрасно уходит к вышестоящему серверу. Форма stats_noreset выбрана намеренно: обычная stats счётчики обнуляет, эта — нет.

dnsmasq

Единица измерения здесь другая: cache-size считается в именах, а не в байтах, и по умолчанию равна 150 записям — для роутера, за которым висит десяток устройств, это уже на грани. Значение 0 выключает кэширование целиком. Запущенный в отладочном режиме (-d) демон по сигналу SIGUSR1 выдаёт полный дамп кэша — простой способ посмотреть, чем тот занят на самом деле.

AdGuard Home

В AdGuardHome.yaml ключ cache_size измеряется в байтах — логика та же, что у Unbound: это бюджет, а не счётчик имён. По соседству лежат cache_ttl_min и cache_ttl_max, которыми переопределяется TTL, пришедший от апстрима.

✅ Как правильно

  • Снять текущий расход: unbound-control stats_noreset | grep '^mem\.'
  • Сопоставить mem.cache.message с msg-cache-size, а mem.cache.rrset — с rrset-cache-size
  • Если оба упёрлись в потолок, а память на машине есть — увеличить лимиты кратно (скажем, до 32m и 64m) и перезапустить демон
  • Для dnsmasq — убедиться, что дефолтные 150 имён не остались нетронутыми

⚠️ Осторожно

Тесноту кэша не лечат растягиванием TTL. Документация dnsmasq по поводу min-cache-ttl формулирует это без обиняков: искусственное продление TTL — в общем случае затея скверная, а сам параметр там сверху ограничен часом. Кэш от этого не увеличится, зато клиенты начнут получать протухшие ответы; на доменах с часто меняющимися адресами связность отвалится именно в тот момент, когда причину искать труднее всего.


Источники

💬 Вопрос к сообществу

Признавайтесь: кто-нибудь после установки резолвера возвращался к размерам его кэша? Или везде так и живут дефолтные 4 МБ и 150 имён? Если снимали статистику — насколько mem.cache.message у вас далёк от лимита?

Featured Replies

No posts to show

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

Последние посетители 0

  • Ни одного зарегистрированного пользователя не просматривает данную страницу
Яндекс.Метрика

Account

Navigation

Поиск

Поиск

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.