В двух словах — это размер буфера, используемого master-сервисом Redis для хранения данных, которые изменились в процессе синхронизации базы данных из памяти Redis-master и одного из его слейвов.
С такими настройками соединение будет разорвано немедленно при достижении <hard limit> — 256 МБ, либо если <soft limit> — 64 МБ — превышен дольше лимита из <soft seconds> — 60 секунд.
Нас интересует slave 268435456 67108864 60.
На Мастере увеличиваем лимит буфера до 512 метров:
10.0.3.105:6389> config set client-output-buffer-limit "slave 4194990176 4194990176 0"
OK
10.0.3.105:6389> config get client-output-buffer-limit
Мне пришлось увеличивать буфер с 512 мегабайт до 1 гига, потом 2, 4 ,8 — и только на 10 GB синхронизация прошла успешно, при том, что база в памяти была чуть меньше 5 гигабайт:
1911:M 17 Feb 20:30:21.308 * Slave 10.0.3.71:6389 asks for synchronization
1911:M 17 Feb 20:30:21.308 * Full resync requested by slave 10.0.3.71:6389
1911:M 17 Feb 20:30:21.308 * Starting BGSAVE for SYNC with target: disk
1911:M 17 Feb 20:30:21.389 * Background saving started by pid 8166
1911:M 17 Feb 20:30:21.851 * Slave 10.0.3.92:6389 asks for synchronization
1911:M 17 Feb 20:30:21.851 * Full resync requested by slave 10.0.3.92:6389
1911:M 17 Feb 20:30:21.851 * Waiting for end of BGSAVE for SYNC
8166:C 17 Feb 20:30:55.069 * DB saved on disk
8166:C 17 Feb 20:30:55.148 * RDB: 851 MB of memory used by copy-on-write
1911:M 17 Feb 20:30:55.365 * Background saving terminated with success
1911:M 17 Feb 20:31:05.160 * Synchronization with slave 10.0.3.92:6389 succeeded
1911:M 17 Feb 20:31:05.223 * Synchronization with slave 10.0.3.71:6389 succeeded
И в логах слейва:
11347:S 17 Feb 20:29:06.523 * Connecting to MASTER 10.0.3.105:6389
11347:S 17 Feb 20:29:06.524 * MASTER <-> SLAVE sync started
11347:S 17 Feb 20:29:06.526 * Non blocking connect for SYNC fired the event.
11347:S 17 Feb 20:29:06.530 * Master replied to PING, replication can continue...
11347:S 17 Feb 20:29:06.532 * Partial resynchronization not possible (no cached master)
11347:S 17 Feb 20:29:06.612 * Full resync from master: e96dc8dcea06375e45f9e0796f796cb642b2a94a:243379789779
11347:S 17 Feb 20:29:36.120 * MASTER <-> SLAVE sync: receiving 1457381394 bytes from master
11347:S 17 Feb 20:29:43.069 # I/O error trying to sync with MASTER: connection lost
11347:S 17 Feb 20:29:43.191 * Connecting to MASTER 10.0.3.105:6389
11347:S 17 Feb 20:29:43.191 * MASTER <-> SLAVE sync started
11347:S 17 Feb 20:29:43.198 * Non blocking connect for SYNC fired the event.
11347:S 17 Feb 20:29:43.446 * Master replied to PING, replication can continue...
11347:S 17 Feb 20:29:43.450 * Partial resynchronization not possible (no cached master)
11347:S 17 Feb 20:29:43.532 * Full resync from master: e96dc8dcea06375e45f9e0796f796cb642b2a94a:247290941137
11347:S 17 Feb 20:30:17.763 * MASTER <-> SLAVE sync: receiving 1458314307 bytes from master
11347:S 17 Feb 20:30:20.516 # I/O error trying to sync with MASTER: connection lost
11347:S 17 Feb 20:30:21.303 * Connecting to MASTER 10.0.3.105:6389
11347:S 17 Feb 20:30:21.303 * MASTER <-> SLAVE sync started
11347:S 17 Feb 20:30:21.304 * Non blocking connect for SYNC fired the event.
11347:S 17 Feb 20:30:21.305 * Master replied to PING, replication can continue...
11347:S 17 Feb 20:30:21.307 * Partial resynchronization not possible (no cached master)
11347:S 17 Feb 20:30:21.389 * Full resync from master: e96dc8dcea06375e45f9e0796f796cb642b2a94a:251282993972
11347:S 17 Feb 20:30:55.365 * MASTER <-> SLAVE sync: receiving 1459112192 bytes from master
11347:S 17 Feb 20:31:05.299 * MASTER <-> SLAVE sync: Flushing old data
11347:S 17 Feb 20:31:20.802 * MASTER <-> SLAVE sync: Loading DB in memory
11347:S 17 Feb 20:31:41.073 * MASTER <-> SLAVE sync: Finished with success
А теперь, когда Production снова жив, рассмотрим внимательнее.
Что вообще происходит?
Redis master-slave — процесс синхронизации
Кратко вспомним процесс синхронизации мастер-слейв:
слейв при запуске или после потери коннекта обращается к мастеру с просьбой передать на слейв данные из базы мастера, что бы слейв мог начать обслуживать запросы клиентов
мастер в ответ на этот запрос:
создаёт дочерний процесс, который дампит базу Redis из памяти на диск в файл dump.rdb (см. fork() vs fork() vs clone())
в это время мастер продолжает отвечать на запросы от других клиентов
и любые данные, которые изменились этими клиентами с момента начала создания дампа сохраняет в буфер репликации
мастер сообщает слейву, что дамп готов, и слейв начинает загружать его по сети к себе, сохраняя на диск
слейв завершает передачу дампа к себе, загружает в память своего процесса Redis, и сообщает мастеру, что слейв готов к работе
мастер проверят свой буфер, и если в нём есть данные — он передаёт изх на слейв, что бы тот заменил данные в своей базе, созданной из дампа, последними, актуальными данными
слейв применяет изменения, и вступает в работу
Таким образом, синхронизация мастер-слейв включает в себя два этапа:
сначала содаётся полная копия данных, которая может содержать устаревшие данные
затем выполняется последовательный набор апдейтов из буфера, которые заканчвают приведение новой базы в состояние, аналогичное базе на мастере
Размер базы и буфера
Проблемы — при снапшоте может потребоваться памяти до х3 от размера базы, и главная сложность заключается даже не в том, что бы создать сам снапшот данных, а в том, что его надо передать на слейв, и при этом сохранить консистентность данных между мастером и слейвом, так как пока данные передаются по сети, с её задержками, пока слейв запишет её к себе на диск, пока загрузит к себе в память — на мастере данные успеют измениться.
Пока выполняется передача дампа — все эти изменения мастер сохраняет в тот самый буфер, но при его переполнении — он очищается, а процесс синхронизации со слейвом начинается с нуля, и может так повторяться до бесконечности.
В теории — размер буфера должен быть не меньше, чем максимальный размер базы, хотя вероятность того, что в процессе дампа изменятся все данные мала.
В любом случае, тут всё достаточно условно, и требует тестирования и мониторинга для подбора оптимальных настроек.
Приблизительынй пример расчёта.
Проверяем празмер базы в памяти:
root@bttrm-production-console:/home/admin# redis-cli -h 10.0.3.105 -p 6389 info memory
------- data ------ --------------------- load -------------------- - child -
keys mem clients blocked requests connections
530178 765.42M 9 0 91699673 (+0) 2856563
530407 766.04M 16 0 91700194 (+521) 2856580
...
530178 — 530407 == 229 ключей за секунду.
Либо можно использовать Prometheus и redis-exporter, а затем сделать выборку по SET-запросам, т.к. основной массив данных будет в них, используя запрос вида: