Пул потоков в MariaDB
Задача, решаемая пулами потоков
Задача масштабируемого серверного программного обеспечения (а СУБД, как MariaDB, — пример такого ПО) — поддерживать максимальную производительность при увеличении числа клиентов. MySQL традиционно выделял поток для каждого подключения клиента, и по мере роста числа одновременных пользователей эта модель демонстрирует снижение производительности. Многие активные потоки — убийцы производительности, так как увеличение числа потоков приводит к интенсивному переключению контекста, плохому использованию кэшей процессора и увеличению конкуренции за горячие блокировки. Идеальное решение, которое помогло бы сократить переключение контекста, — поддерживать меньшее число потоков, чем число клиентов. Но это число не должно быть слишком низким, так как мы также хотим использовать процессоры по максимуму, поэтому в идеале должно быть по одному активному потоку на каждый процессор в системе.
Особенности пула потоков MariaDB
Текущий пул потоков MariaDB был реализован в MariaDB 5.5. Он заменил устаревший пул потоков, который был представлен в MariaDB 5.1. Основным недостатком предыдущего решения было то, что этот пул был статическим — имел фиксированное число потоков. Статические пулы потоков могут иметь свои преимущества для некоторых ограниченных случаев использования, таких как случаи, когда обратные вызовы, выполняемые потоками, никогда не блокируются и не зависят друг от друга. Например, что-то вроде эхо-сервера.
Однако клиенты СУБД более сложные. Например, поток может зависеть от завершения другого потока, и они могут блокировать друг друга с помощью блокировок и/или ввода-вывода. Таким образом, очень сложно, а иногда и невозможно, предсказать, сколько потоков будет оптимальным или даже достаточным для предотвращения тупиковых ситуаций в каждой ситуации. MariaDB 5.5 реализует динамический/адаптивный пул, который сам заботится о создании новых потоков в моменты высокой загрузки и утилизации потоков, если у них нет работы. Это полная переработка устаревшего pool-of-threads планировщика со следующими целями:
- Сделать пул динамическим, чтобы он увеличивался и уменьшался по мере необходимости.
- Минимизировать количество накладных расходов, необходимых для поддержания пула потоков.
- Максимально использовать возможности операционной системы. Например, если доступна собственная реализация пула потоков, то она должна использоваться, а если нет, то используется наилучший метод множественного ввода-вывода.
- Ограничить ресурсы, используемые потоками.
В настоящее время существуют две разные реализации низкого уровня — в зависимости от ОС. Одна реализация разработана специально для Windows и использует родной CreateThreadpool API. Вторая реализация в основном предназначена для использования в системах Unix-подобных. Поскольку реализации отличаются, некоторые системные переменные отличаются между Windows и Unix.
Когда следует использовать пул потоков
Пулы потоков наиболее эффективны в ситуациях, когда запросы относительно короткие и нагрузка ограничена процессором, например, в задачах OLTP. Если рабочая нагрузка не ограничена процессором, то вы все равно можете выиграть, ограничив число потоков, чтобы сохранить память для буферов памяти базы данных.
Когда пул потоков менее эффективен
Существуют специальные, редкие случаи, когда пул потоков, вероятно, будет менее эффективен.
- Если у вас очень импульсная рабочая нагрузка, то пул потоков может вам не подойти. Такие рабочие нагрузки, как правило, характеризуются длительными периодами бездействия, за которыми следуют короткие периоды очень высокой активности многих пользователей. Это также, как правило, рабочие нагрузки, в которых задержки недопустимы, поэтому ограничение создания потоков, используемое пулом потоков, не является идеальным. Даже в этой ситуации производительность можно улучшить, настроив частоту утилизации потоков. Например, с
thread_pool_idle_timeoutв Unix или сthread_pool_min_threadsв Windows.
- Если у вас много одновременных, длинных, не уступающих запросов, то пул потоков может вам не подойти. В этом контексте «не уступающий» запрос — это запрос, который никогда не ждет или который не указывает ожидания пулу потоков. Такие виды рабочих нагрузок в основном используются в сценариях хранилищ данных. Длительные, неуступающие запросы будут задерживать выполнение других запросов. Однако пул потоков имеет обнаружение задержек, чтобы предотвратить их полную монополизацию пула потоков. Более подробную информацию см. в разделе Группы потоков в реализации пула потоков в Unix: Задержки группы потоков. Даже когда весь пул потоков заблокирован неуступающими запросами, вы все равно можете подключиться к серверу через порт
extra-portTCP/IP.
- Если вы полагаетесь на то, что простые запросы всегда завершаются быстро, независимо от того, насколько загружен ваш сервер базы данных, то пул потоков может вам не подойти. Когда пул потоков включен на загруженном сервере, даже простые запросы могут быть помещены в очередь для последующего выполнения. Это означает, что даже если сам оператор не занимает много времени для выполнения, даже простая
SELECT 1, может занять немного больше времени, когда включен пул потоков, чем сone-thread-per-connection, если она окажется в очереди.
Настройка пула потоков
Переменная системы thread_handling — основная переменная системы, используемая для настройки пула потоков.
Существуют и другие системные переменные, которые описаны в следующих разделах. Многие из задокументированных ниже системных переменных являются динамическими, то есть их можно изменить с помощью SET GLOBAL на работающем сервере.
Как правило, нет необходимости изменять многие из этих системных переменных. Цель пула потоков заключалась в обеспечении хорошей производительности «из коробки». Однако значения системных переменных можно изменить, и мы намеревались предоставить как можно больше настроек из базовой реализации. Не стесняйтесь настраивать их по своему усмотрению.
Если вы обнаружите какие-либо проблемы с каким-либо из значений по умолчанию, мы рекомендуем вам отправить отчет об ошибке.
Полный список системных переменных пула потоков см. в разделе Системные и статусные переменные пула потоков.
Настройка пула потоков в Unix
В Unix, если вы хотите использовать пул потоков, вы можете использовать его, установив системную переменную thread_handling в значение pool-of-threads в группе опций сервера группа опций в файле опций файл опций до запуска сервера. Например:
[mariadb] ... thread_handling=pool-of-threads
В Unix также можно настроить следующие системные переменные:
-
thread_pool_size— число групп потоков в пуле потоков, которое определяет, сколько операторов может выполняться одновременно. Значение по умолчанию — количество процессоров в системе. При установке значения этой системной переменной при запуске системы максимальное значение равно 100000. Однако не рекомендуется устанавливать его так высоко. При динамической установке значения этой системной переменной максимальное значение равно либо 128, либо значению, установленное при запуске системы — в зависимости от того, какое значение больше. Дополнительную информацию см. в разделе Группы потоков в реализации пула потоков в Unix.
-
thread_pool_max_threads— максимальное количество потоков в пуле потоков. После достижения этого предела в большинстве случаев новые потоки создаваться не будут. В редких случаях фактическое количество потоков может незначительно превышать это значение, поскольку каждая группа потоков требует по крайней мере двух потоков (т. е. по крайней мере одного потока-рабочего и по крайней мере одного потока-слушателя) для предотвращения тупиковых ситуаций. Значение по умолчанию в MariaDB 5.5 и MariaDB 10.0 —500. Значение по умолчанию в MariaDB 10.1 —1000в MariaDB 10.1. Значение по умолчанию в MariaDB 10.2 и более поздних версиях —65536.
-
thread_pool_stall_limit— количество миллисекунд между каждым контролем задержки, выполняемым таймерным потоком. Значение по умолчанию —500. Обнаружение задержек используется для предотвращения монополизации группы потоков одним клиентом. Когда таймерный поток обнаруживает, что группа потоков заблокирована, он разбуживает спящий поток-рабочий в группе потоков, если он доступен. Если нет, то создает новый рабочий поток в группе. Это временно позволяет нескольким подключениям клиентов в группе потоков работать параллельно. Однако обратите внимание, что таймерный поток не будет создавать новый поток-рабочий, если количество потоков в пуле уже больше или равно максимальному, определенному переменнойthread_pool_max_threads, если только группа потоков еще не имеет потока-слушателя. Дополнительную информацию см. в разделе Группы потоков в реализации пула потоков в Unix: Задержки группы потоков.
-
thread_pool_oversubscribe— определяет, сколько рабочих потоков в группе потоков могут оставаться активными одновременно после того, как группа потоков перегружена из-за задержек. Значение по умолчанию —3. Обычно в группе потоков активен только один рабочий поток. Однако таймерный поток может добавить больше активных рабочих потоков в группу потоков, если обнаружит задержку. При принятии решения о том, следует ли допускать только один поток на процессор или больше одного потока на процессор, нужно учитывать компромиссы. Допущение только одного потока на процессор означает, что поток может иметь неограниченный доступ к процессору во время работы, но также означает, что существует дополнительная накладная от приостановки или возобновления потоков чаще. Разрешение больше одного потока на процессор означает, что потоки должны делить процессор, но также означает, что накладных расходов от приостановки или возобновления потоков меньше. Это в основном предназначено для внутреннего использования и не предназначено для изменения большинству пользователей. Дополнительную информацию см. в разделе Группы потоков в реализации пула потоков в Unix: Перегрузка группы потоков.
-
thread_pool_idle_timeout— количество секунд, в течение которых неактивный поток-рабочий выйдет из системы. Значение по умолчанию —60. Если в данный момент нет работы, сколько времени должен ожидать неактивный поток перед завершением?
Настройка пула потоков в Windows
Реализация пула потоков в Windows использует родной пул потоков, созданный с помощью API CreateThreadpool.
В Windows, если вы хотите использовать пул потоков, то вам ничего не нужно делать, так как по умолчанию система переменная thread_handling уже установлена в значение pool-of-threads.
Однако, если вы хотите использовать старое поведение одного потока на подключение в Windows, вы можете это сделать, установив системную переменную thread_handling в значение one-thread-per-connection в группе опций сервера группа опций в файле опций файл опций перед запуском сервера. Например:
[mariadb] ... thread_handling=one-thread-per-connection
В более старых версиях Windows, таких как XP и 2003, pool-of-threads не реализовано, и сервер автоматически переключится на использование устаревшего метода one-thread-per-connection.
Родной API CreateThreadpool позволяет приложениям устанавливать минимальное и максимальное количество потоков в пуле. Следующие системные переменные могут быть использованы для настройки этих значений в Windows:
-
thread_pool_min_threads– Минимальное количество потоков в пуле. По умолчанию значение 1. Это применимо в особых случаях с очень «импульсивными» рабочими нагрузками. Представьте, что после периодов высокой активности следуют более длительные периоды бездействия. Пока пул потоков простаивает, Windows может принять решение об удалении потоков из пула (по результатам экспериментов, это происходит после того, как поток простоял 1 минуту). При следующей высокой нагрузке может потребоваться несколько миллисекунд или секунд, чтобы размер пула потоков стабилизировался до оптимального значения. Чтобы избежать удаления потоков, можно установить параметр на более высокое значение.
-
thread_pool_max_threads– Максимальное количество потоков в пуле. Потоки не создаются, когда это значение достигнуто. Значение по умолчанию от MariaDB 5.5 до MariaDB 10.0 равно 500 (это было увеличено до 1000 в MariaDB 10.1). Этот параметр может использоваться для предотвращения создания новых потоков, если пул может иметь короткие периоды, когда многие или все клиенты заблокированы (например, с «FLUSH TABLES WITH READ LOCK», высокой конкуренцией за блокировки строк или подобными ситуациями). Новые потоки создаются, если возникает блокировка (например, после интервала ограничения скорости), но иногда вы хотите ограничить количество потоков, если вы знакомы с приложением и хотите, например, сэкономить память. Если ваше приложение постоянно использует 500 потоков, это может быть сильным индикатором высокой конкуренции в приложении, и пул потоков не сильно помогает.
Настройка планирования по приоритетам
Начиная с MariaDB 10.2.2, можно настроить приоритет подключения. Поведение по приоритетам настраивается системной переменной thread_pool_priority.
По умолчанию, если thread_pool_priority установлено в значение auto, запросы будут иметь более высокий приоритет, если текущее подключение находится внутри транзакции. Это позволяет быстрее завершить текущую транзакцию и снижает количество одновременно выполняемых транзакций. Значение по умолчанию обычно улучшает пропускную способность для транзакционных рабочих нагрузок. Но также можно явно установить приоритет текущего подключения на «высокий» или «низкий».
Также существует механизм, гарантирующий, что подключения с высоким приоритетом не монополизируют рабочие потоки в пуле (что привело бы к неопределенным задержкам для подключений с низким приоритетом). В Unix-системах подключения с низким приоритетом помещаются в очередь с высоким приоритетом после таймаута, заданного системной переменной thread_pool_prio_kickup_timer.
Настройка дополнительного порта
MariaDB позволяет настроить дополнительный порт для административных подключений. Это предназначено в первую очередь для ситуаций, когда все потоки в пуле потоков заблокированы, и вам все еще нужен способ доступа к серверу. Однако это также можно использовать для того, чтобы системы мониторинга (включая мониторы MaxScale) всегда имели доступ к системе, даже когда все подключения на основном порту используются. Этот дополнительный порт использует старую обработку потоков one-thread-per-connection.
Вы можете включить его и настроить определенный порт, установив системную переменную extra_port.
Вы можете настроить определенное количество подключений для этого порта, установив системную переменную extra_max_connections.
Эти системные переменные можно установить в группе опций сервера группа опций в файле опций файл опций перед запуском сервера. Например:
[mariadb] ... extra_port = 8385 extra_max_connections = 10
После настройки дополнительного порта вы можете использовать клиент mariadb с опцией -P для подключения к порту.
$ mariadb -u root -P 8385 -p
Мониторинг активности пула потоков
В настоящее время для мониторинга активности пула доступны две переменные состояния.
| Переменная | Описание |
|---|---|
Threadpool_threads |
Количество потоков в пуле потоков. В редких случаях это значение может быть немного выше thread_pool_max_threads, потому что каждой группе потоков необходимо как минимум два потока (т. е. как минимум один рабочий поток и как минимум один поток-слушатель) для предотвращения тупиковых ситуаций. |
Threadpool_idle_threads |
Количество неактивных потоков в пуле потоков. Потоки становятся неактивными по разным причинам, например, ожидая новой работы. Однако неактивный поток не обязательно не получил работы. Потоки также считаются неактивными, если они заблокированы во время ожидания ввода-вывода с диска или ожидания блокировки и т. д. Эта переменная состояния имеет смысл только в Unix-системах. |
Группы потоков в реализации пула потоков в Unix
В Unix-системах реализация пула потоков использует объекты, называемые группами потоков, для разделения клиентских подключений на множество независимых наборов потоков. Дополнительную информацию см. в разделе Группы потоков в реализации пула потоков в Unix.
Устранение блокировки пула потоков
При использовании глобальных блокировок, даже при высоком значении системной переменной thread_pool_max_threads, все равно возможно заблокировать весь пул.
Представьте себе ситуацию, когда клиент выполняет FLUSH TABLES WITH READ LOCK, а затем приостанавливается. Если затем количество других клиентов, подключающихся к серверу для начала операций записи, превышает максимальное количество разрешенных потоков в пуле, это может заблокировать сервер. Это делает невозможным выполнение оператора UNLOCK TABLES. Это также может заблокировать MaxScale от мониторинга сервера.
Для устранения проблемы MariaDB позволяет настроить дополнительный порт для административных подключений. См. раздел Настройка дополнительного порта для получения информации о конфигурации.
После настройки дополнительного порта вы можете использовать клиент mariadb с опцией -P для подключения к порту.
$ mariadb -u root -P 8385 -p
Это гарантирует, что ваши администраторы могут получить доступ к серверу в тех случаях, когда количество потоков уже равно настроенному значению системной переменной thread_pool_max_threads, и все потоки заблокированы. Это также гарантирует, что MaxScale по-прежнему может получить доступ к серверу для получения информации о мониторинге.
После подключения к дополнительному порту вы можете решить проблему, увеличив значение системной переменной thread_pool_max_threads или убив виновное подключение (то есть подключение, которое удерживает глобальную блокировку, которое будет в состоянии sleep).
MariaDB Пул потоков против Oracle MySQL Enterprise Пула потоков
Коммерческие версии MySQL начиная с 5.5 включают Oracle MySQL Enterprise пул потоков, реализованный как плагин, который предоставляет аналогичную функциональность. Подробное обсуждение дизайна функции находится в блоге Mikael Ronstrom. Вот краткий обзор сходств и различий, основанный на приведенных выше материалах.
Сходства
- В Unix-системах как MariaDB, так и Oracle MySQL Enterprise пул потоков разделят клиентские подключения на группы. Параметр thread_pool_size таким образом имеет одинаковое значение как для MySQL, так и для MariaDB.
- Обе реализации используют аналогичную проверку схемы для обнаружения зависаний потоков, и обе имеют одинаковое имя параметра для thread_pool_stall_limit (хотя в MariaDB он измеряется в миллисекундах, а не в 10-миллисекундных единицах, как в Oracle MySQL).
Различия
- Реализация в Windows совершенно отличается - MariaDB использует родные средства пула потоков Windows, в то время как реализация Oracle включает удобную функцию WSAPoll() (предоставленная для удобства портирования приложений из Unix). Вследствие зависимости от WSAPoll(), реализация Oracle не будет работать с именованными каналами и подключениями общей памяти.
- MariaDB использует самые эффективные средства мультиплексирования ввода-вывода для каждой операционной системы: Windows (внутренне используется порт завершения ввода-вывода), Linux (epoll), Solaris (каналы событий), FreeBSD и OSX (kevent). Oracle использует оптимизированные средства мультиплексирования ввода-вывода только в Linux с epoll, в остальных случаях - poll().
- В отличие от Oracle MySQL Enterprise Пула потоков, пул потоков MariaDB встроен, а не является плагином.
MariaDB Пул потоков против Percona Пула потоков
Реализация Percona представляет собой порт пула потоков MariaDB с некоторыми добавленными функциями. В частности, Percona добавили планирование по приоритетам в свои версии 5.5-5.7. MariaDB 10.2 и Percona планирование по приоритетам работают аналогичным образом, но есть некоторые различия в деталях.
- В MariaDB, значения 10.2 thread_pool_priority=auto,high, low соответствуют значениям Percona thread_pool_high_prio_mode=transactions,statements,none
- Percona имеет переменную подключения thread_pool_high_prio_tickets, которая позволяет помещать каждые n-ю низкоприоритетную запросы в очередь высокого приоритета. В MariaDB нет соответствующих настроек.
- MariaDB имеет настройку thread_pool_prio_kickup_timer, которой нет в Percona.
Внутреннее устройство пула потоков
Подробная информация об реализации на низком уровне представлена в WL#246
Запуск бенчмарков
При запуске sysbench и, возможно, других бенчмарков, создающих множество потоков на том же компьютере, что и сервер, рекомендуется запускать драйвер бенчмарка и сервер на разных процессорах для получения реальных результатов. Запуск большого количества потоков драйвера и только нескольких потоков сервера на одних и тех же процессорах приведет к тому, что планировщик операционной системы будет назначать потоки драйвера бенчмарка с гораздо большей вероятностью, чем потоки сервера, то есть драйвер будет прерывать сервер. Для разделения процессоров драйвера бенчмарка и сервера используйте «taskset –c» на Linux или «set /affinity» на Windows, как предпочтительный метод решения этой ситуации.
Возможная альтернатива на Unix (если taskset или отдельный компьютер для запуска бенчмарка по какой-либо причине нежелательны) заключается в увеличении thread_pool_size, чтобы сделать потоки сервера более «конкурентноспособными» по отношению к потокам клиента.
При запуске sysbench хорошим правилом является выделение 1/4 всех процессоров для sysbench и 3/4 для mysqld. Также желательно запускать sysbench и mysqld на разных NUMA-узлах, если это возможно.
Примечания
Переменная сервера thread_cache_size не используется при использовании пула потоков, и переменная состояния Threads_cached будет иметь значение 0.
См. также
© 2023 MariaDB
Licensed under the Creative Commons Attribution 3.0 Unported License and the GNU Free Documentation License.
https://mariadb.com/kb/en/thread-pool-in-mariadb/