Оригинальный адрес:Чай пуэр 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 Вт, а может быть и больше, для достижения этой цели есть три важных компонента:
- Пул потоков Percona Server
- правильные настройки сети
- Настройте несколько 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