Как переменная thread_stack связана с количеством UNION в запросе? - PullRequest
0 голосов
/ 13 июля 2020

Для ускорения обновлений я использую трюк UNION:

UPDATE shop_items t
INNER JOIN (
    SELECT 0 id, 0 title
    UNION ALL
    SELECT 1, 'text1'
    UNION ALL
    SELECT 2, 'text2'
    ...
) data ON data.id = t.id
SET
t.id    = data.id,
t.title = data.title

Он отлично работает для запросов длиной около 1 МБ, но после увеличения размера он начинает сбоить с ошибкой:

ОШИБКА 1436 (HY000) в строке 1: Переполнение стека потоков: из стека 286720 байт использовано 266736 байт, необходимо 20000 байт. Используйте mysqld --thread_stack = #, чтобы указать больший стек.

Я, конечно, могу «указать больший стек» и найти подходящее значение для каждой указанной длины запроса c путем простого предположения, но Я считаю этот подход неуместным: если в процессе разработки вы что-то делаете, но не знаете, как именно это работает, вы можете получить плохой результат, и в будущем код будет случайно давать сбой.

MySQL docs говорит, что :

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

Что не очень помогает.

Итак, существует ли прямая и вычислимая связь thread_stack со сложностью запросов с объединениями? Может быть, длина запроса или количество UNION? Если я знаю правила, я могу соответствующим образом ограничить сложность запросов.

Ответы [ 2 ]

1 голос
/ 13 июля 2020

Весьма вероятно, что каждый SELECT в вашем UNION порождает поток. Тем не менее, это не та деталь, которую MySQL раскрывает разборчиво, и я бы не рекомендовал разрабатывать программное обеспечение, основанное на попытке сопоставить детали внутренней реализации, которые могут измениться без предупреждения в будущих версиях. Если вы полны решимости использовать этот «трюк», я бы сказал, ограничьте сложность генерируемого с его помощью запроса до уровня значительно ниже того, где вы начнете видеть проблемы.

0 голосов
/ 13 июля 2020

Ответ, предоставленный chaos , был правильным: запросы ограничены количеством SELECT / UNION, а не общим количеством символов. Я попытался удалить все тексты из своего запроса, уменьшив его с 1 МБ до 100 КБ, но MySQL все равно отказался его принять. Но как только я уменьшил количество строк до предела, который мы пытаемся найти, запрос начал работать.

И вот полный ответ, который я вывел из наблюдений, экспериментов и немного математического шаманизма.

По умолчанию MySQL значение для thread_stack равно 286 720 bytes, что означает 262 KiB.

С этим значением я могу создать запрос с 4076 SELECTs: одна база SELECT + 4075 UNIONs.

4076 это почти 4096, без 20. Это означает, что кто-то украл число, равное 20 SELECTs.

286 720 (262 KiB) это почти 262 144 (256 KiB). Похоже, что MySQL хочет эту 24 576 (6 KiB) для своих теневых целей.

Давайте разделим одно почти на другое почти: 262 144 / 4096 = 64. Кажется, для одного SELECT требуется 64 bytes памяти.

Тогда мы можем изобрести формулу: (thread_stack - shady MySQL hoard - stolen 20 SELECTs) / bytes per SELECT = SELECTs available to us

Если по умолчанию 286720 байт: (286 720 - 24 576 - 1 280) / 64 = 4076

Затем я проверил это с настройкой thread_stack=131072 в файле конфигурации. Это 128 KiB. Вот математика: (131 072 - 24 576 - 1 280) / 64 = 1644

Я отредактировал свой запрос, чтобы он содержал 1644 SELECTs. Он работал без проблем.

Затем добавил еще один маленький UNION + SELECT и сразу получил:

ERROR 1436 (HY000) в строке 1: Переполнение стека потоков: использовано 111088 байт из стек 131072 байта и требуется 20000 байтов. Используйте mysqld --thread_stack = #, чтобы указать больший стек.

Похоже, что сейчас формула работает правильно. Но сработает ли это в будущем - возможно, но это не более чем предположение.

...