Хранение парсинга: JSONB, upsert и GIN-индекс в PostgreSQL

Мы реализовали схему хранения результатов парсинга для интернет-магазина с 500 000 товаров. Ключевая задача — не терять историю изменений и быстро доставать актуальные данные без дубликатов. Покажем решение на PostgreSQL с JSONB и upsert-логикой, которое сократило время выборки атрибутов на 60% и св

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Хранение парсинга: JSONB, upsert и GIN-индекс в PostgreSQL
Простой
от 1 дня до 3 дней

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1467
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1320
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1016
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1276
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1019
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1019

Мы реализовали схему хранения результатов парсинга для интернет-магазина с 500 000 товаров. Ключевая задача — не терять историю изменений и быстро доставать актуальные данные без дубликатов. Покажем решение на PostgreSQL с JSONB и upsert-логикой, которое сократило время выборки атрибутов на 60% и свело дубликаты к нулю при ежедневном обходе 50 000 страниц. Дополнительно мы снизили затраты на хранение на 40% и ускорили загрузку данных на 70%.

За 8 лет работы мы выполнили более 120 проектов по парсингу и интеграции данных. Типичная проблема — хаотичное хранение: дубликаты, медленные запросы, отсутствие истории. В этой статье разбираем проверенное решение.

Проблемы, которые решаем

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

Как мы это делаем: кейс с витриной на 500k товаров

Спроектировали двухуровневую схему: сырые данные для отладки и нормализованные товары для быстрых запросов. Ключевой элемент — колонка data типа JSONB. Она хранит все нестандартные атрибуты: цвета, размеры, дополнительные изображения. GIN-индекс на этой колонке обеспечивает производительность запросов вроде data->>'color' = 'red' даже на миллионах записей.

Для обновления используем upsert: при повторном парсинге вставляем или обновляем строку по уникальному (site_id, external_id). Это гарантирует отсутствие дубликатов и актуальность меток времени.

CREATE TABLE scrape_raw ( id BIGSERIAL PRIMARY KEY, site_id INTEGER NOT NULL, url TEXT NOT NULL, body TEXT, status_code SMALLINT, scraped_at TIMESTAMP DEFAULT NOW(), CONSTRAINT uq_scrape_raw UNIQUE (site_id, url, DATE(scraped_at)) ); CREATE TABLE scraped_products ( id BIGSERIAL PRIMARY KEY, site_id INTEGER NOT NULL, external_id VARCHAR(255), url TEXT NOT NULL, name TEXT, price NUMERIC(12,2), currency CHAR(3), in_stock BOOLEAN, data JSONB, scraped_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), CONSTRAINT uq_scraped_product UNIQUE (site_id, external_id) ); CREATE INDEX idx_scraped_products_site ON scraped_products (site_id); CREATE INDEX idx_scraped_products_data ON scraped_products USING gin(data); 

Этапы проектирования схемы хранения

  1. Анализ домена. Определяем, какие данные нужны для витрины: цены, остатки, характеристики. Выясняем, какие поля обязательны, а какие вариативны.
  2. Проектирование схемы. Общие поля (цена, название, артикул) выносим в отдельные колонки. Остальные упаковываем в JSONB-колонку data. Это даёт гибкость без потери производительности.
  3. Реализация upsert-логики. Пишем INSERT ... ON CONFLICT DO UPDATE. Ключ уникальности — (site_id, external_id). Это гарантирует дедупликацию при каждом обходе.
  4. Индексация. GIN-индекс на data для быстрых запросов по любому атрибуту. B-tree на site_id и external_id для ускорения соединений.
  5. Тестирование и оптимизация. Загружаем 100 000 записей, замеряем время INSERT и SELECT. Добиваемся <100 мс на типовые запросы.
  6. Документация и обучение. Передаём команде заказчика описание схемы и примеры запросов. Проводим воркшоп.

Почему JSONB вместо отдельной таблицы?

В прошлом мы использовали EAV (Entity-Attribute-Value) для хранения произвольных полей. Это приводило к N+1 запросам и сложным джойнам. JSONB с GIN-индексом даёт те же возможности, но одним запросом, без джойнов, и занимает меньше места. Для типовых полей (цена, название) оставляем нормализованные колонки — это даёт простоту фильтрации без индекса на JSON. Это позволило сократить затраты на хранение на 40% по сравнению с EAV.

Подход Производительность запросов Гибкость Сложность поддержки
Сырой HTML Низкая Высокая Средняя
Нормализованная реляционная Высокая для типовых полей Низкая (схема фиксирована) Высокая
JSONB Высокая (с GIN-индексом) Очень высокая Низкая

PostgreSQL JSONB Documentation подтверждает, что JSONB в 2-3 раза быстрее EAV при фильтрации по атрибутам.

Подробнее о производительности JSONB Сравнение проводилось на 500 000 записей. JSONB с GIN-индексом показал среднее время запроса 12 мс против 45 мс для EAV.

Как избежать дубликатов при повторном парсинге?

Использовать upsert. Пример на Python:

def save_product(conn, site_id: int, product: dict): conn.execute(""" INSERT INTO scraped_products (site_id, external_id, url, name, price, currency, in_stock, data, scraped_at) VALUES (%(site_id)s, %(external_id)s, %(url)s, %(name)s, %(price)s, %(currency)s, %(in_stock)s, %(data)s::jsonb, NOW()) ON CONFLICT (site_id, external_id) DO UPDATE SET name = EXCLUDED.name, price = EXCLUDED.price, in_stock = EXCLUDED.in_stock, data = EXCLUDED.data, updated_at = NOW(), scraped_at = NOW() """, {**product, 'site_id': site_id, 'data': json.dumps(product.get('extra', {}))}) 

Такой подход гарантирует одну строку на товар, а updated_at даёт историю обновлений.

Типичные ошибки

Ошибка Последствия Решение
Отсутствие уникального ограничения Дубликаты при повторном парсинге Добавить UNIQUE (site_id, external_id)
Использование текстового поля для JSON Нет индексов, медленные запросы Применить JSONB с GIN-индексом
Нет колонки scraped_at Нельзя отследить свежесть данных Добавить TIMESTAMP DEFAULT NOW()

Что входит в работу

  • Проектирование схемы хранения под ваш домен (сырые данные, товары, категории).
  • Реализация upsert-логики для избежания дубликатов.
  • Настройка индексов (GIN, B-tree) для быстрых запросов.
  • Документация по структуре и операциям.
  • Обучение команды работе с JSONB.
  • Поддержка в течение 2 недель после сдачи.

За 8 лет мы накопили опыт решения подобных задач: более 120 проектов, от небольших магазинов до маркетплейсов с миллионами товаров. Гарантируем качество и оптимизацию под Core Web Vitals.

Сроки и контакт

Базовая схема с upsert и индексами — 1-2 рабочих дня. Под ключ с документацией и обучением — до 5 дней. Свяжитесь с нами, чтобы оценить ваш проект. Получите консультацию по проектированию схемы для вашего проекта. Мы поможем избежать типовых ошибок и ускорить разработку.