SlideShare a Scribd company logo
"Мы два месяца долбались, а потом построили индекс" (c) Аксенов
Привет!
● Меня зовут Саша
● Я работаю главным инженером
● В компании Git in Sky
● Однажды я настроил один
MySQL-сервер
Какого цвета инсталлятор Oracle?
● Вы используете базы данных?
● Умеете читать и понимать
план запроса?
● Настраивали однажды
MySQL-сервер?
● Может, и не однажды?
● Может, и не MySQL?
Какова цель операции?
● Однажды ко мне обратился
один человек
● Он предоставил следующие
документы:
● http://slideshare.net/profyclub_ru/08-6
● Действовать надо было быстро
и осторожно
Кто мой заказчик?
● Конструктор сайтов, http://setup.ru
● Пользовательские файлы хранятся в базе
данных (PostgreSQL)
● Для больших файлов используется large
objects API
● Приложение на Perl, под Apache + mod_perl
● В 2012-м это работало хорошо, в 2014-м...
Перечень возникших сложностей
● Количество файлов: 6 млн => 207(85) млн
● Размер индексов: 2Gb => десятки Gb
● Скорость синхронизации: 100 f/s => ~30 f/s
● Объем базы данных на дисках: 6Tb на тот
момент
● ^ Сейчас уже 6.7Tb, и дальше будет только
больше
Анализ ситуации
● Гражданский специалист: “Файлы в БД?
АААА, куда я попал!”
● Советы взять какое-нибудь другое хранилище
я уже слышал, можно их не повторять :)
● Наш специалист: “Ничего не
ломаем, валим всех аккуратно,
отходим быстро”
Детальный анализ ситуации
● Бизнес-причины хранить файлы в СУБД:
● Нужны транзакции при публикации сайта
● Варианты:
● Менять хранилище и делать транзакции на
уровне приложения
● Найти транзакционное
хранилище (а это СУБД :) )
Объекты предметной области
● Таблица domains – имена доменов
● Таблица content – метаинформация о файле
(время последнего изменения и путь)
● Таблица stat – сами бинарные данные и их
sha-1 хэш для дедупликации
● Таблица deleted – признак того, что файл
удален
● Все четыре связаны между собой
Пользовательские сценарии
● Публикация и синхронизация файлов:
● Публикуем всегда на одну и ту же ноду
● Кастомный синхронизатор не очень быстро
обновляет все остальные ноды
● Отдача статического контента:
● Отдаем а) последнюю,
б) неудаленную версию
Что плохо?
● Отдача файлов работает не очень быстро
● Публикация и синхронизация – тоже
● Существующее железо справляется не очень
хорошо
● Одним словом, Hetzner!
Я бы даже сказал, полный Hetzner
● Было: RAID0 2*3Tb SATA, 16G RAM, 128G
SSD – для pg_temp и nginx, сортировка в
PostgreSQL и буферизация в nginx – быстро
● Стало: RAID10 4*4Tb SATA, 48G RAM без SSD
● SSD не дают, хотя место в корпусе еще есть –
Hetzner!
● Надо жить с этим
Но как?
● Как обычно:
● slow queries log, потом pgFouine или
pgBadger, раз в сутки – смотреть отчеты, в
них смотреть план запроса
Все еще проще
● Два самых популярных запроса при отдаче и
синхронизации - “найти неудаленный файл” и
“найти, что синхронизировать” - это запросы
ко view
● Они и тормозят, их и надо оптимизировать
Начнем с отдачи файлов
● Этот план запроса мне не нравится
● В нем слишком многабукв
Нам нужен новый план
● Материализовать view
● В PostgreSQL 9.2 нет materialized view
● Но в книге “Enterprise Rails” написано, как их
эмулировать с помощью триггеров
● Enterprise WHAT, sorry?
Шаверма своими руками
● “Поверх” нематериализованного view
делается таблица с такими же полями
● Она работает как кэш – записи в ней
заводятся по запросу
● Сначала ищем в ней, потом в исходном view,
если не нашлось в ней
● Записи инвалидируются триггерами на всех
таблицах-участниках исходного view
Как измерить результат?
● pgFouine и pgBadger не подходят – долго
ждать, много процессить, slow log
нерепрезентативен
● Расширение pg_stat_statements
● ^ Лучшее, что было со мной
● Позволяет смотреть статистику
в реальном времени
Как пользоваться?
● 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;
Что покажет?
Близки ли мы к цели?
● Хорошо: кэширующая таблица кэширует
● Плохо: примерно 30-40% запросов не
попадают в кэш
● Может быть, надо подождать?
● На третий день Зоркий Глаз заметил, что у
тюрьмы нет одной стены
Know your weapon
● “Посмотреть в таблице, потом во view”
● А что, если у нас 404, и файла нет вообще?
● Зачем ходить за такими файлами во view?
Стало ли лучше?
● Ночью – 15мс в среднем
● Днем – 40-50мс в среднем
● Обычно я работаю по ночам
● А результат нужно смотреть в середине дня
на пике нагрузки
● Что очень неудобно
Я люблю графики!
● Главная метрика – время отдачи контента
● Ее лучше измерять на эппсервере, а не в
базе?
● Zabbix
● Graphite/StatsD
● http://goo.gl/x6If1S
● ^ Ansible playbook для установки StatsD и
Graphite
Я не люблю Zabbix!
● Это плохо написанная система “все-в-одном”,
по качеству напоминающая китайскую
видеодвойку из 90-х
● К тому же, там плохие планы запросов
UNIX-way не всегда вреден
● Graphite/StatsD stack:
● Dashboard (сначала – стандартный от
Graphite),
● Веб-сервис отдачи графиков (на Django)
● Коллектор с RRD-like хранилищем (Carbon)
● Агрегатор/препроцессор с UDP-интерфейсом
(собственно, StatsD)
StatsD server
● Есть на Go, Node.JS, Python, Perl, C, Ruby, ...
● Сперва я взял Python:
● Потом опомнился и взял Perl
Уже должен быть результат?
● 40-50мс никак не хотят превращаться в 0-1
● Что делать?
● Построить более лучшие индексы
Сказано – сделано
● Для самого частого запроса построен индекс
на все три столбца, по которым идет поиск
● В этот момент все стало еще хуже! :)
● Размер индекса – 18 гигабайт
● Зоркий Глаз опять заметил, что у тюрьмы нет
одной стены
Чрезвычайные меры
● Одно из полей, по которым индекс – varchar
● Превращаем varchar в int:
● http://stackoverflow.com/a/9812029/601572
● Совсем забыл сказать: база данных уже
полна хранимых процедур и триггеров, кроме
того, я их совершенно не боюсь
● Просто не люблю
Что же было по ссылке?
● Я не помню, поэтому записал прямо сюда:
● create function h_int(text) returns int as $$
select ('x'||substr(md5($1),1,8))::bit(32)::int;
$$ language sql;
К чему приводит чтение*
● *плана запроса
● 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
Счастливы ли мы?
● Размер индекса: 18G => 8G
● Время запроса: 40-50мс => 20-25мс
● 90% всех запросов обслуживаются за 100мс
● В среднем запрос обслуживается
приложением за 50мс
Как это выглядит в Graphite
Как это выглядит в Zabbix
Часть вторая, момент истины
● При проверке существования файла я
получал id файла, если он есть, и решил этим
воспользоваться, чтобы ходить во view по PK
● Оказалось, я ошибся ранее, и мне
возвращался массив id – при оптимизации это
стало явным
● Я исправил ошибку и этой оптимизацией
добился ускорения еще в два раза
Что мешало
● PL/pgSQL – плохой, негодный язык
● Я так и не понял, как в нем сконструировать
программно множество из нуля строк,
поэтому возвращал, при необходимости,
такое множество, делая SQL-запрос в
специально заготовленную пустую таблицу с
нужным списком полей
Что еще удалось
● В качестве dashboard к Graphite я поставил
Grafana
● Систему отдачи файлов я переписал на
смешанную асинхронно/синхронную,
используя для внутренних нужд HTTP status
418 I'm a teapot
● “Асинхронная” не значит “быстрая”
(наоборот), но значит “экономичная”
Что было дальше
● Я пытался тюнить кастомный репликатор, но
быстро понял, что это невозможно – он
работает на пределе
● Я решил заменить его на какое-то общее
средство репликации и выбрал Bucardo
● Bucardo в тестовом режиме работало
отлично, но из 6+Tb базы среплицировало
1Tb
WTF?
● Know your weapon:
● Large objects – это просто еще один
key-value storage, по сути
● Репликация ими не занимается
● При этом в нашей базе мы никогда их не
перезаписываем после создания!
● Кроме того, у них уникальные номера
И вот тут мне карта пошла!
● Object storages:
● LeoFS
● OpenStack Swift
● Elliptics
● Riak CS
● Ceph Object Gateway
● Но это другая история, и она еще не
закончена
Выводы
● Иногда, прежде чем сказать “давайте
перепишем всё”, стоит попробовать
переписать не всё
● “Переписать всё” - тоже выход, надо только
уметь писать и знать, где взять оружие
Спасибо за внимание!
● Пожалуйста, ваши вопросы!
● С вами был Александр Чистяков,
● Главный инженер Git in Sky
● http://twitter.com/noatbaksap
● alex@gitinsky.com
● http://gitinsky.com,
http://meetup.com/DevOps-40

More Related Content

PDF
Benchmarking PostgreSQL in Linux and FreeBSD
Alex Chistyakov
 
PDF
My talk on Hadoop stack operations engineering at OSPCon
Alex Chistyakov
 
PDF
My talk on LeoFS, Highload++ 2014
Alex Chistyakov
 
PDF
My talk at Highload++ 2015
Alex Chistyakov
 
PPTX
Why we did not choose Hadoop
Serguei Gitinsky
 
PDF
On Docker
Alex Chistyakov
 
PDF
My talk on Graphite stack on 58it.ru
Alex Chistyakov
 
PDF
My talk on HBase ops engineering at TBD Jun 2016
Alex Chistyakov
 
Benchmarking PostgreSQL in Linux and FreeBSD
Alex Chistyakov
 
My talk on Hadoop stack operations engineering at OSPCon
Alex Chistyakov
 
My talk on LeoFS, Highload++ 2014
Alex Chistyakov
 
My talk at Highload++ 2015
Alex Chistyakov
 
Why we did not choose Hadoop
Serguei Gitinsky
 
On Docker
Alex Chistyakov
 
My talk on Graphite stack on 58it.ru
Alex Chistyakov
 
My talk on HBase ops engineering at TBD Jun 2016
Alex Chistyakov
 

What's hot (20)

PDF
Using Ansible
Alex Chistyakov
 
PDF
My talk on Salt and Ansible from DevConf 2014
Alex Chistyakov
 
ODP
My talk on Docker, Youcon 2015
Alex Chistyakov
 
PDF
My talk on DevOps engineer's adventures in the Windows world at UWDC 2017
Alex Chistyakov
 
PPTX
Спасение 6 миллионов файлов в условиях полного Хецнера
Daniel Podolsky
 
PDF
My talk on administering PostgreSQL
Alex Chistyakov
 
PDF
My talk on PgDay Russia 2014
Alex Chistyakov
 
PPTX
101 способ приготовления RabbitMQ и немного о pipeline архитектуре / Филонов ...
Ontico
 
PDF
libfpta — обгоняя SQLite и Tarantool / Леонид Юрьев (Positive Technologies)
Ontico
 
PDF
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Ontico
 
PDF
Олег Бартунов и Иван Панченко
CodeFest
 
PDF
Ansible in the enterprise
Alex Chistyakov
 
PDF
Эволюция php code coverage в Badoo. Доклад Ильи Агеева на LoveQA РИТ.
Badoo Development
 
PDF
Константин Осипов
CodeFest
 
PDF
Путь к Go на конкретном примере
Sergey Xek
 
PDF
RTB DSP на языке Go укрощение buzzwords / Даниил Подольский (Qmobi.Com)
Ontico
 
PDF
Performance engineering stories from #fdminicon Saransk
Alex Chistyakov
 
PDF
Эффективная отладка репликации MySQL / Света Смирнова (Percona)
Ontico
 
PDF
Профилирование кода на C/C++ в *nix системах
Aleksander Alekseev
 
PDF
Отладка производительности приложения на Erlang / Максим Лапшин (Erlyvideo)
Ontico
 
Using Ansible
Alex Chistyakov
 
My talk on Salt and Ansible from DevConf 2014
Alex Chistyakov
 
My talk on Docker, Youcon 2015
Alex Chistyakov
 
My talk on DevOps engineer's adventures in the Windows world at UWDC 2017
Alex Chistyakov
 
Спасение 6 миллионов файлов в условиях полного Хецнера
Daniel Podolsky
 
My talk on administering PostgreSQL
Alex Chistyakov
 
My talk on PgDay Russia 2014
Alex Chistyakov
 
101 способ приготовления RabbitMQ и немного о pipeline архитектуре / Филонов ...
Ontico
 
libfpta — обгоняя SQLite и Tarantool / Леонид Юрьев (Positive Technologies)
Ontico
 
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Ontico
 
Олег Бартунов и Иван Панченко
CodeFest
 
Ansible in the enterprise
Alex Chistyakov
 
Эволюция php code coverage в Badoo. Доклад Ильи Агеева на LoveQA РИТ.
Badoo Development
 
Константин Осипов
CodeFest
 
Путь к Go на конкретном примере
Sergey Xek
 
RTB DSP на языке Go укрощение buzzwords / Даниил Подольский (Qmobi.Com)
Ontico
 
Performance engineering stories from #fdminicon Saransk
Alex Chistyakov
 
Эффективная отладка репликации MySQL / Света Смирнова (Percona)
Ontico
 
Профилирование кода на C/C++ в *nix системах
Aleksander Alekseev
 
Отладка производительности приложения на Erlang / Максим Лапшин (Erlyvideo)
Ontico
 
Ad

Viewers also liked (16)

PDF
My talk from PgConf.Russia 2016
Alex Chistyakov
 
PDF
My talk on Piter Py 2016
Alex Chistyakov
 
PDF
My talk on monitoring systems at RootConf 2016
Alex Chistyakov
 
PDF
My talk at Linux Piter 2015
Alex Chistyakov
 
ODP
My talk on using LVM thin provisioning from SPbLUG/DevOps-40 meetup 25.06.14
Alex Chistyakov
 
PPTX
Управление рисками при эксплуатации ИТ-инфраструктуры
Serguei Gitinsky
 
PDF
My talk on Docker from Moscow Django Meetup #25
Alex Chistyakov
 
PDF
NoSQL — неспроста ли это "ЖЖЖ"?
Daniel Podolsky
 
PDF
My talk at Linux Piter 2016
Alex Chistyakov
 
PDF
Мой modern Perl (весенняя встреча Piter United)
Alex Chistyakov
 
PDF
Диалог с воображаемым слушателем, а также поток сознания, вне контекста НЕ ИН...
Alex Chistyakov
 
PDF
Выступление в DataArt на тему "Кто такие DevOps?"
Alex Chistyakov
 
PDF
Презентация про DTrace на ADDconf в Минске
Alex Chistyakov
 
PDF
Optimization of a big PostgreSQL database
Alex Chistyakov
 
ODP
My talk on LeoFS, HappyDev 2014
Alex Chistyakov
 
PDF
DevOps-40 meetup #7, Project FiFo
Alex Chistyakov
 
My talk from PgConf.Russia 2016
Alex Chistyakov
 
My talk on Piter Py 2016
Alex Chistyakov
 
My talk on monitoring systems at RootConf 2016
Alex Chistyakov
 
My talk at Linux Piter 2015
Alex Chistyakov
 
My talk on using LVM thin provisioning from SPbLUG/DevOps-40 meetup 25.06.14
Alex Chistyakov
 
Управление рисками при эксплуатации ИТ-инфраструктуры
Serguei Gitinsky
 
My talk on Docker from Moscow Django Meetup #25
Alex Chistyakov
 
NoSQL — неспроста ли это "ЖЖЖ"?
Daniel Podolsky
 
My talk at Linux Piter 2016
Alex Chistyakov
 
Мой modern Perl (весенняя встреча Piter United)
Alex Chistyakov
 
Диалог с воображаемым слушателем, а также поток сознания, вне контекста НЕ ИН...
Alex Chistyakov
 
Выступление в DataArt на тему "Кто такие DevOps?"
Alex Chistyakov
 
Презентация про DTrace на ADDconf в Минске
Alex Chistyakov
 
Optimization of a big PostgreSQL database
Alex Chistyakov
 
My talk on LeoFS, HappyDev 2014
Alex Chistyakov
 
DevOps-40 meetup #7, Project FiFo
Alex Chistyakov
 
Ad

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

PDF
Олег Царев, Кирилл Коринский Сравнительный анализ хранилищ данных
Siel01
 
PDF
#RuPostges в Yandex, эпизод 3. Что же нового в PostgreSQL 9.6
Nikolay Samokhvalov
 
PDF
2014.12.06 03 Александр Чистяков — Устройство object storage на примере LeoFS
HappyDev
 
PDF
Как считать и анализировать сотни гигабит трафика в секунду, Станислав Николо...
Ontico
 
PDF
20111002 information retrieval raskovalov_lecture3
Computer Science Club
 
PPSX
CodeFest 2014. Круглов И. — События на каждом углу. Путешествие в мир системн...
CodeFest
 
PDF
High Load 2009 Imdg Presentation
HighLoad2009
 
PDF
Я. Садовская "Управление конфигурациями и тестовой средой", DUMP-2014
it-people
 
PDF
HBase on Dev{Highload}
Alex Chistyakov
 
PDF
16 декабря, DEV {highload} - конференция о Highload веб-разработке, "Эксплуат...
IT-Portfolio
 
PDF
специализированные http-демона (Сергей Боченков, Александр Панков)
Ontico
 
PDF
Павел Пушкарев "Отказоустойчивость сервисов"
Yandex
 
PDF
Последние новости постгреса с PGCon / О.Бартунов, А.Коротков, Ф.Сигаев (Postg...
Ontico
 
PDF
Колёса: Раньше и сейчас. Как поменять архитектуру высоконагруженного проекта
ITCrowd Almaty
 
PDF
Там, где Rails не справляются
Max Lapshin
 
PDF
Оптимизация программ для современных процессоров и Linux, Александр Крижановс...
Ontico
 
PDF
Romanova techforum bash
kuchinskaya
 
PDF
PUG #9 at OWOX: Поиск узких мест в приложении на PHP
Анна Магас
 
PDF
CodeFest 2014. Каплуновский Б. — Использование асинхронного I/O для снижения ...
CodeFest
 
PDF
20120226 information retrieval raskovalov_lecture03-04
Computer Science Club
 
Олег Царев, Кирилл Коринский Сравнительный анализ хранилищ данных
Siel01
 
#RuPostges в Yandex, эпизод 3. Что же нового в PostgreSQL 9.6
Nikolay Samokhvalov
 
2014.12.06 03 Александр Чистяков — Устройство object storage на примере LeoFS
HappyDev
 
Как считать и анализировать сотни гигабит трафика в секунду, Станислав Николо...
Ontico
 
20111002 information retrieval raskovalov_lecture3
Computer Science Club
 
CodeFest 2014. Круглов И. — События на каждом углу. Путешествие в мир системн...
CodeFest
 
High Load 2009 Imdg Presentation
HighLoad2009
 
Я. Садовская "Управление конфигурациями и тестовой средой", DUMP-2014
it-people
 
HBase on Dev{Highload}
Alex Chistyakov
 
16 декабря, DEV {highload} - конференция о Highload веб-разработке, "Эксплуат...
IT-Portfolio
 
специализированные http-демона (Сергей Боченков, Александр Панков)
Ontico
 
Павел Пушкарев "Отказоустойчивость сервисов"
Yandex
 
Последние новости постгреса с PGCon / О.Бартунов, А.Коротков, Ф.Сигаев (Postg...
Ontico
 
Колёса: Раньше и сейчас. Как поменять архитектуру высоконагруженного проекта
ITCrowd Almaty
 
Там, где Rails не справляются
Max Lapshin
 
Оптимизация программ для современных процессоров и Linux, Александр Крижановс...
Ontico
 
Romanova techforum bash
kuchinskaya
 
PUG #9 at OWOX: Поиск узких мест в приложении на PHP
Анна Магас
 
CodeFest 2014. Каплуновский Б. — Использование асинхронного I/O для снижения ...
CodeFest
 
20120226 information retrieval raskovalov_lecture03-04
Computer Science Club
 

More from Alex Chistyakov (20)

PDF
My slides from DevOpsDays 2019
Alex Chistyakov
 
PDF
My slides from BMM №3 May 2019
Alex Chistyakov
 
PDF
My slides from DevOps-40 meetup Jun 2019
Alex Chistyakov
 
PDF
My slides from SECR'2018
Alex Chistyakov
 
PDF
My slides from the first SPb SRE community meetup at DataArt
Alex Chistyakov
 
PDF
My slides from CC'2019
Alex Chistyakov
 
PDF
My slides from BMM №4 Nov 2019
Alex Chistyakov
 
PDF
My slides from DevOps-40 meetup Oct 2019
Alex Chistyakov
 
PDF
My slides from DevOps-40 meetup Dec 2019
Alex Chistyakov
 
PDF
Configuration management and Kubernetes
Alex Chistyakov
 
PDF
Ansible and other stuff
Alex Chistyakov
 
PDF
Python performance engineering in 2017
Alex Chistyakov
 
PDF
My talk at SPb SQA sub-meetup of ITGM
Alex Chistyakov
 
PDF
My talk at SECR 2017
Alex Chistyakov
 
PDF
On scaling teams
Alex Chistyakov
 
PDF
MariaDB workshop
Alex Chistyakov
 
PDF
Docker for JS people
Alex Chistyakov
 
PDF
My talk on GitHub open data at ITGM #10
Alex Chistyakov
 
PDF
My talk on DevOps :) at Stachka 2017
Alex Chistyakov
 
PDF
My talk on programming languages at SPbLUG Mar 2017
Alex Chistyakov
 
My slides from DevOpsDays 2019
Alex Chistyakov
 
My slides from BMM №3 May 2019
Alex Chistyakov
 
My slides from DevOps-40 meetup Jun 2019
Alex Chistyakov
 
My slides from SECR'2018
Alex Chistyakov
 
My slides from the first SPb SRE community meetup at DataArt
Alex Chistyakov
 
My slides from CC'2019
Alex Chistyakov
 
My slides from BMM №4 Nov 2019
Alex Chistyakov
 
My slides from DevOps-40 meetup Oct 2019
Alex Chistyakov
 
My slides from DevOps-40 meetup Dec 2019
Alex Chistyakov
 
Configuration management and Kubernetes
Alex Chistyakov
 
Ansible and other stuff
Alex Chistyakov
 
Python performance engineering in 2017
Alex Chistyakov
 
My talk at SPb SQA sub-meetup of ITGM
Alex Chistyakov
 
My talk at SECR 2017
Alex Chistyakov
 
On scaling teams
Alex Chistyakov
 
MariaDB workshop
Alex Chistyakov
 
Docker for JS people
Alex Chistyakov
 
My talk on GitHub open data at ITGM #10
Alex Chistyakov
 
My talk on DevOps :) at Stachka 2017
Alex Chistyakov
 
My talk on programming languages at SPbLUG Mar 2017
Alex Chistyakov
 

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

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