С каким объемом данных вы работали в последнем проекте?

Ответ

В последнем проекте на PostgreSQL общий объем данных составлял около 500 ГБ. Основные таблицы:

Таблица Примерный объем записей Назначение
users ~10 млн Данные пользователей
transactions ~150 млн Финансовые операции
action_logs ~1 млрд Логирование действий

Примененные методы оптимизации:

  1. Индексация: Создание составных индексов для частых запросов.
    @Entity
    @Table(name = "transactions", indexes = {
        @Index(name = "idx_user_created", columnList = "user_id, created_at")
    })
    public class Transaction { ... }
  2. Партиционирование: Таблица action_logs была разделена по диапазонам дат (PARTITION BY RANGE (created_at)).
  3. Репликация: Настройка реплик для чтения, чтобы разгрузить primary-сервер.

Нагрузка составляла до 5k RPS, средний размер строки — 1-2 КБ.

Ответ 18+ 🔞

Да ты послушай, что у нас тут за цирк с конями развернулся! Последний проект на PostgreSQL, блядь, — это просто песня. Общий объём данных — около 500 гигов, нехило так, да? А внутри — три кита, на которых всё держится, и держится, надо сказать, с переменным успехом.

Вот смотри, какие у нас главные герои в этой эпопее:

Таблица Записей, блядь (примерно) Чем занимается
users ~10 лямов Тут наши пользователи, народ, так сказать.
transactions ~150 лямов Финансовые операции, бабло туда-сюда.
action_logs ~1 миллиард, Карл! Логирование действий, куча всякого мусора, который «на всякий случай».

И знаешь, что самое весёлое? Эта таблица action_logs — она же просто пиздец! Миллиард записей, ёпта! Представляешь? Это ж надо было столько наляпать. Ну, мы, конечно, не лыком шиты, пришлось эту махину как-то укрощать.

Что мы с этим добром делали, чтобы не сдохнуть:

  1. Индексация, мать её. Без неё — просто пипец, всё встанет колом. Для самых частых запросов лепили составные индексы. Вот, смотри, как в коде это выглядело, красота же:

    @Entity
    @Table(name = "transactions", indexes = {
        @Index(name = "idx_user_created", columnList = "user_id, created_at")
    })
    public class Transaction { ... }

    Чтобы когда ищут транзакции по юзеру и дате — не перерывали всю таблицу, как свиньи корыто.

  2. Партиционирование. Это вообще спасение для нашей action_logs. Мы её, бедную, порезали на куски по датам (PARTITION BY RANGE (created_at)). Теперь когда нужно старые логи вычистить или запрос за вчера сделать — не надо ковыряться во всём миллиарде, только в одном куске. Гениально и просто, как тапок.

  3. Репликация. Ну, куда ж без неё. Настроили реплики для чтения, чтобы primary-сервер не ебался со всеми этими «просто посмотреть» запросами. Пусть рабы работают, а царь-сервер пусть важные дела делает — пишет данные.

А нагрузка, представляешь, до 5 тысяч запросов в секунду доходила! RPS, блядь! И каждая строка в среднем по 1-2 килобайта. В общем, не скучали. Иногда казалось, что сейчас всё накроется медным тазом, но нет, вывезли. Хуй с горы, но вывезли.