Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.
Привет!
● Меня зовут Саша
● Я работаю главным инженером
● В компании Git in Sky
● Однажды я настроил один
MySQL-сервер
Какого цвета инсталлятор Oracle?
● Вы используете базы данных?
● Умеете читать и понимать
план запроса?
● Настраивали одна...
Какова цель операции?
● Однажды ко мне обратился
один человек
● Он предоставил следующие
документы:
● http://slideshare.ne...
Кто мой заказчик?
● Конструктор сайтов, http://setup.ru
● Пользовательские файлы хранятся в базе
данных (PostgreSQL)
● Для...
Перечень возникших сложностей
● Количество файлов: 6 млн => 207(85) млн
● Размер индексов: 2Gb => десятки Gb
● Скорость си...
Анализ ситуации
● Гражданский специалист: “Файлы в БД?
АААА, куда я попал!”
● Советы взять какое-нибудь другое хранилище
я...
Детальный анализ ситуации
● Бизнес-причины хранить файлы в СУБД:
● Нужны транзакции при публикации сайта
● Варианты:
● Мен...
Объекты предметной области
● Таблица domains – имена доменов
● Таблица content – метаинформация о файле
(время последнего ...
Пользовательские сценарии
● Публикация и синхронизация файлов:
● Публикуем всегда на одну и ту же ноду
● Кастомный синхрон...
Что плохо?
● Отдача файлов работает не очень быстро
● Публикация и синхронизация – тоже
● Существующее железо справляется ...
Я бы даже сказал, полный Hetzner
● Было: RAID0 2*3Tb SATA, 16G RAM, 128G
SSD – для pg_temp и nginx, сортировка в
PostgreSQ...
Но как?
● Как обычно:
● slow queries log, потом pgFouine или
pgBadger, раз в сутки – смотреть отчеты, в
них смотреть план ...
Все еще проще
● Два самых популярных запроса при отдаче и
синхронизации - “найти неудаленный файл” и
“найти, что синхрониз...
Начнем с отдачи файлов
● Этот план запроса мне не нравится
● В нем слишком многабукв
Нам нужен новый план
● Материализовать view
● В PostgreSQL 9.2 нет materialized view
● Но в книге “Enterprise Rails” напис...
Шаверма своими руками
● “Поверх” нематериализованного view
делается таблица с такими же полями
● Она работает как кэш – за...
Как измерить результат?
● pgFouine и pgBadger не подходят – долго
ждать, много процессить, slow log
нерепрезентативен
● Ра...
Как пользоваться?
● SELECT
(total_time / 1000 / 60) as total_minutes,
(total_time/calls) as average_time,
calls, query
FRO...
Что покажет?
Близки ли мы к цели?
● Хорошо: кэширующая таблица кэширует
● Плохо: примерно 30-40% запросов не
попадают в кэш
● Может быт...
Know your weapon
● “Посмотреть в таблице, потом во view”
● А что, если у нас 404, и файла нет вообще?
● Зачем ходить за та...
Стало ли лучше?
● Ночью – 15мс в среднем
● Днем – 40-50мс в среднем
● Обычно я работаю по ночам
● А результат нужно смотре...
Я люблю графики!
● Главная метрика – время отдачи контента
● Ее лучше измерять на эппсервере, а не в
базе?
● Zabbix
● Grap...
Я не люблю Zabbix!
● Это плохо написанная система “все-в-одном”,
по качеству напоминающая китайскую
видеодвойку из 90-х
● ...
UNIX-way не всегда вреден
● Graphite/StatsD stack:
● Dashboard (сначала – стандартный от
Graphite),
● Веб-сервис отдачи гр...
StatsD server
● Есть на Go, Node.JS, Python, Perl, C, Ruby, ...
● Сперва я взял Python:
● Потом опомнился и взял Perl
Уже должен быть результат?
● 40-50мс никак не хотят превращаться в 0-1
● Что делать?
● Построить более лучшие индексы
Сказано – сделано
● Для самого частого запроса построен индекс
на все три столбца, по которым идет поиск
● В этот момент в...
Чрезвычайные меры
● Одно из полей, по которым индекс – varchar
● Превращаем varchar в int:
● http://stackoverflow.com/a/98...
Что же было по ссылке?
● Я не помню, поэтому записал прямо сюда:
● create function h_int(text) returns int as $$
select ('...
К чему приводит чтение*
● *плана запроса
● SET enable_bitmapscan=false; <= nested loops
SELECT something
FROM stat s JOIN ...
Счастливы ли мы?
● Размер индекса: 18G => 8G
● Время запроса: 40-50мс => 20-25мс
● 90% всех запросов обслуживаются за 100м...
Как это выглядит в Graphite
Как это выглядит в Zabbix
Часть вторая, момент истины
● При проверке существования файла я
получал id файла, если он есть, и решил этим
воспользоват...
Что мешало
● PL/pgSQL – плохой, негодный язык
● Я так и не понял, как в нем сконструировать
программно множество из нуля с...
Что еще удалось
● В качестве dashboard к Graphite я поставил
Grafana
● Систему отдачи файлов я переписал на
смешанную асин...
Что было дальше
● Я пытался тюнить кастомный репликатор, но
быстро понял, что это невозможно – он
работает на пределе
● Я ...
WTF?
● Know your weapon:
● Large objects – это просто еще один
key-value storage, по сути
● Репликация ими не занимается
●...
И вот тут мне карта пошла!
● Object storages:
● LeoFS
● OpenStack Swift
● Elliptics
● Riak CS
● Ceph Object Gateway
● Но э...
Выводы
● Иногда, прежде чем сказать “давайте
перепишем всё”, стоит попробовать
переписать не всё
● “Переписать всё” - тоже...
Спасибо за внимание!
● Пожалуйста, ваши вопросы!
● С вами был Александр Чистяков,
● Главный инженер Git in Sky
● http://tw...
"Мы два месяца долбались, а потом построили индекс" (c) Аксенов
Prochain SlideShare
Chargement dans…5
×

"Мы два месяца долбались, а потом построили индекс" (c) Аксенов

3 066 vues

Publié le

Мой доклад про оптимизацию PostgreSQL хранилища с DevConf 2014

Publié dans : Technologie
  • Soyez le premier à commenter

"Мы два месяца долбались, а потом построили индекс" (c) Аксенов

  1. 1. Привет! ● Меня зовут Саша ● Я работаю главным инженером ● В компании Git in Sky ● Однажды я настроил один MySQL-сервер
  2. 2. Какого цвета инсталлятор Oracle? ● Вы используете базы данных? ● Умеете читать и понимать план запроса? ● Настраивали однажды MySQL-сервер? ● Может, и не однажды? ● Может, и не MySQL?
  3. 3. Какова цель операции? ● Однажды ко мне обратился один человек ● Он предоставил следующие документы: ● http://slideshare.net/profyclub_ru/08-6 ● Действовать надо было быстро и осторожно
  4. 4. Кто мой заказчик? ● Конструктор сайтов, http://setup.ru ● Пользовательские файлы хранятся в базе данных (PostgreSQL) ● Для больших файлов используется large objects API ● Приложение на Perl, под Apache + mod_perl ● В 2012-м это работало хорошо, в 2014-м...
  5. 5. Перечень возникших сложностей ● Количество файлов: 6 млн => 207(85) млн ● Размер индексов: 2Gb => десятки Gb ● Скорость синхронизации: 100 f/s => ~30 f/s ● Объем базы данных на дисках: 6Tb на тот момент ● ^ Сейчас уже 6.7Tb, и дальше будет только больше
  6. 6. Анализ ситуации ● Гражданский специалист: “Файлы в БД? АААА, куда я попал!” ● Советы взять какое-нибудь другое хранилище я уже слышал, можно их не повторять :) ● Наш специалист: “Ничего не ломаем, валим всех аккуратно, отходим быстро”
  7. 7. Детальный анализ ситуации ● Бизнес-причины хранить файлы в СУБД: ● Нужны транзакции при публикации сайта ● Варианты: ● Менять хранилище и делать транзакции на уровне приложения ● Найти транзакционное хранилище (а это СУБД :) )
  8. 8. Объекты предметной области ● Таблица domains – имена доменов ● Таблица content – метаинформация о файле (время последнего изменения и путь) ● Таблица stat – сами бинарные данные и их sha-1 хэш для дедупликации ● Таблица deleted – признак того, что файл удален ● Все четыре связаны между собой
  9. 9. Пользовательские сценарии ● Публикация и синхронизация файлов: ● Публикуем всегда на одну и ту же ноду ● Кастомный синхронизатор не очень быстро обновляет все остальные ноды ● Отдача статического контента: ● Отдаем а) последнюю, б) неудаленную версию
  10. 10. Что плохо? ● Отдача файлов работает не очень быстро ● Публикация и синхронизация – тоже ● Существующее железо справляется не очень хорошо ● Одним словом, Hetzner!
  11. 11. Я бы даже сказал, полный Hetzner ● Было: RAID0 2*3Tb SATA, 16G RAM, 128G SSD – для pg_temp и nginx, сортировка в PostgreSQL и буферизация в nginx – быстро ● Стало: RAID10 4*4Tb SATA, 48G RAM без SSD ● SSD не дают, хотя место в корпусе еще есть – Hetzner! ● Надо жить с этим
  12. 12. Но как? ● Как обычно: ● slow queries log, потом pgFouine или pgBadger, раз в сутки – смотреть отчеты, в них смотреть план запроса
  13. 13. Все еще проще ● Два самых популярных запроса при отдаче и синхронизации - “найти неудаленный файл” и “найти, что синхронизировать” - это запросы ко view ● Они и тормозят, их и надо оптимизировать
  14. 14. Начнем с отдачи файлов ● Этот план запроса мне не нравится ● В нем слишком многабукв
  15. 15. Нам нужен новый план ● Материализовать view ● В PostgreSQL 9.2 нет materialized view ● Но в книге “Enterprise Rails” написано, как их эмулировать с помощью триггеров ● Enterprise WHAT, sorry?
  16. 16. Шаверма своими руками ● “Поверх” нематериализованного view делается таблица с такими же полями ● Она работает как кэш – записи в ней заводятся по запросу ● Сначала ищем в ней, потом в исходном view, если не нашлось в ней ● Записи инвалидируются триггерами на всех таблицах-участниках исходного view
  17. 17. Как измерить результат? ● pgFouine и pgBadger не подходят – долго ждать, много процессить, slow log нерепрезентативен ● Расширение pg_stat_statements ● ^ Лучшее, что было со мной ● Позволяет смотреть статистику в реальном времени
  18. 18. Как пользоваться? ● SELECT (total_time / 1000 / 60) as total_minutes, (total_time/calls) as average_time, calls, query FROM pg_stat_statements ORDER BY total_minutes/average_time desc;
  19. 19. Что покажет?
  20. 20. Близки ли мы к цели? ● Хорошо: кэширующая таблица кэширует ● Плохо: примерно 30-40% запросов не попадают в кэш ● Может быть, надо подождать? ● На третий день Зоркий Глаз заметил, что у тюрьмы нет одной стены
  21. 21. Know your weapon ● “Посмотреть в таблице, потом во view” ● А что, если у нас 404, и файла нет вообще? ● Зачем ходить за такими файлами во view?
  22. 22. Стало ли лучше? ● Ночью – 15мс в среднем ● Днем – 40-50мс в среднем ● Обычно я работаю по ночам ● А результат нужно смотреть в середине дня на пике нагрузки ● Что очень неудобно
  23. 23. Я люблю графики! ● Главная метрика – время отдачи контента ● Ее лучше измерять на эппсервере, а не в базе? ● Zabbix ● Graphite/StatsD ● http://goo.gl/x6If1S ● ^ Ansible playbook для установки StatsD и Graphite
  24. 24. Я не люблю Zabbix! ● Это плохо написанная система “все-в-одном”, по качеству напоминающая китайскую видеодвойку из 90-х ● К тому же, там плохие планы запросов
  25. 25. UNIX-way не всегда вреден ● Graphite/StatsD stack: ● Dashboard (сначала – стандартный от Graphite), ● Веб-сервис отдачи графиков (на Django) ● Коллектор с RRD-like хранилищем (Carbon) ● Агрегатор/препроцессор с UDP-интерфейсом (собственно, StatsD)
  26. 26. StatsD server ● Есть на Go, Node.JS, Python, Perl, C, Ruby, ... ● Сперва я взял Python: ● Потом опомнился и взял Perl
  27. 27. Уже должен быть результат? ● 40-50мс никак не хотят превращаться в 0-1 ● Что делать? ● Построить более лучшие индексы
  28. 28. Сказано – сделано ● Для самого частого запроса построен индекс на все три столбца, по которым идет поиск ● В этот момент все стало еще хуже! :) ● Размер индекса – 18 гигабайт ● Зоркий Глаз опять заметил, что у тюрьмы нет одной стены
  29. 29. Чрезвычайные меры ● Одно из полей, по которым индекс – varchar ● Превращаем varchar в int: ● http://stackoverflow.com/a/9812029/601572 ● Совсем забыл сказать: база данных уже полна хранимых процедур и триггеров, кроме того, я их совершенно не боюсь ● Просто не люблю
  30. 30. Что же было по ссылке? ● Я не помню, поэтому записал прямо сюда: ● create function h_int(text) returns int as $$ select ('x'||substr(md5($1),1,8))::bit(32)::int; $$ language sql;
  31. 31. К чему приводит чтение* ● *плана запроса ● SET enable_bitmapscan=false; <= nested loops SELECT something FROM stat s JOIN domains d ON d.id = s.domain JOIN content c ON c.id = s.content LEFT JOIN deleted e ON e.id = s.id WHERE d.name = domname AND h_int(s.name) = h_int(filename) <= новый индекс AND s.name = filename AND date_part('epoch'::text, s.ptime) = filerev
  32. 32. Счастливы ли мы? ● Размер индекса: 18G => 8G ● Время запроса: 40-50мс => 20-25мс ● 90% всех запросов обслуживаются за 100мс ● В среднем запрос обслуживается приложением за 50мс
  33. 33. Как это выглядит в Graphite
  34. 34. Как это выглядит в Zabbix
  35. 35. Часть вторая, момент истины ● При проверке существования файла я получал id файла, если он есть, и решил этим воспользоваться, чтобы ходить во view по PK ● Оказалось, я ошибся ранее, и мне возвращался массив id – при оптимизации это стало явным ● Я исправил ошибку и этой оптимизацией добился ускорения еще в два раза
  36. 36. Что мешало ● PL/pgSQL – плохой, негодный язык ● Я так и не понял, как в нем сконструировать программно множество из нуля строк, поэтому возвращал, при необходимости, такое множество, делая SQL-запрос в специально заготовленную пустую таблицу с нужным списком полей
  37. 37. Что еще удалось ● В качестве dashboard к Graphite я поставил Grafana ● Систему отдачи файлов я переписал на смешанную асинхронно/синхронную, используя для внутренних нужд HTTP status 418 I'm a teapot ● “Асинхронная” не значит “быстрая” (наоборот), но значит “экономичная”
  38. 38. Что было дальше ● Я пытался тюнить кастомный репликатор, но быстро понял, что это невозможно – он работает на пределе ● Я решил заменить его на какое-то общее средство репликации и выбрал Bucardo ● Bucardo в тестовом режиме работало отлично, но из 6+Tb базы среплицировало 1Tb
  39. 39. WTF? ● Know your weapon: ● Large objects – это просто еще один key-value storage, по сути ● Репликация ими не занимается ● При этом в нашей базе мы никогда их не перезаписываем после создания! ● Кроме того, у них уникальные номера
  40. 40. И вот тут мне карта пошла! ● Object storages: ● LeoFS ● OpenStack Swift ● Elliptics ● Riak CS ● Ceph Object Gateway ● Но это другая история, и она еще не закончена
  41. 41. Выводы ● Иногда, прежде чем сказать “давайте перепишем всё”, стоит попробовать переписать не всё ● “Переписать всё” - тоже выход, надо только уметь писать и знать, где взять оружие
  42. 42. Спасибо за внимание! ● Пожалуйста, ваши вопросы! ● С вами был Александр Чистяков, ● Главный инженер Git in Sky ● http://twitter.com/noatbaksap ● alex@gitinsky.com ● http://gitinsky.com, http://meetup.com/DevOps-40

×