Другой диалект

Счётчик архива: платишь за прочитанные байты

10 мин
Чему научишься
  • считать стоимость запроса до его запуска: модель «платишь за прочитанные байты»
  • объяснять, почему SELECT * — деньги на ветер, а LIMIT счёт не уменьшает
  • пользоваться dry-run и кэшем результатов
  • понимать, когда компании выгодны слоты вместо оплаты за байты

Утренний счёт

Утро начинается с вызова: комендант станции переслал тебе ведомость. За вчерашние запросы к Дальнему архиву счётчик списал заметный кусок энергии станции-донора — и рядом с каждым запросом стоит цифра.

Ты просматриваешь список и замечаешь странное: самый «дорогой» запрос вернул пять строк. Самый дешёвый — пятьдесят. Цена явно не про количество строк в ответе.

КВЕРИ: Я разобрал прейскурант архива, пока ты спал. Плохая новость: платят тут не за ответ, а за чтение. Хорошая: значит, ценой можно управлять.

Счётчик станции: две колонны данных целиком утекают в мерный столб, а в чашку внизу падает лишь несколько капель результата. Курсант стоит перед счётчиком с ведомостью света, робот-кот смотрит, как падает уровень.
Платишь за прочитанные колонки целиком, а не за строки в ответе: LIMIT уменьшает вывод, но не счёт.

Модель on-demand: байты, а не строки

BigQuery в базовом режиме (on-demand) берёт деньги за одно: сколько байтов таблиц запрос прочитал. В земных деньгах — около 6,25 $ за прочитанный ТиБ (первый ТиБ в месяц бесплатный). ТиБ — тебибайт, двоичный терабайт: 1024 ГиБ ≈ 1,1 «обычного» ТБ. Прайс Google считает именно в них, поэтому и курс тоже. Счётчик станции устроен так же, просто валюта другая.

Вспомни прошлый урок: данные лежат колонками. Поэтому объём чтения считается просто:

скан = сумма размеров прочитанных колонок целиком

  • SELECT name FROM fa_users — прочитана одна колонка name, по всем строкам таблицы;
  • SELECT * FROM fa_users — прочитаны ВСЕ колонки;
  • количество строк в ответе на счёт не влияет вообще.

В Postgres ты о «стоимости» SELECT не думал: сервер твой, диск твой, разница между быстрым и медленным запросом — минуты ожидания. В BigQuery у каждого запроса есть цена в деньгах — и она известна ДО запуска.

Почему LIMIT не экономит

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

Колоночный движок работает иначе: чтобы отдать тебе хоть одну строку, он уже прочитал нужные колонки целиком — распределённо, большими блоками. LIMIT применяется в самом конце, к готовому результату. Счёт к этому моменту уже выставлен.

То же с WHERE (пока таблица не партиционирована — об этом глава «Деньги и масштаб»): фильтр уменьшает число строк в ответе, но колонки из WHERE и SELECT всё равно прочитаны по всей таблице.

Прикинем. Представь, что журнал событий колонии дорос до продовых размеров: десять колонок примерно одного веса, всего 5 ТиБ — по ~500 ГиБ на колонку. Тогда:

-- прочитает ~1 ТиБ (две колонки целиком): ~6,25 $
SELECT user_id, event_name FROM journal;

-- прочитает РОВНО столько же: ~6,25 $
SELECT user_id, event_name FROM journal LIMIT 100;

-- прочитает все 5 ТиБ: ~31 $
SELECT * FROM journal LIMIT 100;

Один SELECT * «на посмотреть» — как оставить кран открытым: вода уже утекла, сколько бы стаканов ты ни налил.

Счёт считают до LIMIT, а не послеSELECT * … LIMIT 10прочитано5 ТиБ10 строкSELECT user_id, tsпрочитано1 ТиБ10 строкплатят прочитанные колонки, а не строки вывода
Оба запроса вернут по десять строк — но заплатишь ты за прочитанные колонки

Dry-run: цена до запуска. Кэш: повтор бесплатно

Главный инструмент выживания — dry-run: прогон запроса без исполнения. BigQuery разбирает запрос, смотрит таблиц и возвращает точный объём будущего скана — бесплатно и мгновенно. В консоли BigQuery оценка видна прямо при наборе («This query will process 1.2 TB when run»), в CLI — bq query --dry_run, в API — флаг dryRun.

Привычка EXPLAIN из Postgres здесь превращается в привычку dry-run: там ты смотрел план, чтобы сберечь минуты, — здесь смотришь байты, чтобы сберечь деньги.

Второй друг — кэш результатов. Повторяешь в точности тот же запрос, а данные не менялись — BigQuery отдаёт сохранённый результат бесплатно (кэш живёт примерно сутки). Поэтому «запустить ещё раз посмотреть» не страшно; страшно «чуть поменять и прогнать по всей таблице заново». Что именно ломает кэш (например, недетерминированные функции вроде CURRENT_TIMESTAMP()) — разберём в главе «Деньги и масштаб».

Сухой прогон: счёт видно до запускатекст запроса1,4 ТиБ будет прочитаноданные не читаются, деньги не тратятся✓ узкий список колонок — счёт падает сразу✗ EXPLAIN здесь не про время, а про байты
Сухой прогон показывает объём чтения до того, как ты заплатишь

Слоты: когда байты считать надоело

У модели «за байты» есть потолок: когда запросов становится очень много, компания переходит на capacity-модель — покупает слоты, единицы вычислительной мощности, и платит за них фиксированно, сколько бы байтов ни читали запросы. На неё переходят, когда нагрузка стала постоянной или когда компании важнее заранее знать потолок расходов, чем считать цену каждого запроса. Это выбор администратора, а не аналитика: тарификация меняется, синтаксис — нет.

Для тебя правило простое: пока счёт идёт за байты — каждая лишняя колонка стоит денег; если в компании слоты — неэкономный запрос всё равно отнимает общую мощность у коллег. Дешёвые привычки окупаются в обеих моделях.

Песочница курса бесплатна — и потому честное предупреждение: эмулятор не считает байты и не умеет dry-run. Числа в этом уроке — арифметика по прейскуранту реального BigQuery. В заведи рефлекс: сначала dry-run, потом запуск.

Проверь себя
Прод-журнал колонии: 10 колонок примерно равного веса, вся таблица — 5 ТиБ. Сколько прочитает запрос SELECT user_id, event_name FROM journal LIMIT 100 и каков счёт при тарифе 6,25 $/ТиБ?
Проверь себя
Счёт за запрос к большой непартиционированной таблице оказался слишком велик. Какая правка РЕАЛЬНО уменьшит следующий счёт?
Закрепление: реши задачи
Решено 0 из 3 · для зачёта достаточно 2
Главное из урока
  • on-demand: платишь за прочитанные байты колонок, ~6,25 $ за ТиБ; строки в ответе ни при чём
  • LIMIT и WHERE (без партиций) счёт не уменьшают: колонки читаются целиком до них
  • dry-run показывает точную цену до запуска; точный повтор запроса отдаётся из кэша бесплатно
  • слоты — capacity-модель для больших команд; дешёвые привычки нужны в обеих

Привычка Postgres → идиома BigQuery (часть 1):

Привычка из PostgresКак в BigQuery
SELECT * «глянуть данные»список колонок или SELECT * EXCEPT(...)
LIMIT 10 «чтобы дёшево»LIMIT только про размер вывода; цену не меняет
EXPLAIN перед тяжёлым запросомdry-run: точные байты до запуска
CREATE INDEX нет: партиции и кластеризация (глава 5)
«база сама закэширует»кэш результатов: точный повтор — бесплатно

Документация: тарифы BigQuery, лучшие практики стоимости.

Счётчик понят. Но архив хранит данные, записанные в спешке умирающими колониями, — в них мусор, а запросу нельзя умирать. Следующий урок — SAFE-режим.