В чем практические различия между `REPLACE` и` INSERT… ON DUPLICATE KEY UPDATE` в MySQL?

81

Мне нужно установить значения всех полей записи с определенным ключом (на самом деле ключ является составным), вставив запись, если записи с таким ключом еще нет.

REPLACEкажется, что он предназначен для работы, но в то же время его справочная страница предлагает INSERT ... ON DUPLICATE KEY UPDATE.

Что из них лучше выбрать и почему?

Единственный «побочный эффект» REPLACEэтого приходит мне на ум - это увеличивает значения автоинкремента (к счастью, я их не использую), хотя, INSERT ... ON DUPLICATE KEY UPDATEвероятно, не будет. Какие еще практические отличия следует учитывать? В каких конкретных случаях можно REPLACEпредпочесть INSERT ... ON DUPLICATE KEY UPDATEи наоборот?

Иван
источник
INSERT ... ON DUPLICATE KEY UPDATE фактически также увеличивает счетчик автоинкремента. Не для обновляемой записи, а для следующей вставленной записи. Таким образом, если наивысший идентификатор равен 10, и вы выполняете дублирующую вставку, а затем вставляете новое уникальное значение, идентификатор этой строки станет 12.
marlar

Ответы:

117

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

Использование INSERT ... ON DUPLICATE KEY UPDATEпозволяет избежать этой проблемы и поэтому является предпочтительным.

Марк Байерс
источник
1
Хороший ответ, но в моем случае эта проблема решена не будет. Шанс столкновения можно считать 50/50. Что мне тогда выбрать? И поскольку INSERT ... ON DUPLICATE KEY UPDATEвыглядит значительно «лучше», то в каких случаях «ЗАМЕНИТЬ» может быть лучшим выбором?
Иван
3
Я провел небольшое исследование и, насколько я могу судить, нет общей причины использовать REPLACE вместо INSERT ... ON DUPLICATE KEY UPDATE. По сути, это устаревшая функция. Если нет какой-либо конкретной причины, по которой ваш код полагается на удаление и повторное добавление строк с соответствующими эффектами на индексы и значения автоинкремента, похоже, нет никаких причин для его использования.
Натан Стретч
2
Вкл. REPLACEОбновит значение автоматического приращения вашего PK, если оно делает DELETEи INSERT. Именно этого я и хочу. Я не хочу, чтобы потребитель нашел запись под одним и тем же ПК, поэтому они не получают строк. Когда я хочу, чтобы они его нашли (актуальное обновление), я используюUPDATE
radtek
Итак, другая половина вопроса: когда вы предпочтете REPLACEбольше INSERT ... ON DUPLICATE KEY UPDATE? Почему INSERT+ DELETEкогда-либо предпочтительнее UPDATE?
LemonPi
59

Чтобы ответить на вопрос с точки зрения производительности, я провел тест, используя оба метода

Замена в включает в себя:
1. Попытайтесь вставить в таблицу
2. Если 1 не удается, удалить строку и вставить новую строку

Вставка при дублировании ключа Обновление включает в себя:
1. Попытку вставить в таблицу
2. Если 1 не удается, обновить строку

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

Я пробовал оба утверждения в моей таблице InnoDB, содержащие 62 510 записей (только обновления). На скорости набора:
Заменить на: 77,411 секунды
Вставить при дублировании ключа Обновление: 2,446 секунды

Insert on Duplicate Key update is almost 32 times faster.

Размер таблицы: 1249250 строк с 12 столбцами на Amazon m3.medium.

Катрикс
источник
Крутая статистика, пробовали Insert on Duplicate Key Replace? Это было медленнее?
radtek
@radtek вы можете только писать ON DUPLICATE KEY UPDATE, вы не можете писать ON DUPLICATE KEY REPLACE. Если вы хотите обновить все значения существующей строки при дублировании ключа, вы должны написать ON DUPLICATE KEY UPDATE col1=VALUES(col1), col2=VALUES(col2), ...- вам нужно перечислить все столбцы вручную.
izogfif
Я знаю, что просто спрашивал, что было быстрее и похоже на обновление.
radtek
9

При использовании REPLACEвместо INSERT ... ON DUPLICATE KEY UPDATEя иногда наблюдаю проблемы с блокировкой ключей или взаимоблокировкой, когда для данного ключа быстро поступает несколько запросов. Атомарность последнего (помимо того, что оно не вызывает каскадных удалений) - еще одна причина для его использования.

Эндрю Мао
источник
3

Если вы не перечислите все столбцы, я думаю, REPLACEчто все неупомянутые столбцы сбросят их значения по умолчанию в замененных строках. ON DUPLICATE KEY UPDATEоставит неупомянутые столбцы без изменений.

Barmar
источник
3

В каких конкретных случаях REPLACE может быть предпочтительнее INSERT ... ON DUPLICATE KEY UPDATE и наоборот?

Я только что на собственном опыте выяснил, что в случае таблиц с системой хранения FEDERATED INSERT...ON DUPLICATE KEY UPDATEоператоры принимаются, но терпят неудачу (с ошибкой 1022: не могу записать; дублирующий ключ в таблице ...), если дублирующийся ключ происходит нарушение - см. соответствующий маркер на этой странице Справочного руководства MySQL.

К счастью, я смог использовать его REPLACEвместо INSERT...ON DUPLICATE KEY UPDATEсвоего триггера после вставки для достижения желаемого результата репликации изменений в FEDERATED-таблицу.

w5m
источник
2

Replace кажется, что он выполняет две операции в случае, если ключ уже существует. Возможно, это означает, что между ними есть разница в скорости?

(INSERT) одно обновление против одного удаления + одна вставка (REPLACE)

РЕДАКТИРОВАТЬ: Мое предположение, что замена может быть медленнее, на самом деле совершенно неверно. Ну, в любом случае, согласно этому сообщению в блоге ... http://www.tokutek.com/2010/07/why-insert-on-duplicate-key-update-may-be-slow-by-incurring-disk-seeks /

Исаак Файф
источник
1

«Возможно, что в случае ошибки дублирования ключа, механизм хранения может выполнить REPLACE как обновление, а не удаление плюс вставку, но семантика останется той же».

http://dev.mysql.com/doc/refman/5.7/en/replace.html

user3522775
источник
0

REPLACE иногда кажется необходимым, потому что INSERT IGNORE не работает с преобразованиями данных.

Если я сделаю это, я установлю только самый большойCityPop для себя:

ВСТАВИТЬ ИГНОРИРОВАНИЕ В Крупнейшие города (stateID, largeCityPop, statePop) ВЫБЕРИТЕ stateID, MAX (city.pop) как largeCityPop, state.pop ИЗ города ПРИСОЕДИНЯЙТЕСЬ к состоянию на city.stateID = state.ID ГРУППА ПО city.stateID ПРИ ДУБЛИКАЦИИ КЛЮЧЕВОГО ОБНОВЛЕНИЯ largeCityPop = largeCityPop

Если я это сделаю, я неправильно использую функцию GROUP:

ВСТАВИТЬ ИГНОРИРОВАНИЕ В Крупнейшие города (stateID, largeCityPop, statePop) ВЫБЕРИТЕ stateID, MAX (city.pop) как largeCityPop, state.pop ИЗ города ПРИСОЕДИНЯЙТЕСЬ к состоянию на city.stateID = state.ID ГРУППА ПО city.stateID НА ДУБЛИКАТЕ КЛЮЧЕВОГО ОБНОВЛЕНИЯ largeCityPop = MAX (город. поп)

И если я это сделаю, MySQL не распознает имя столбца:

ВСТАВИТЬ ИГНОРИРОВАНИЕ В Крупнейшие города (stateID, largeCityPop, statePop) ВЫБЕРИТЕ stateID, MAX (city.pop) как largeCityPop, state.pop ИЗ города ПРИСОЕДИНЯЙТЕСЬ к состоянию на city.stateID = state.ID ГРУППА ПО city.stateID НА ДУБЛИКАЦИОННОМ ОБНОВЛЕНИИ КЛЮЧА largeCityPop = city .largestCityPop

Это работает, но кажется просто уродливым:

ВСТАВИТЬ ИГНОРИРОВАНИЕ В Крупнейшие города (stateID, largeCityPop, statePop) ВЫБРАТЬ * ИЗ (ВЫБЕРИТЬ stateID, MAX (city.pop) как BigCityPop, state.pop FROM city JOIN state on city.stateID = state.ID BY city.stateID) x ON ДУБЛИКАЦИОННОЕ ОБНОВЛЕНИЕ КЛЮЧА largeCityPop = BigCityPop

Дэн Марш
источник
Осторожно: INSERT IGNOREзапрос завершится успешно (и выдаст предупреждение), если внешнее ограничение не сработает ! Если вы хотите поймать такую ​​ошибку, лучше используйте ON DUPLICATE KEY UPDATEбез IGNORE.
izogfif