В базе данных проекта хранились разные интересные данные.
Например:
Программисту понадобилось произвести над каждой записью в этой таблице некоторое довольно тяжелое вычисление.
Для увеличения скорости программист решил запустить обработку в несколько процессов:
И все бы хорошо, но в логах видно кое-что странное
с распределением работы по воркерам.
Что же случилось?
Программист попробовал поменять количество воркеров.
При 8 рабочих процессах почти все записи снова достались
нулевому, при 6 – нулевому, второму и четвертому,
а между 5 воркерами записи распределились
равномерно.
Неравномерное распределение записей по рабочим процессам происходит из-за сочетания двух обстоятельств:
При подстановке в запрос через плейсхолдеры (where movie_hash % ? = ?) переменные заключаются в кавычки (получается where movie_hash % '4' = '3').
При выполнении операций над целыми числами, заключенными в кавычки, mysql приводит их к вещественным,
и операции производит как с вещественными.
А точность вещественных чисел ограничена, и на больших
целых ее перестает хватать – младшие биты
больших целых, превращенных во float’ы, распределены
неравномерно.
Сравните:
Цитата из
документации mysql
(описывается приведение при сравнении, но при других операциях аналогично):
The following rules describe how conversion occurs for comparison operations:
If one or both arguments are NULL … – не наш случай
If both arguments in a comparison operation are strings … – не наш случай
If both arguments are integers, they are compared as integers. – не наш случай, у нас второй аргумент – строка
Hexadecimal values … – не наш случай
If one of the arguments is a TIMESTAMP or DATETIME – не наш случай
If one of the arguments is a decimal value – не наш случай
In all other cases, the arguments are compared as floating-point (real) numbers. – мы попадаем сюда, что подтверждается последним запросом с делением на 6e0.
Чтобы все же обеспечить равномерное распределение работы, программист мог бы воспользоваться
либо непосредственной подстановкой переменных в текст запроса (where movie_hash % $total = $num),
либо воспользоваться конструкцией cast(? as unsigned).