Redis: основные параметры конфигурации и тюнинг производительности

Автор: | 05/09/2019

В процессе настройки Redis  в роли кеширующего сервиса для приложения – набросался такой вот пост.

Рассмотрим основные параметры конфигурации Redis, их значения, как они и на что они влияют, и на какие из них стоит обратить внимание.

Местами совсем кратко, но везде со ссылками.

Начать, пожалуй, стоит с утилиты redis-benchmark.

Устанавливается вместе с сами Redis, можно сразу использовать:

[simterm]

root@bttrm-dev-app-1:/home/admin# redis-benchmark -p 6389 -n 1000 -c 10 -k 1
====== 1 ======
  1000 requests completed in 0.03 seconds
  10 parallel clients
  3 bytes payload
  keep alive: 1

98.30% <= 1 milliseconds
99.30% <= 2 milliseconds
100.00% <= 2 milliseconds
30303.03 requests per second

[/simterm]

Кроме того – можно выполнить проверку кол-ва операций с помощью redis-cli и опции --latency или --latency-dist:

Redis server-level config

timeout

Отключать соединение при неактивности клиента заданное количество секунд.

Ноль для отключения возможности (IDLE-клиенты будут висеть до перезагрузки, или пока не отключатся сами):

...
timeout 0
...

В целом – есть смысл оставить дефолтные 300 секунд, что бы не оставались зависшие клиенты.

tcp-keepalive

См. Redis Clients Handling.

Не влияет при PUBLISH/SUBSCRIBE (real-time operations), см. Redis Pub/Sub: Intro Guide и Redis Pub/Sub… How Does it Work?

Сервер отправляет ACK-запросы (Acknowledgment) через указанный промежуток времени в секундах, поддерживая сессию:

...
tcp-keepalive 300
...

[simterm]

127.0.0.1:6389> CONFIG GET tcp-keepalive
1) "tcp-keepalive"
2) "300"

[/simterm]

Значение по-умолчанию – 300 секунд (но зависит от версии).

Если клиент не отвечает на запрос – соединение закрывается.

Если и timeout, и tcp-keepalive на стороне сервера будут заданы в 0 (отключены) – то “мёртвые” подключения будут висеть до перезагрузки сервера.

См. Things that you may want to know about TCP Keepalives.

Проверяем с включенным tcp-keepalive (-k == 1):

[simterm]

root@bttrm-dev-app-1:/home/admin# redis-benchmark -p 6389 -n 1000 -c 10 -k 1 -q | tail -2
MSET (10 keys): 16129.03 requests per second

[/simterm]

И без:

[simterm]

root@bttrm-dev-app-1:/home/admin# redis-benchmark -p 6389 -n 1000 -c 10 -k 0 -q | tail -2
MSET (10 keys): 7042.25 requests per second

[/simterm]

Снова-таки – не вижу смысла в отключении проверки вообще, потому – оставляем дефолтные 300 секунд.

RDB Persistence

Создаёт полную копию базы. См. Redis Persistence.

Поведение определяется параметром save (см. также Redis save, SAVE и BGSAVE).

Проверяем:

[simterm]

127.0.0.1:6379> CONFIG GET save
1) "save"
2) "3600 1 300 100 60 10000"

[/simterm]

Удаляем:

[simterm]

127.0.0.1:6379> CONFIG SET save ""
OK
127.0.0.1:6379> CONFIG GET save
1) "save"
2) ""

[/simterm]

Сохраняем:

[simterm]

127.0.0.1:6389> CONFIG rewrite
OK

[/simterm]

В случае, если Redis используется просто для кеширования – можно отключить: убираем save из конфига вообще.

При этом сам механизм RDB будет использоваться для синхронизации мастер-слейв при использовании репликации (см. Redis: репликация, часть 1 — обзор. Replication vs Sharding. Sentinel vs Cluster. Топология Redis).

AOF persistence

Append Only File – сохраняет в лог каждую операцию.

Аналогично RDB, если Redis используем просто, как кеш – лог не нужен.

Для отключения – меняем параметр appendonly, и задаём его в no:

...
appendonly no
...

maxmemory

maxmemory задаёт максимальный размер памяти сервера, который будет доступен Redis-у для хранения данных.

См. Using Redis as an LRU cache.

Может быть задан в виде %:

[simterm]

127.0.0.1:6389> CONFIG SET maxmemory 80
OK

[/simterm]

Или мегабайт/гигабайт:

[simterm]

127.0.0.1:6389> CONFIG SET maxmemory 1gb
OK
127.0.0.1:6389> CONFIG GET maxmemory
1) "maxmemory"
2) "1073741824"

[/simterm]

Или в 0 для отключения лимита вообще и является значением по-умолчанию для 64-х битных систем. Для 32-х битных – 3 ГБ.

При достижении лимита – будут выбрано решение об удалении данных, основываясь на политиках, см. maxmemory-policy.

Учитывая, что у нас на каждом хосте кроме самого Redis крутятся PHP-процессы и memcached – под Redis можно отдать не более 50% памяти.

maxmemory-policy

Определяет политику, которая будет использовать Redis при достижении maxmemory.

Note: LRU – Less Recently Used

Может иметь одно из значений:

  • volatile-lru: будут удалены наименее используемые ключи, у которых задан expire
  • allkeys-lru: будут удалены наименее используемые ключи независимо от expire
  • volatile-random: удалить случайный ключ с заданным expire
  • allkeys-random: удалить случайный ключ независимо от expire
  • volatile-ttl: удалить ключ с наименьшим оставшимся TTL
  • noeviction: не выполнять очистку вообще, просто возвращать ошибку при операциях записи

Для проверки значение expire – используйте TTL/PTTL:

[simterm]

127.0.0.1:6379> set test "test"
OK
127.0.0.1:6379> ttl test
(integer) -1
127.0.0.1:6379> pttl test
(integer) -1
127.0.0.1:6379> EXPIRE test 10
(integer) 1
127.0.0.1:6379> ttl test
(integer) 8
127.0.0.1:6379> ttl test
(integer) 7
127.0.0.1:6379> ttl test
(integer) 7

[/simterm]

В нашем случае – девелоперы не уверены, что expire задаётся для всех ключей, а учитывая, что снова-таки – это просто сервис кеширования, данные из которого потерять не страшно – то задаём maxmemory-policy allkeys-lru.

unixsocket

Если приложение и Redis работают на одном хосте – попробуйте использование сокета вместо TCP-порта.

Задаём:

...
unixsocket /tmp/redis.sock
unixsocketperm 755
...

Может дать хороший прирост производительности, см. Tuning Redis for extra Magento performance.

Проверим с redis-benchmark.

Создаём тестовый конфиг:

unixsocket /tmp/redis.sock
unixsocketperm 775
port 0

Запускаем с сокетом:

[simterm]

root@bttrm-dev-console:/etc/redis-cluster# redis-server /etc/redis-cluster/test.conf

[simterm]

Проверяем:

[simterm]

root@bttrm-dev-console:/home/admin# redis-benchmark -s /tmp/redis.sock -n 1000 -c 100 -k 1
...
90909.09 requests per second

[/simterm]

И через TCP-порт:

[simterm]

root@bttrm-dev-console:/home/admin# redis-benchmark -p 7777 -n 1000 -c 100 -k 1
...
66666.67 requests per second

[/simterm]

loglevel

Уровень детализации лога. При debug – наиболее подробный, следовательно – отнимает больше ресурсов.

Может быть (от наиболее подробного – к наименее): debug, verbose, notice, warning.

Значение по-умолчанию – notice, пока сервис в процессе допиливания – можно оставить notice, позже – переключить в warning.

OS-level config

Transparent Huge Page

Механизм ядра Linux, позволяющий уменьшить количество объектов при выделении и управлении виртуальной памятью. См Transparent Hugepages: measuring the performance impactDisable Transparent Hugepages и Latency induced by transparent huge pages.

В Redis, судя по документации>>>, имеет значение только при включенном RDB, но имеет смысл отключить вообще:

Проверить текущее значение можно:

[simterm]

root@bttrm-dev-app-1:/home/admin# cat /sys/kernel/mm/transparent_hugepage/enabled 
always [madvise] never

[/simterm]

Значение в скобках явлется текущим заданным значением, а данном случае – madvice.

madvice указывает на использование THP только в случае, если это явно запрошено приложением через вызов madvice().

Проверить состояние можно так:

[simterm]

root@bttrm-dev-app-1:/home/admin# grep HugePages /proc/meminfo 
AnonHugePages:         0 kB
ShmemHugePages:        0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0

[/simterm]

maxclients и fs.file-max

Задаёт максимальное количество одновременно подключенных клиентов.

Значение по умолчанию – 10.000, переопределяется через maxclients, см. Maximum number of clients.

При этом Redis проверяет лимиты операционной системы на максимальное количество файловых дескрипторов – sysctl fs.file-max глобально для ядра:

[simterm]

root@bttrm-dev-app-1:/home/admin# sysctl fs.file-max
fs.file-max = 202080
root@bttrm-dev-app-1:/home/admin# cat /proc/sys/fs/file-max
202080

[/simterm]

И ulimit для пользователя на каждый процесс:

[simterm]

root@bttrm-dev-app-1:/home/admin# ulimit -Sn
1024

[/simterm]

Для systemd-based систем можно задать лимит через systemd-юнит файл Redis с помощью LimitNOFILE:

[simterm]

root@bttrm-dev-app-1:/home/admin# cat /etc/systemd/system/redis.service | grep LimitNOFILE
LimitNOFILE=65535

[/simterm]

tcp-backlog и net.core.somaxconn

Redis задаёт ограничение очереди подключения клиентов в параметре tcp-backlog (по-умолчанию 511).

При этом – операционна ясистема проверяет собственный лимит – net.core.somaxconn, и если он меньше, чем лимит Redis – будет выдано предупреждение вида:

The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128

Что такое TCP backlog и net.core.somaxconn

Что бы понять значение tcp-backlog и роль net.core.somaxconn – вспомним процесс установления сессии и начала передачи данных:

  1. сервер: приложение на сервере выполняет listen(), передавая в listen() первым аргументом файловый дескриптор сокета, а вторым – размер accept backlog (значение tcp-backlog из redis.conf)
  2. клиент: приложение клиента выполняет connect() и отправляет серверу SYN-пакет
    1. на стороне клиента соединение переходит в состояние SYN_SENT
    2. на стороне сервера новое соединение переходит в состояние SYN_RCVD, и попадает в очередь syn backlog (net.ipv4.tcp_max_syn_backlog) – incomplete connection queue
  3. сервер: отправляет SYN+ACK
  4. клиент: отправляет ACK, и переводит соединение в ESTABLISHED
  5. сервер: принимет ACK, и переводит соединение в ESTABLISHED, перемещая его в accept backlogcomplete connection queue
  6. сервер вызывает accept(), передавая ему соединение из очереди accept backlog
  7. клиент: вызывает write(), и начинает передачу данных
  8. север: вызывает read(), и получает данные

Собственно, если в listen() Redis-а значение backlog передаётся больше, чем оно задано в лимитах ядра – net.core.somaxconn – и вызывается сообещение “TCP backlog setting cannot be enforced“.

128 является значением по-умолчанию:

[simterm]

root@bttrm-dev-app-1:/home/admin# sysctl net.core.somaxconn
net.core.somaxconn = 128

[/simterm]

Задаём через -w:

[simterm]

root@bttrm-dev-console:/home/admin# sysctl -w net.core.somaxconn=65535
net.core.somaxconn = 65535
root@bttrm-dev-console:/home/admin# sysctl -p

[/simterm]

См. TCP connection backlog – a struggling server и TCP Three-Way Handshake.

vm.overcommit_memory

Наверно, самый неоднозначный параметр.

Крайне рекомендую посмотреть пост Redis: fork — Cannot allocate memory, Linux, виртуальная память и vm.overcommit_memory – про виртуальную память, системные вызовы и Redis.

См. также overcommit_memory и См. Background saving fails with a fork() error under Linux even if I have a lot of free RAM.

overcommit_memory играет роль при выполнении операций созданий снапшотов данных из памяти на диск, а конкретно – при вызове BGSAVE.

В нашем случае, с отключенным RDB и AOF, когда Redis используется только для кеширования, и хранить данные на диске смысла нет — нет смысла и в изменении overcommit_memory, и следует оставить его в значении по-умолчанию — 0.

А уж если хочется задать лимит вручную — то лучше использовать overcommit_memory == 2, и ограничение по %, что бы всегда оставался запас памяти.

vm.swappiness

Если операционной системе разрешено использование SWAP – она может сдемпить часть данных Redis на диск, и когда Redis попробует их считать – это вызовет задержку, т.к. ему придётся ждать, пока система не считает данные с диска обратно в память.

Во избежание – отключаем SWAP вообще:

[simterm]

root@bttrm-dev-console:/home/admin# sysctl -w vm.swappiness=0

[/simterm]

Ссылки по теме