"Отказоустойчивый standby PostgreSQL (HAProxy + PgBouncer)" Виктор...
Transcript of "Отказоустойчивый standby PostgreSQL (HAProxy + PgBouncer)" Виктор...
Strictly ConfidentialStrictly Confidential
Отказоустойчивый standby PostgreSQL: HAProxy + PgBouncer
Victor Yagofarov [email protected]
Strictly ConfidentialStrictly Confidential 2
Обо мне
Виктор Ягофаров, Avito.ru
• Администратор Баз Данных• 8 лет в качестве Системного Администратора FreeBSD и Linux
• 2 года занимался развитием PostgreSQL-инфраструктуры в Rambler
• Тем же занят в Avito :)
Strictly ConfidentialStrictly Confidential 3
О чем доклад?
• Как мы резервируем postgres в Avito• Почему именно так, другие варианты• Протокол pgsql и haproxy + pgbouncer• Подводные камни• Tips and tricks
Strictly ConfidentialStrictly Confidential 4
PgBouncer
• Мультиплексор: "многие к одному"• transaction pooling для приложений (более экономный)
• session pooling для разработчиков (тяжелее сломать)
• Возможность совмещать разные виды пулов в 1.6• pg_hba.conf -style конфиг, начиная с 1.7
Strictly ConfidentialStrictly Confidential 5
HAProxy
• Быстрый tcp/http load balancer• Гибкость настройки способов балансировки для разных типов нагрузки
• Тонкий тюнинг failover• Custom http health checks: логика проверок ограничена лишь фантазией
• Настраиваемое поведение при переключении на backup и обратно
• Возможность с помощью health checks на множестве нод агрегировать статистику и видеть картину в целом
Strictly ConfidentialStrictly Confidential 6
app01
PgBouncer
application
HAProxy
Standby01
PgBouncer
PostgreSQ
Standby02
PgBouncer
PostgreSQ
Standby03
PgBouncer
PostgreSQ
app101
PgBouncer
application
HAProxy
…
Active Backup Failed
Общая схема
Strictly ConfidentialStrictly Confidential 7
Конфиг HAProxy и PgBouncerlisten pgsql-db_main_s
bind 127.0.0.1:16002timeout client 20mtimeout connect 1stimeout server 20mbalance roundrobinoption log-health-checksoption tcpkaoption tcplogoption httpchk GET /db_main_s?username=app_ro&port=6432 # настройки чекераhttp-check send-state
server host-sb01 host-sb01:6432 check addr 127.0.0.1 port 5777 inter 6s fall 5 rise 3server host-sb02 host-sb02:6432 check addr 127.0.0.1 port 5777 inter 6s fall 5 rise 3
backupserver host-sb03 host-sb03:6432 check addr 127.0.0.1 port 5777 inter 6s fall 5 rise 3
backup[databases]db_main_s = host=127.0.0.1 port=16002 pool_size=10
Strictly ConfidentialStrictly Confidential 8
Health checking
cat /etc/xinetd.d/pgcheck
service pgcheck{ disable = no type = UNLISTED flags = REUSE socket_type = stream port = 5777 wait = no user = nobody server = /usr/local/bin/pgcheck log_on_failure += USERID only_from = 127.0.0.1/32 per_source = UNLIMITED}
Strictly ConfidentialStrictly Confidential 9
pgcheck - эмулятор web-сервиса
#!/usr/bin/env perl…$| = 1; # отключаем буферизацию
# Set whole script timeout to 5 seconds via alarm$SIG{ ALRM } = sub { http 504 => "Timeout checking database health";};alarm 5; ### время таймаута всего скрипта…
my $dbh = DBI->connect("dbi:Pg:dbname=$db;host=$host;port=$port", "$username", '', { PrintError => 0, RaiseError => 0, pg_server_prepare => 0 } ) or # отключаем prepare чтобы избежать prepare на сервере http 502 => "Error occured connecting database ($DBI::errstr)";…
Strictly ConfidentialStrictly Confidential 10
pgcheck - эмулятор web-сервиса
… продолжение скрипта
# do not use database if check_ha() returns 'false'
my $sth = $dbh->prepare("select public.check_ha()");my $rv = $sth->execute or http 503 => "Error occured while 'select check_ha()' on '$db' at '$host' ($DBI::errstr)";my @row = $sth->fetchrow_array;if ( $row[0] == 0 ) { http 503 => "Error occured while 'select check_ha()' on '$db' at '$host': service disabled manually";}…# If everything is ok, return 200http 200 => "Database '$db' at '$host' is alive";
Strictly ConfidentialStrictly Confidential 11
check_ha()
db_main=# \df+ check_ha
use Sys::Hostname;my $h = Sys::Hostname::hostname;
if ($h eq 'unknown-host') { return 0;} elsif ($h eq 'db-sql02') { # standby return 1;} elsif ($h eq 'db-sql03') { # master return 0;} elsif ($h eq 'db-sql05') { # standby return 1;} else { return 0;}
Strictly ConfidentialStrictly Confidential 12
Поведение при разрывах tcp
select * from tbl where id = 1 select * from tbl where id = 1 # разрыв и переключение на другой сервер в haproxy .. select * from tbl where id = 1
Вне транзакции выполним, подключившись к серверу через локальный pgbouncer:
Последний запрос выполнится без ошибок!Но в случае транзакции, последний select получит ошибку
select 'text' || pg_sleep(60) ;
Клиент получит ошибку "ERROR: server conn crashed"
Долгий запрос:
Strictly ConfidentialStrictly Confidential 13
Поведение при разрывах tcp
• Не транзакционные, быстрые запросы в одной сессии (до 1 мс) могут не заметить разрыва tcp (tcpkill/tcpdrop), следовательно - они более "stateless" и имеют меньше шансов завершиться с ошибкой. Это же касается быстрых транзакций.
• Локальный pgbouncer пересоздает подключение незаметно для клиента, haproxy повышает HA.
• Но переключение в haproxy происходит не мгновенно
• HAProxy по-умолчанию не разрывает существующие сессии (см. on-marked-down shutdown-sessions, on-marked-up shutdown-backup-sessions)
Strictly ConfidentialStrictly Confidential 14
Особенности pgbouncer
• Проблема осиротевшего процесса в PostgreSQL• Pgbouncer cannot connect to server
Strictly ConfidentialStrictly Confidential 15
Особенности haproxy
• haproxy "релоадит" конфиг через порождение нового процесса и передачу в него новых коннектов. Старый процесс может ждать бесконечно старые tcp states
• в http статистике haproxy не всегда показывает переменную с описанием ошибки чекера
Strictly ConfidentialStrictly Confidential 16
Особенности psql
• Особенности флагов psql (-1, -f, -c)• set local statement_timeout и psql -c
psql -Upostgres -c "set local statement_timeout = '3 s'; select pg_sleep(5)"
psql -1 -Upostgres -h avi-sql26 -p 6432 -f- << 'EOF'set local statement_timeout = '3 s';select pg_sleep(5);EOF
Strictly ConfidentialStrictly Confidential 17
Особенности psql
• psql -v ON_ERROR_STOP=1 : обязателен при -f• Что еще не работает в single-transaction (-1):
create index concurrently ;create database;vacuum;- - что-то еще? вполне!
• \timing включает в себя задержки сети• set search_path в transaction pooling - так делать опасно. Используйте set local в transaction pooling mode
Strictly ConfidentialStrictly Confidential
Victor Yagofarov [email protected]
Всем спасибо!
Вопросы?