Article

Как мы переехали с PostgreSQL на ClickHouse — и госзакуп-аналитика начала летать

Вчера — опенсорс-эксперимент одного инженера в Яндексе. Сегодня — компания за $15 млрд. Секрет до смешного прост. Плюс — наша живая конфигурация на 781 млн строк.

ClickHouseData EngineeringPostgreSQLКазахстан14 мин
Мадияр Хамзанов
Мадияр Хамзанов
3 июля 2026

Расскажу простую историю с очень конкретным финалом. Я делаю агрегатор госзакупок Казахстана 10b.kz — мы парсим официальный портал госзакупок РК, планы, договоры, акты и казначейские платежи. Данных стало реально много: сотни миллионов строк. Сырьё лежало в PostgreSQL, и аналитика начала упираться в пол: каждый серьёзный отчёт — это полный скан и тяжёлый GROUP BY, который тянется секундами и минутами. Я перевёл всё на ClickHouse — и та же самая аналитика по таблице в 31 млн лотов стала отвечать за 0,08–0,5 секунды. Ниже — сначала на пальцах, в чём фокус, а потом — моя живая продовая конфигурация с точными цифрами.

781 млн
строк на одной VPS
192 ГиБ данных
0,08 с
агрегация 31,4 млн лотов
378 млн строк/сек
4.8×
сжатие на диске
355 → 74 ГиБ
16 vCPU
ноль шардов, одна машина
30 ГБ RAM

Коротко для тех, кто спешит

1

Одна недорогая VPS (16 vCPU, 30 ГБ RAM) держит 781 млн строк / 192 ГиБ данных о госзакупках РК — без всякого кластера и шардинга.

2

Аналитика по таблице в 31 млн лотов отвечает за 0,08–0,5 секунды. Пик сканирования — 378 млн строк/сек и 3,9 ГиБ/сек на одном сервере.

3

Секрет ClickHouse не в хитром алгоритме, а в одной простой идее: колоночное хранение — читаем с диска только нужные столбцы, а не строки целиком.

4

ClickHouse — это не замена PostgreSQL, а другой инструмент. PostgreSQL — для транзакций (OLTP). ClickHouse — для аналитики (OLAP): много чтения, агрегации, полные сканы.

1. Что это вообще такое, на пальцах

Представьте обычную базу вроде PostgreSQL как толстую тетрадь, где данные записаны по строкам: одна строка — один лот, и все его поля (номер, заказчик, сумма, дата, статус…) лежат рядом, одной записью. Удобно, когда нужно достать или поправить одну конкретную запись целиком. Но когда ты хочешь просто сумму по одной колонке за год, приходится пролистать каждую строку целиком — вместе со всеми ненужными полями.

ClickHouse хранит данные по колонкам: все суммы лотов лежат подряд в одном месте, все даты — в другом, все имена заказчиков — в третьем. Нужна сумма по одной колонке — ClickHouse читает только её, а не всю таблицу. Плюс есть бонус: значения одного типа, лежащие подряд, жмутся в разы лучше, чем перемешанные поля строки. Отсюда вся магия — меньше читаем с диска, значит, быстрее отвечаем. Это и называется колоночной базой данных (хороший разбор темы есть и у MotherDuck, и у Fivetran).

Конкретный пример из моей боевой таблицы лотов (31,4 млн строк, 5,9 ГиБ). Один и тот же GROUP BY: если запросу нужны только 2 числовые колонки, ClickHouse читает с диска 330 МиБ вместо 5,9 ГиБ — примерно в 18 раз меньше. Строчной базе в любом случае пришлось бы поднять строки целиком.

Сколько реально прочитано с диска

Один GROUP BY по таблице лотов (31,4 млн строк, логически 5,9 ГиБ). Меньше — лучше.

Первый запрос трогает 2 колонки из ~60 → 0,08 с. Второй тянет ещё и длинное строковое имя заказчика → 6,6 ГиБ и 0,46 с. Тот самый эффект «читаем только нужное».

Отсюда сразу две суперсилы. (1) Column pruning — читаем только те колонки, что нужны запросу. (2) Сжатие 4.8× — одинаковые по типу значения подряд ужимаются в разы: у меня 355 ГиБ логических данных занимают на диске 74 ГиБ. Меньше байт на диске = меньше I/O = быстрее ответ.

2. Вчера опенсорс, сегодня компания за $15 млрд

Мне нравится эта история, потому что она про силу простой идеи. В 2009 году инженер-математик Алексей Миловидов в Яндексе затеял эксперимент: можно ли строить аналитические отчёты в реальном времени прямо из сырых, неагрегированных данных, которые к тому же постоянно доливаются. Три года он доказывал гипотезу — и в 2012-м ClickHouse пошёл в прод как движок Яндекс.Метрики, второй по величине системы веб-аналитики в мире.

В июне 2016-го проект открыли под лицензией Apache 2. В сентябре 2021-го из него выросла отдельная компания ClickHouse Inc. в Сан-Франциско (основатели — Aaron Katz, Alexey Milovidov и Yury Izrailevsky; стартовые $50 млн дали Index Ventures и Benchmark, среди участников был и Яндекс). А в январе 2026-го ClickHouse привлёк $400 млн при оценке $15 млрд (раунд Series D во главе с Dragoneer) — вдвое больше, чем годом раньше ($6,35 млрд в мае 2025). Всего компания подняла более $1 млрд.

2009
эксперимент в Яндексе
Алексей Миловидов
2016
open source, Apache 2
июнь
2021
компания ClickHouse Inc.
Сан-Франциско
$15 млрд
оценка 2026
Series D, Dragoneer

Мораль. «Секрет» ClickHouse — не хитрый алгоритм, а одна простая идея (колоночное хранение для аналитики), доведённая до совершенства и заточенная под скорость. Миловидов, кстати, до сих пор сооснователь и CTO — редкий случай, когда автор опенсорс-эксперимента остаётся техническим сердцем компании за $15 млрд.

3. Честно: это не замена PostgreSQL

Скажу прямо, потому что сам долго думал, что выбрать под свой агрегатор. Главное, что я понял: ClickHouse — это НЕ замена PostgreSQL, это другой инструмент под другую задачу. Это не «лучше/хуже», это «для разного».

PostgreSQL — OLTP

  • Для транзакций: заказы, платежи, регистрации.
  • Точечные вставки и апдейты, где важна консистентность каждой записи.
  • Достать/поправить одну строку целиком — это его стихия.
  • Строчное хранение — идеально для «одна операция = одна запись».

ClickHouse — OLAP

  • Для аналитики: дашборды, отчёты, поиск по истории.
  • Много чтения и агрегаций по сотням миллионов строк.
  • Полные сканы без предрасчёта и без индекса под каждый запрос.
  • Плохо подходит для частых точечных UPDATE/DELETE.

Разницу OLTP vs OLAP проще всего запомнить так: OLTP — это «много мелких операций над отдельными записями» (банкомат), OLAP — «одна тяжёлая операция над всей историей» (годовой отчёт). ClickHouse не нужен, если данных мало, и он не про частые точечные апдейты.

Вывод простой и честный: если данных реально много и нагрузка аналитическая — дашборды, отчёты, поиск по истории — пробуй ClickHouse, не пожалеешь. Для транзакций оставь PostgreSQL.
Автор, Data Engineering

4. Наша схема: 781 млн строк на одной VPS

Теперь как это выглядит у меня в бою. Мы парсим официальный API goszakup.gov.kz (плюс Самрук, казначейство, планы). Раньше сырьё было в PostgreSQL, и аналитика упиралась в полные сканы и тяжёлые GROUP BY. Сейчас всё в ClickHouse, организованном как многослойная (medallion) ELT-архитектура — сырьё очищается и превращается в витрины слой за слоем:

СлойБазаЧто там
rawgoszakup_rawСырые выгрузки API как есть, дедуп через ReplacingMergeTree
stggoszakup_stgОчистка, типизация, нормализация
dmrtgoszakup_dmrtВитрины — широкие денормализованные таблицы
refsgoszakup_refs35 справочников (статусы, КАТО, ЕНС ТРУ, валюты)
aigoszakup_aiML-обогащение лотов (категоризация, маржа)

Ключевые таблицы — вот из чего складываются эти 781 млн строк и 192 ГиБ. Обратите внимание на движок: MergeTree для стабильных данных и ReplacingMergeTree там, где ETL постоянно доливает новые версии строк.

ТаблицаСтрокРазмерДвижок
goszakup_raw.plans_all42,0 млн11,3 ГиБReplacingMergeTree
goszakup_dmrt.lots31,4 млн5,9 ГиБMergeTree
goszakup_stg.trd_app52,5 млн4,4 ГиБMergeTree
goszakup_stg.contract_all14,4 млн4,5 ГиБReplacingMergeTree
goszakup_stg.treasury_pay11,5 млн2,8 ГиБReplacingMergeTree
goszakup_stg.acts32,7 млн2,0 ГиБMergeTree

Как ClickHouse заменил UPSERT-ы из PostgreSQL. Сырой слой на ReplacingMergeTree — это замена INSERT ... ON CONFLICT: ETL просто пишет новые версии строк по ключу, а ClickHouse сам схлопывает дубли при фоновом слиянии. Никаких блокировок и гонок на апдейтах — загрузка идемпотентна.

5. Железо и конфигурация

Никакого кластера. Одна машина. Вот вся правда о железе:

CPU
16 vCPU
AMD EPYC-Genoa @ 2.0 GHz
RAM
30 ГиБ
ClickHouse ограничен 18,4 ГиБ, остальное — под Python-ETL
Диск
600 ГБ SSD
ext4, занято 407 ГБ данными ClickHouse

ОС — Ubuntu 24.04 LTS, ClickHouse 25.12 под systemd, аптайм на момент замеров — 4 недели. Главный нюанс: сервер «горячий» — рядом на этой же машине молотит Python-ETL, который тоже хочет память. Поэтому я подрезал ClickHouse, чтобы он не съел всё под кэши и не подрался с ETL за RAM:

xml
<!-- /etc/clickhouse-server/config.d/memory.xml -->
<clickhouse>
    <!-- не забирать под сервер больше 60% RAM: рядом работает ETL -->
    <max_server_memory_usage_to_ram_ratio>0.6</max_server_memory_usage_to_ram_ratio>

    <!-- кэш засечек (primary index marks) -->
    <mark_cache_size>5368709120</mark_cache_size>        <!-- 5 ГиБ -->

    <!-- кэш распакованных блоков -->
    <uncompressed_cache_size>8589934592</uncompressed_cache_size>  <!-- 8 ГиБ -->
</clickhouse>

И ещё одна деталь, которая и даёт «полёт»: max_threads = auto(16). По умолчанию каждый запрос параллелится на все 16 ядер — одинGROUP BY раскладывается на 16 потоков и молотит данные векторно, блоками. Отсюда и сотни миллионов строк в секунду на одной VPS.

6. Два козыря: сжатие и column pruning

Козырь №1 — сжатие 4.8×. 355 ГиБ логических данных превращаются в 74 ГиБ на диске. Колоночное хранение + кодек LZ4: значения одного типа лежат подряд и жмутся кратно лучше, чем перемешанные поля строки. Меньше читать с диска = быстрее запросы. Это ровно то, за что колоночные базы обгоняют строчные на аналитике.

МетрикаЛогическиНа диске
Объём данных ClickHouse355 ГиБ74 ГиБ
Коэффициент сжатия4.8×
КодекLZ4 (колоночный)

Козырь №2 — column pruning (читается только нужное). Один и тот же GROUP BY по таблице лотов (31,4 млн строк, 5,9 ГиБ), но с разным набором колонок:

2 из ~60 колонок
Суммы по годам (2 числовые колонки)
330 МиБ · 0,08 с
Запрос трогает только 2 колонки из ~60 — остальные с диска даже не читаются.
+ строка
Топ заказчиков (+ длинное строковое имя)
6,6 ГиБ · 0,46 с
Добавили в GROUP BY тяжёлую строковую колонку — и объём чтения вырос в 20 раз.

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

7. Живые бенчмарки

Все замеры — на боевом сервере, во время работающего ETL. Никаких синтетических датасетов, только продовые таблицы. Начнём с классики — годовая динамика по 31,4 млн лотов:

sql
SELECT
    toYear(tender_publish_date_day) AS y,
    count()                         AS lots,
    round(sum(lot_amount) / 1e9, 1) AS bln_tenge
FROM goszakup_dmrt.lots
GROUP BY y
ORDER BY y DESC;

-- 2025: 3 869 168 лотов / 15 750 млрд ₸
-- 2024: 3 694 027 лотов / 15 910 млрд ₸
-- 2023: 3 910 433 лота  / 12 150 млрд ₸
-- ⏱ 0,08 с · 31,4 млн строк · 378 млн строк/сек · 3,9 ГиБ/сек

Дальше — топ-10 заказчиков по сумме лотов. Тут в GROUP BY уходит длинная строка (имя заказчика), поэтому чтение тяжелее, но всё равно доли секунды:

sql
SELECT
    customer_name_ru,
    round(sum(lot_amount) / 1e9, 0) AS bln_tenge
FROM goszakup_dmrt.lots
GROUP BY customer_name_ru
ORDER BY bln_tenge DESC
LIMIT 10;

-- АО «НК ҚазАвтоЖол» ................. 3 805 млрд ₸
-- КГУ «Управление строительства
--     г. Алматы» ..................... 2 538 млрд ₸
-- МЧС РК ............................. 1 584 млрд ₸
-- ⏱ 0,46 с · 31,4 млн строк · 69 млн строк/сек

А вот полнотекстовый поиск честным полным сканом — ILIKE '%компьютер%' по 31,4 млн названий лотов, без всякого индекса:

sql
SELECT count()
FROM goszakup_dmrt.lots
WHERE lot_name_ru ILIKE '%компьютер%';

-- 191 310 совпадений
-- ⏱ 0,32 с · полный скан 31,4 млн строк · 98 млн строк/сек

Сводка по пяти боевым запросам — агрегация, top-N по строке, сумма платежей, JOIN двух больших таблиц и полнотекстовый скан:

ЗапросСтрокВремяСтрок/сек
Q1 · агрегация по годам31,4 млн0,08 с378 млн
Q2 · top-N по строке31,4 млн0,46 с69 млн
Q3 · sum платежей11,5 млн0,17 с66 млн
Q4 · JOIN 14,4М × 11,5М26 млн0,27 с95 млн
Q5 · ILIKE полный скан31,4 млн0,32 с98 млн

Пропускная способность по запросам

Млн строк в секунду на одной VPS (16 vCPU), боевые таблицы. Больше — лучше.

Q3 — сумма всех казначейских выплат (11,5 млн платежей → 43,9 трлн ₸) за 0,17 с. Q4 — JOIN 14,4 млн договоров с 11,5 млн платежей за 0,27 с.

Про «холодный» кэш. При самом первом запуске те же запросы шли ~1 секунду (данные ещё не в кэше). После прогрева — десятые доли секунды. Но и в том, и в другом случае это интерактивная аналитика без предрасчёта и без индексов под каждый запрос. Для сравнения: такой же полный скан и GROUP BY в строчной базе на этих объёмах тянулся бы секундами и минутами.

8. Почему это вообще работает — 4 причины

1
Колоночное хранение
Читаем 330 МиБ вместо 5,9 ГиБ — только нужные столбцы, а не строки целиком.
2
Векторизованное выполнение
Запрос идёт блоками на всех 16 ядрах → сотни млн строк/сек на одной машине.
3
Сжатие 4.8×
Меньше I/O, больше данных влезает в кэш — 355 ГиБ ужимаются до 74 ГиБ.
4
MergeTree под ELT
ReplacingMergeTree даёт идемпотентную загрузку без UPSERT-ов и блокировок.

Именно четвёртый пункт и был для меня главной причиной ухода с PostgreSQL в самом пайплайне: ReplacingMergeTree заменил всю возню с INSERT ... ON CONFLICT и блокировками на апдейтах. А общий выигрыш по скорости на аналитике для колоночных баз хорошо известен — они и считаются самыми быстрыми под аналитику именно за счёт трёх множителей: сжатие в 5–10 раз, сокращение I/O на 90%+ на широких таблицах и векторизованное выполнение.

9. Как повторить у себя — просто скопируй промпт

Не обязательно быть дата-инженером. Если у тебя есть данные в PostgreSQL (или обычные csv/excel-файлы) и тормозящая аналитика — просто открой Claude Code (или Cursor) в папке проекта и скопируй промпт ниже. Агент сам поставит ClickHouse, сам перельёт туда данные и покажет, стало ли быстрее. От тебя — один клик «Копировать» и один ответ на вопрос агента, где лежат данные.

промпт для Claude Code / Cursor
Ты — мой дата-инженер. Объясняй всё ПРОСТО, шаг за шагом, без жаргона.
Задача: ускорить мою аналитику, переехав на ClickHouse. Сделай всё сам.

ШАГ 0 — ДАННЫЕ.
Мои данные лежат здесь: <PostgreSQL-строка подключения ИЛИ путь к csv/parquet/xlsx>.
Если я не указал — поищи сам в текущей папке и стандартных местах (Desktop,
Downloads) csv/parquet/xlsx или локальную базу Postgres. Если ничего не нашёл —
просто спроси меня, где данные и что я обычно по ним считаю.

ШАГ 1 — ПОСТАВЬ CLICKHOUSE ЛОКАЛЬНО.
Установи ClickHouse самым простым способом для моей ОС
(на Linux/Mac: curl https://clickhouse.com/ | sh, затем ./clickhouse server;
на Windows — через Docker: docker run -d --name ch -p 8123:8123 -p 9000:9000
clickhouse/clickhouse-server). Проверь, что сервер поднялся, и покажи мне команду
подключения (clickhouse-client).

ШАГ 2 — ПЕРЕЛЕЙ ДАННЫЕ.
Создай таблицу с движком MergeTree под мои данные (сам подбери типы колонок и
разумный ORDER BY — обычно это дата или id). Если данные обновляются по ключу —
используй ReplacingMergeTree вместо UPSERT. Залей мои данные в ClickHouse.

ШАГ 3 — ЗАМЕРЬ «БЫЛО / СТАЛО».
Возьми 3-4 моих типичных аналитических запроса (агрегации, GROUP BY, суммы по
периодам, топ-N, полнотекстовый поиск). Прогони их и в старой базе (если это
Postgres), и в ClickHouse. Замерь время каждого. Покажи размер данных на диске
до и после (у ClickHouse он будет в разы меньше из-за сжатия).

ШАГ 4 — ВЕРДИКT (просто и по-человечески):
  • Насколько быстрее стало (во сколько раз), таблицей «запрос / было / стало».
  • Сколько сэкономили места на диске.
  • Стоит ли мне переезжать на ClickHouse ПОЛНОСТЬЮ или держать его рядом с
    Postgres только под аналитику. Ответь прямо: ДА или НЕТ и почему.

ВАЖНО помни: ClickHouse — для аналитики (много чтения, отчёты, дашборды), а НЕ
для транзакций и частых точечных апдейтов — их оставь Postgres. Если данных мало
(меньше пары миллионов строк) — честно скажи, что ClickHouse мне пока не нужен.

Ничего не сломается: агент ставит ClickHouse отдельно и копирует данные, а не переносит — твой PostgreSQL остаётся на месте. Хочешь так же, как у меня — в проде на VPS — попроси агента поставить ClickHouse через официальный apt/systemd-способ и настроить max_server_memory_usage_to_ram_ratio под объём RAM сервера.

Итог

Одна VPS за копейки, 16 ядер, ноль шардов — и интерактивная аналитика по сотням миллионов строк госзакупок РК. Для аналитической нагрузки (много чтения, агрегации, полные сканы, редкие точечные апдейты) ClickHouse обходит классический PostgreSQL не на проценты, а в разы — и ещё экономит диск за счёт сжатия.

Но помню и повторю: это OLAP-инструмент. Для транзакций, точечных апдейтов и консистентности каждой записи оставляй PostgreSQL. А если данных реально много и нагрузка аналитическая — бери ClickHouse, накатывай MergeTree-схему слоями, и твоя аналитика тоже полетит. Проверено на 781 млн строк.

Мадияр Хамзанов
Мадияр Хамзанов
mkhamzanov.com

Все статьи

Блог
Мой open-source репозиторий для Bitrix24: MCP + дашборд + ETL в одном7 июн. 2026 г.
ИИ для предпринимателя: с чего начать и что можно автоматизировать31 мая 2026 г.
Бесплатный MCP для поиска товаров на Kaspi.kz — Claude видит реальные цены30 мая 2026 г.
MCP простыми словами: почему через год мы все будем жить в чате с ИИ27 мая 2026 г.
Свой VPS на Hetzner + VSCode: SSH-ключи и зачем это нужно26 мая 2026 г.
Хватит жать Allow: дайте Claude Code полные права11 мая 2026 г.
AmoCRM + Claude Code: один токен — и CRM говорит с тобой10 мая 2026 г.
Excel в LLM — это $2 за вопрос. Postgres + MD-схема — $0.019 мая 2026 г.
Railway vs Hetzner: когда платить $40, а когда €4 за один сервер1 мая 2026 г.
Data-to-Text без LLM: как генерировать тысячи описаний прямо в SQL1 мая 2026 г.
Гайд для начинающих: рабочее место разработчика с нуля за 1 час27 апр. 2026 г.
Сбор и анализ отзывов 2ГИС с Claude Code: готовые промпты3 апр. 2026 г.
Как найти 50 клиентов за 10 минут через 2GIS и сделать им сайт с AI25 мар. 2026 г.