Комментарии 35
> траблшутинге перформанс ишшусов
че?!
че?!
Какое-то двоякое ощущение от статьи: с одной стороны вроде умные вещи про непосредственно миграцию с MyISAM на InnoDB (хотя тут я не специалист). А с другой — странные php советы.
Во-первых, использование mysqli_query.
Во-вторых, for для повторения запросов. Мне кажется правильнее все-таки падать с ошибкой, а не долбить пока не получится. Ну или если и использовать такой подход, то далеко не везде.
В-третьих — не вижу никакого преимущества в использовании &$result. Только ухудшает читаемость кода.
Во-первых, использование mysqli_query.
Во-вторых, for для повторения запросов. Мне кажется правильнее все-таки падать с ошибкой, а не долбить пока не получится. Ну или если и использовать такой подход, то далеко не везде.
В-третьих — не вижу никакого преимущества в использовании &$result. Только ухудшает читаемость кода.
Ах ну да, еще и переменные то с маленькой буквы, то с большой.
Normally, you must write your applications so that they are always prepared to re-issue a transaction if it gets rolled back because of a deadlock.
dev.mysql.com/doc/refman/5.5/en/innodb-deadlocks.html
В-третьих — не вижу никакого преимущества в использовании &$result.
Тогда:
— Внутри функции my_mysqli_query создается локальная переменная $result.
— Ей присваивается значение, выбранное из базы (это могут быть сотни строк)
— этот объект возвращается в вызывающую функцию копирующим конструктором:
$Result = my_mysqli_query($link, $query);
— при этом происходит передача значения через стэк, а локальная переменная $result выхоит из cкоупа и разрушается (вызывается деструктор объекта).
Т.е. происходит лишняя операция создания объекта, копирования и разрушения объекта при каждом вызове my_mysqli_query().
Если конечно внутри PHP не реализованы смарт объекты с поддержкой референсов на их юсадж каунт, о чем я не знаю, поэтому полагаться на эту гипотетическую возможность не хочу. Поэтому сужу по опыту из языка С.
Кстати, если бы эти smart objects они были реализованы — то не существовало/не требовалось бы в PHP оператора передачи переменной по ссылке (&), это было бы лишним.
Тогда:
— Внутри функции my_mysqli_query создается локальная переменная $result.
— Ей присваивается значение, выбранное из базы (это могут быть сотни строк)
— этот объект возвращается в вызывающую функцию копирующим конструктором:
$Result = my_mysqli_query($link, $query);
— при этом происходит передача значения через стэк, а локальная переменная $result выхоит из cкоупа и разрушается (вызывается деструктор объекта).
Т.е. происходит лишняя операция создания объекта, копирования и разрушения объекта при каждом вызове my_mysqli_query().
Если конечно внутри PHP не реализованы смарт объекты с поддержкой референсов на их юсадж каунт, о чем я не знаю, поэтому полагаться на эту гипотетическую возможность не хочу. Поэтому сужу по опыту из языка С.
Кстати, если бы эти smart objects они были реализованы — то не существовало/не требовалось бы в PHP оператора передачи переменной по ссылке (&), это было бы лишним.
Гуглиться за одну минуту: stackoverflow.com/questions/2715026/are-php5-objects-passed-by-reference
Ну во-первых, все объекты в php передаются по-ссылке (хотя в аналогии с С скорее в виде указателей).
Во-вторых даже если бы происходило копирование, то это явно не самое узкое место этой функции. Что-то мне подсказывает, что основное время уйдет на выполнение самого запроса.
Во-вторых даже если бы происходило копирование, то это явно не самое узкое место этой функции. Что-то мне подсказывает, что основное время уйдет на выполнение самого запроса.
Статья не про php.
Смысл кода понятен, любой пхп-шник за минуты адаптирует его под свой фреймворк/движок/стандарт за считанные минуты.
Спасибо за статью!
Смысл кода понятен, любой пхп-шник за минуты адаптирует его под свой фреймворк/движок/стандарт за считанные минуты.
Спасибо за статью!
Цель статьи была не в том, чтобы научить меня азам PHP. Но за ссылку спасибо.
Отсюда вывод — нужно отступать от классической теории реляцонных баз данных, и выделять большие по размеру столбцы (MEDIUMTEXT например) в отдельные таблицы, если данные в них меняются реже, чем остальные атрибуты в этой сущности.
Ну вообще в выделении больших по размеру столбцов в отдельные таблицы я не вижу ничего плохого, окромя хорошего. Особенно если в «основной» таблице у вас много полей. Только к размеру row image в binary log это отношения не имеет. То, чего вы добиваетесь, нужно делать установкой опции binlog-row-image либо в minimal, либо в noblob.
innodb_log_file_size к «подводному камню №3» отношения тоже не имеет. Это не тоже самое, что binary log. И формат лога в данном случае называется ROW, а не MIXED. MIXED значит, что мы всё, что можно, пишем в STATEMENT, а что нельзя — в ROW.
# этот размер выставляем в 50-80% от размера всей оперативной памяти у сервера БД.
innodb_buffer_pool_size = 512M
Этот совет взят из мануала или шаблона конфигурации по умолчанию, но на данный момент ведутся разговоры, что он устарел в связи с резко возросшим количеством памяти на боевых серверах. Правильный совет: innodb_buffer_pool_size дожен вмещать все активные данные (грубо говоря суммарный размер таблиц, которыми вы пользуетесь). И вот если он больше размера доступной памяти, тогда пользоваться правилом 50-80%
Долбить запросом, пока он не выполнится или не пройдет 10 (кстати почему именно 10?) попыток это пять.
Следующим шагом рекомендую аппаратный перезагрузчик сервака на основе анализа звука кулеров.
Следующим шагом рекомендую аппаратный перезагрузчик сервака на основе анализа звука кулеров.
Читайте MySQL документацию, и не учите меня жить) Там написано, что именно нужно всего лишь повторить запрос. Повторный запрос попадет в другие условия на базе (дедлока уже не будет, конфликтующая транзакция уже либо выполнится, либо захватит локи), поэтому повторный запрос либо выполнится успешно сразу, либо немного посидит на локах.
10 — для перестраховки. Я же не зря логгирую это — и по логам, все дедлоки у меня резолвятся со второй попытки.
10 — для перестраховки. Я же не зря логгирую это — и по логам, все дедлоки у меня резолвятся со второй попытки.
Мне понятно зачем вы это делаете, но всё равно выглядит именно цикл странно. Было бы лучше написать, что транзакцию можно повторить и один из способов сделать это в цикле. Также объяснить почему именно столько повторений и что в другом случае их может быть, например 42
ага, убрать магическое число в дефайн
Лучше rand-ом инициализировать =)
Да зачем тут ранд-то? Число — впролне характеризует «надёжность» — чем больше итераций, тем выше шанс, что сервер всё-таки выполнит запрос.
Ранд тут уместен как задержка между итерациями цикла, чтобы не получилось, что два таких цикла друг друга забивают.
Кстати, в первом if ($result) можно не breakом завершать цикл, а сразу return $result. Зачем дорабатывать функцию зря? :)
Ранд тут уместен как задержка между итерациями цикла, чтобы не получилось, что два таких цикла друг друга забивают.
Кстати, в первом if ($result) можно не breakом завершать цикл, а сразу return $result. Зачем дорабатывать функцию зря? :)
Теория одного return из функции. Старая школа.
Шутка.
Просто это же зависит от приложения. Например, в одном случае мы можем повторять попытки до бесконечности, в другом до достижения какого-то timeout (или количества попыток как в статье). Соответственно возникает вопрос: почему 10, а не что-то ещё. В независимости в дефайне ли число или hard-coded.
Просто это же зависит от приложения. Например, в одном случае мы можем повторять попытки до бесконечности, в другом до достижения какого-то timeout (или количества попыток как в статье). Соответственно возникает вопрос: почему 10, а не что-то ещё. В независимости в дефайне ли число или hard-coded.
10 — чтобы было, и чтобы было больше 2, но меньше бесконечности.
Реальное число retries = 2 (со второй попытки все происходит успешно).
Всё, разумеется, анализируется, и в случае появления 10-й попытки перезапустить транзакцию (в логах) будут применяться уже другие меры — к коду, к порядку написания запросов, и т.д.
Реальное число retries = 2 (со второй попытки все происходит успешно).
Всё, разумеется, анализируется, и в случае появления 10-й попытки перезапустить транзакцию (в логах) будут применяться уже другие меры — к коду, к порядку написания запросов, и т.д.
Вообще, десятикратным автоповтором для любого дедлока вы можете сами себя заDOSить при определенной нагрузке.
Не буду утверждать, что это однозначно плохое решение, но я бы не рекомендовал для бездумного повторения. В крайнем случае — ставить не 10 а 2.
Вы же сами пишите, что всегда проходит со второй попытки, зачем десять-то делать?
И, да, в своих проектах я на дедлоках просто прерываю исполнение и откатываю всю транзакцию (радуя пользователя ошибкой, да).
Но, обязательно кидаю сообщение себе на почту и, получив такое сообщение, сразу начинаю расследование.
Любой дедлок — это ошибка в структуре данных и/или коде и должен устраняться.
Не буду утверждать, что это однозначно плохое решение, но я бы не рекомендовал для бездумного повторения. В крайнем случае — ставить не 10 а 2.
Вы же сами пишите, что всегда проходит со второй попытки, зачем десять-то делать?
И, да, в своих проектах я на дедлоках просто прерываю исполнение и откатываю всю транзакцию (радуя пользователя ошибкой, да).
Но, обязательно кидаю сообщение себе на почту и, получив такое сообщение, сразу начинаю расследование.
Любой дедлок — это ошибка в структуре данных и/или коде и должен устраняться.
Здесь не нужен таймаут, он уже есть в БД, в логике разрешения деадлоков. Если таковой возник, БД сама по своему внутреннему таймеру выбьет обе транзакции с неудачей.
Выбивается только более «легкая» транзакция, и сразу же, моментально, как только был обнаружен дедлок. Никакого таймаута там нет.
Такое чувство, что слово «deadlock» народ неправильно понимает — это не тот случай, когда две транзакции, как бараны, стоят и смотрят друг на друга часами.
Такое чувство, что слово «deadlock» народ неправильно понимает — это не тот случай, когда две транзакции, как бараны, стоят и смотрят друг на друга часами.
Есть подозрение, что вы и термин deadlock применяете не на месте. Потому, что это именно когда две транзакции как бараны не могут разойтись. Обеим нужны ресурсы А и Б, первая захватила А и ждёт, когда освободится Б, а вторая — захватила Б и ждёт, когда освободится А. И всё, стоят и ждут, своё держат, и никуда не продвинутся, пока кто-то не вмешается.
Я действительно погорячился, для решения проблемы действительно достаточно убить одну (любую) из них. Таймаут в свою очередь не так нужен для обнаружения дедлоков, которые можно находить анализом ситуации, как для лайвлоков, когда ситуация меняется, но не продвигается. Пример лайвлока — два человека не могут разойтись в корридоре, каждый пытается уступить дорогу другому и в результате они снова сталкиваются.
Как бы то ни было, мой основной посыл — что логика с таймаутом всё равно более уместна в коде разрешения блокировок в базе, чем в коде, который рестартует транзакции — остаётся в силе.
Я действительно погорячился, для решения проблемы действительно достаточно убить одну (любую) из них. Таймаут в свою очередь не так нужен для обнаружения дедлоков, которые можно находить анализом ситуации, как для лайвлоков, когда ситуация меняется, но не продвигается. Пример лайвлока — два человека не могут разойтись в корридоре, каждый пытается уступить дорогу другому и в результате они снова сталкиваются.
Как бы то ни было, мой основной посыл — что логика с таймаутом всё равно более уместна в коде разрешения блокировок в базе, чем в коде, который рестартует транзакции — остаётся в силе.
Эмм… Там так и написано)
которая будет повторять откаченную транзакцию, например так:
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Правильная миграция с MyISAM на InnoDB