Задача MySQL: создание 100 000 подключений

MySQL

Оригинальный адрес:Чай пуэр Ohna.com/blog/2019/0…

Цель этой статьи — изучить способ установки соединений 10w на одном сервере MySQL. Вместо 10w бездействующих соединений мы хотим установить соединения, которые могут выполнять запросы.

Вы можете спросить, действительно ли моему серверу MySQL нужны соединения 10 Вт? Я видел много различных сценариев развертывания, таких как использование пула соединений, размещение 1000 соединений в пуле соединений каждого приложения и развертывание 100 таких серверов приложений. Есть также несколько действительно плохих практик, использующих метод «медленный запрос, повторное подключение и повторная попытка». Это создает эффект снежного кома, что может привести к установлению тысяч соединений за несколько секунд.

Поэтому я решил поставить «маленькую цель», чтобы посмотреть, смогу ли я ее достичь.

этап подготовки

Давайте сначала взглянем на аппаратное обеспечение.Сервер предоставляется пакетом.net (поставщик облачных услуг), и конфигурация выглядит следующим образом:

instance size: c2.medium.x86 Physical Cores @ 2.2 GHz (1 X AMD EPYC 7401P) Memory: 64 GB of ECC RAM Storage : INTEL® SSD DC S4500, 480GB

Нам нужно 5 таких серверов, 1 для сервера MySQL и 4 для клиентов. Сервер MySQL использует MySQL 8.0.13-4 Percona Server с подключаемым модулем пула потоков, который должен поддерживать тысячи подключений.

Инициализировать конфигурацию сервера

Настройки сети:

- { name: 'net.core.somaxconn', value: 32768 }
- { name: 'net.core.rmem_max', value: 134217728 }
- { name: 'net.core.wmem_max', value: 134217728 }
- { name: 'net.ipv4.tcp_rmem', value: '4096 87380 134217728' }
- { name: 'net.ipv4.tcp_wmem', value: '4096 87380 134217728' }
- { name: 'net.core.netdev_max_backlog', value: 300000 }
- { name: 'net.ipv4.tcp_moderate_rcvbuf', value: 1 }
- { name: 'net.ipv4.tcp_no_metrics_save', value: 1 }
- { name: 'net.ipv4.tcp_congestion_control', value: 'htcp' }
- { name: 'net.ipv4.tcp_mtu_probing', value: 1 }
- { name: 'net.ipv4.tcp_timestamps', value: 0 }
- { name: 'net.ipv4.tcp_sack', value: 0 }
- { name: 'net.ipv4.tcp_syncookies', value: 1 }
- { name: 'net.ipv4.tcp_max_syn_backlog', value: 4096 }
- { name: 'net.ipv4.tcp_mem', value: '50576   64768 98152' }
- { name: 'net.ipv4.ip_local_port_range', value: '4000 65000' }
- { name: 'net.ipv4.netdev_max_backlog', value: 2500 }
- { name: 'net.ipv4.tcp_tw_reuse', value: 1 }
- { name: 'net.ipv4.tcp_fin_timeout', value: 5 }

Настройки ограничения системы:

[Service]
LimitNOFILE=1000000
LimitNPROC=500000

Соответствующая конфигурация MySQL (файл my.cnf):

back_log=3500
max_connections=110000

Клиент использует версию sysbench 0.5, а не 1.0.x. Конкретные причины будут объяснены позже.

Выполните команду: sysbench --test=sysbench/tests/db/select.lua --mysql-host=139.178.82.47 --mysql-user=sbtest--mysql-password=sbtest --oltp-tables-count=10 --report-interval=1 --num-threads=10000 --max-time=300 --max-requests=0 --oltp-table-size=10000000 --rand-type=uniform --rand-init= в бегах

Первый шаг, 10 000 подключений

Этот шаг очень прост, и мы можем сделать его без особых настроек. Для этого шага требуется только одна машина в качестве клиента, но клиент может иметь следующие ошибки:

FATAL: error 2004: Can't create TCP/IP socket (24)

Это связано с ограничением количества открытых файлов, которое ограничивает количество сокетов TCP/IP и может быть настроено на стороне клиента:

ulimit -n100000

Теперь посмотрим на производительность:

[  26s] threads: 10000, tps: 0.00, reads: 33367.48, writes: 0.00, response time: 3681.42ms (95%), errors: 0.00, reconnects:  0.00
[  27s] threads: 10000, tps: 0.00, reads: 33289.74, writes: 0.00, response time: 3690.25ms (95%), errors: 0.00, reconnects:  0.00

Второй шаг, 25 000 подключений

Этот шаг вызовет ошибку на сервере MySQL:

Can't create a new thread (errno 11); if you are not out of available memory, you can consult the manualfor a possible OS-dependent bug

Решение этой проблемы смотрите по этой ссылке:

https://www.percona.com/blog/2013/02/04/cant_create_thread_errno_11/

Но этот подход не работает в нашей текущей ситуации, так как мы исчерпали все ограничения:

cat /proc/`pidof mysqld`/limits
Limit                     Soft Limit Hard Limit           Units
Max cpu time              unlimited  unlimited            seconds
Max file size             unlimited  unlimited            bytes
Max data size             unlimited  unlimited            bytes
Max stack size            8388608    unlimited            bytes
Max core file size        0          unlimited            bytes
Max resident set          unlimited  unlimited            bytes
Max processes             500000     500000               processes
Max open files            1000000    1000000              files
Max locked memory         16777216   16777216             bytes
Max address space         unlimited  unlimited            bytes
Max file locks            unlimited  unlimited            locks
Max pending signals       255051     255051               signals
Max msgqueue size         819200     819200               bytes
Max nice priority         0          0
Max realtime priority     0          0
Max realtime timeout      unlimited unlimited            us

Вот почему мы изначально выбрали сервис с пулом потоков: https://www.percona.com/doc/percona-server/8.0/performance/threadpool.html.

Добавьте следующую строку в файл my.cnf и перезапустите службу.

thread_handling=pool-of-threads

Проверьте результаты

[   7s] threads: 25000, tps: 0.00, reads: 33332.57, writes: 0.00, response time: 974.56ms (95%), errors: 0.00, reconnects:  0.00
[   8s] threads: 25000, tps: 0.00, reads: 33187.01, writes: 0.00, response time: 979.24ms (95%), errors: 0.00, reconnects:  0.00

Пропускная способность осталась прежней, но время отклика 95% уменьшилось с 3690 мс до 979 мс.

Шаг 3, 50 000 подключений

Здесь мы встречаемся с нашей самой большой проблемой. При попытке сначала установить соединение 5w sysbench сообщил об ошибке:

FATAL: error 2003: Can't connect to MySQL server on '139.178.82.47' (99)

Ошибка Error (99) является загадочной и означает, что указанный адрес не может быть выделен. Эта проблема вызвана ограничением на количество портов, которые может открыть приложение. Конфигурация нашей системы по умолчанию:

cat /proc/sys/net/ipv4/ip_local_port_range : 32768 60999

Это означает, что у нас есть только 28 231 доступный порт (60 999 минус 32 768), или максимальное количество TCP-соединений, которые вы можете установить с данным IP-адресом. Вы можете расширить этот диапазон на сервере и клиенте:

echo 4000 65000 > /proc/sys/net/ipv4/ip_local_port_range

Это дает нам 61 000 подключений, что близко к максимальному пределу портов, доступных для IP (65535). Ключевым моментом здесь является то, что если мы хотим достичь соединений 10 Вт, нам нужно назначить больше IP-адресов серверу MySQL, поэтому я назначил серверу MySQL два IP-адреса.

После решения проблемы количества портов мы столкнулись с новой проблемой:

sysbench 0.5:  multi-threaded system evaluation benchmark
Running the test with following options:
Number of threads: 50000
FATAL: pthread_create() for thread #32352 failed. errno = 12 (Cannot allocate memory)

Эта проблема вызвана проблемой выделения памяти с помощью sysbench. Память, которую может выделить только sysbench, может создать только 32 351 соединение, эта проблема более серьезна в версии 1.0.x.

Ограничения Sysbench 1.0.x

Sysbench 1.0.x использует другой компилятор Lua, что делает невозможным создание более 4000 подключений. Таким образом, похоже, что Sysbench достигает своего предела раньше, чем Percona Server, поэтому нам нужно использовать больше клиентов. При максимальном количестве подключений 32 351 на клиента нам потребуется как минимум 4 клиента, чтобы достичь цели подключения 10 Вт.

Для достижения соединений 5w мы использовали две машины в качестве клиентов, каждая с 25 000 потоков. Результат выглядит следующим образом:

[  29s] threads: 25000, tps: 0.00, reads: 16794.09, writes: 0.00, response time: 1799.63ms (95%), errors: 0.00, reconnects:  0.00
[  30s] threads: 25000, tps: 0.00, reads: 16491.03, writes: 0.00, response time: 1800.70ms (95%), errors: 0.00, reconnects:  0.00

Пропускная способность аналогична предыдущему шагу (общее число транзакций в секунду составляет 16794*2 = 33588), но производительность снижается, а 95% времени отклика удваивается. Это ожидаемо, так как мы удвоили количество подключений по сравнению с предыдущим шагом.

Шаг 4, 75 000 подключений

На этом шаге мы добавляем еще один сервер в качестве клиента, и каждый клиент также запускает 25 000 потоков. Результат выглядит следующим образом:

[ 157s] threads: 25000, tps: 0.00, reads: 11633.87, writes: 0.00, response time: 2651.76ms (95%), errors: 0.00, reconnects:  0.00
[ 158s] threads: 25000, tps: 0.00, reads: 10783.09, writes: 0.00, response time: 2601.44ms (95%), errors: 0.00, reconnects:  0.00

Шаг 5, 100 000 подключений

Наконец добрались до станции, этот шаг тоже не сложный, просто нужно открыть еще один клиент, тоже запустить 25000 потоков. Результат выглядит следующим образом:

[ 101s] threads: 25000, tps: 0.00, reads: 8033.83, writes: 0.00, response time: 3320.21ms (95%), errors: 0.00, reconnects:  0.00
[ 102s] threads: 25000, tps: 0.00, reads: 8065.02, writes: 0.00, response time: 3405.77ms (95%), errors: 0.00, reconnects:  0.00

Пропускная способность остается на уровне 32260 (8065*4), а время отклика 95% составляет 3405 мс.

Здесь есть очень важная вещь, и вы, должно быть, обнаружили, что скорость отклика соединений 10w с потоками даже лучше, чем скорость отклика соединений 1w без пулов потоков.Пулы потоков позволяют Percona Server более эффективно управлять ресурсами и обеспечивают лучшее время отклика.

В заключение

Достижимо подключение 10 Вт, а может быть и больше, для достижения этой цели есть три важных компонента:

  1. Пул потоков Percona Server
  2. правильные настройки сети
  3. Настройте несколько IP-адресов для сервера MySQL (ограничение 65535 подключений на IP-адрес)

приложение

Наконец, вставьте полный файл my.cnf

[mysqld]
datadir {{ mysqldir }}
ssl=0
skip-log-bin
log-error=error.log
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
character_set_server=latin1
collation_server=latin1_swedish_ci
skip-character-set-client-handshake
innodb_undo_log_truncate=off
# general
table_open_cache = 200000
table_open_cache_instances=64
back_log=3500
max_connections=110000
# files
innodb_file_per_table
innodb_log_file_size=15G
innodb_log_files_in_group=2
innodb_open_files=4000
# buffers
innodb_buffer_pool_size= 40G
innodb_buffer_pool_instances=8
innodb_log_buffer_size=64M
# tune
innodb_doublewrite= 1
innodb_thread_concurrency=0
innodb_flush_log_at_trx_commit= 0
innodb_flush_method=O_DIRECT_NO_FSYNC
innodb_max_dirty_pages_pct=90
innodb_max_dirty_pages_pct_lwm=10
innodb_lru_scan_depth=2048
innodb_page_cleaners=4
join_buffer_size=256K
sort_buffer_size=256K
innodb_use_native_aio=1
innodb_stats_persistent = 1
#innodb_spin_wait_delay=96
innodb_adaptive_flushing = 1
innodb_flush_neighbors = 0
innodb_read_io_threads = 16
innodb_write_io_threads = 16
innodb_io_capacity=1500
innodb_io_capacity_max=2500
innodb_purge_threads=4
innodb_adaptive_hash_index=0
max_prepared_stmt_count=1000000
innodb_monitor_enable = '%'
performance_schema = ON