How's that again?

10 способов достижения HighLoad'а и BigData на ровном месте

1. Масштабирование

Типичный случай: расчитываем на миллион пользователей, поставили 100 инстансов PostgreSQL, шардирование по created_at.

Последствия:

  • любое взаимодействие, затрагивающее пользователей, лежащих на разных нодах (например, чат) - боль, логика сильно усложняется
  • оказывается, что пользователи, созданные давно - малоактивны, поэтому 90 машин кластера простаивают, а оставшиеся 10 утилизованы на 100%. Надо было шардить по user_id.
  • при обращении к незанятой машине кэш у нее холодный и запарос выполняется в разы дольше

Как надо было:

  • нечего было сразу масштабироваться, нужно было сначала вырасти до ресурсов одной машины, потом посчитать стоимость ее апгрейда и только после этого принимать решение о масштабировании

2. Бизнес хочет хранить данные за все время

Нужны отчеты за все время, поэтому мы в одну огромную базу сохраняем все данные и никогда не удаляем

Последствия:

  • через несколько лет получаем big data на пустом месте

Как надо было:

  • ретроспективные данные можно архивировать
  • не хранить сырые данные, а хранить агрегаты по ним (но это если бизнес согласится, что им не понадобятся новые агрегаты)
  • можно партиционировать и на одном сервере хранить архивные данные, на другом - горячие + агрегаты

3. EAV упрощает проектирование

EAV (Entity-attribute-value) - это подход, используемый когда у нас есть сущности, у которых есть много атрибутов, но используется лишь малая их часть. Тогда создается таблица Attributes с тремя колонками:

  • Entity: идентификатор сущности
  • Attribute: название атрибута
  • Value: значение атрибута

Применяется, чтобы упростить проектирование

Последствия:

  • все данные лежат в 3-4 гигантских таблицах, которые все время джойнятся. Типы полей Attribute и Value будут, скорее всего, текстовые, а это значит что эффективность индексирования таких данных будет крайне мала. В результате наши джойны будут очень долгими.
  • через некоторое время EAV гордо переименовывается в ядро и обрастает витринами и представлениями с денормализованными данными в реляционном виде. Работает медленно и плохо. Любое изменение в схеме ведет кучу изменений в этих разрозненных представлениях. Чтобы упростить, приходится выкидывать ядро.

Как надо было:

  • не лениться и делать отдельные реляционные таблицы

4. ORM упрощает разработку

  • универсальный способ убить производительность любой базы

Как надо было:

  • использовать только в прототипах

5. Главное зло в PostgreSQL - autovacuum

Постоянно работает и всему мешает, в результате чего его выключают.

Последствия:

  • фрагментированная таблица на 100К строк занимает 100 ГБ

Как надо было:

  • не отключать и смотреть здесь как с ним жить

6. JOIN это зло - они медленные

Последствия:

Чтобы не использовать джойн, в контроллер вытягиваются 2 таблицы из базы, они джойнятся средствами ЯП. Затем, чтобы оптимизировать этот велосипедный джойн, добавляется выбор алгоритма - nested loop, hash или merge. В результате получается самодельная БД, только плохая.

Как надо было:

  • Надо было джойнить и не выпендриваться. Джойны на самом деле быстрые, реляционная база данных оптимизирована для работы с джойнами.

7. Давайте изобретем Slony

(Slony - система репликации, используемая в PostgreSQL)

Последствия:

  • велосипед всегда работает как-то не так, потому что репликация - это обработка распределенных транзакций, а это тяжело
  • велосипед скорее всего будет работать на уровне SQL, таблиц, триггеров и хранимых процедур. Это медленно и чревато конфликтами.

Как надо было:

  • использовать готовые проверенные решения. Если в них чего-то нет, значит тому есть причина. Возможно желаемый функционал просто невозможно реализовать с учетом всех сложностей репликации.
  • в готовых решениях используется репликация лога транзакций на низком уровне

8. У меня в тесте все работает

Разработчики используют EXPLAIN, но только на своей разработческой машине. А на продакшне данных в 1000 раз больше и все сразу тормозит.

Как надо было:

  • у разработчиков должен быть какой-то ограниченный доступ к продакшну. В идеале на чтение. Если нельзя, то, например, доступ к мониторингу базы, чтобы после деплоя видеть, сколько ресурсов потребляет каждый запрос. Либо же должны быть отлаженные процедуры работы с DBA, чтобы можно было попросить их прогнать какой-то запрос.

9. Be smart, as a java-developer

Разработчик хочет быстрее закачать данные в базу, поэтому делает это в 500 тредов. А на стороне БД есть только 10 воркеров (по числу ядер) и в результате 500 тредов, пытающиеся пролезть в 10 воркеров, начинают драться между собой на стороне приложения.

SQL-запрос через ко-рутины питона может быть в 10 раз медленнее, чем без них.

10. Приятные мелочи

Если запрос от веба возвращает миллион строк, то:

  • он никогда не будет работать достаточно быстро для веба
  • стоит подумать, а зачем он нужен? никто не будет читать миллион строк в браузере

20 счетчиков с count(*) на главной странице:

  • будут работать медленно
  • точные обытно не нужны
  • в постгресе можно написать процедуру, которая будет запрашивать данные от анализатора статистики планировщика. Они будут быстрые, но приблизительные.