Чи оновлення рядка з тим самим значенням насправді оновлює рядок?


28

У мене є питання щодо продуктивності. Скажімо, у мене є користувач з прізвищем Майкл. Візьміть наступний запит:

UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123

Чи справді запит виконає оновлення, навіть якщо воно оновлюється до однакового значення? Якщо так, то як я можу запобігти цьому?


1
Чому б ви виконували операцію і одночасно очікували, що вона не буде виконуватися?
Макс Вернон

@MaxVernon Ruby на ORM Rails не оновлює запис, тому мені було цікаво, якщо PostgreSQL зробив те саме.
OneSneakyMofo

1
Я б запропонував, якщо це робить Ruby on Rails, він, ймовірно, спочатку робить вибір, щоб побачити, чи потрібен рядок оновлення.
Макс Вернон

Відповіді:


35

Завдяки моделі MVCC Postgres і відповідно до правил SQL, a UPDATEпише нову версію рядків для кожного рядка, яка не виключається в WHEREпункті.

Це робить мають більш-менш істотний вплив на продуктивність, прямо і побічно. "Порожні оновлення" мають таку саму ціну за рядок, як і будь-яке інше оновлення. Вони спрацьовують триггерами (якщо вони є), як і будь-яке інше оновлення, вони мають бути зафіксованими WAL, і вони створюють мертві рядки, що роздувають таблицю і створюють більше роботи для VACUUMподальшого, як і будь-яке інше оновлення.

Записи індексів та стовпців TOASTed, де жоден із залучених стовпців не змінено, можуть залишатися однаковими, але це справедливо для будь-якого оновленого рядка. Пов'язані:

Майже завжди добре виключати такі порожні оновлення (коли є фактичний шанс, що це може статися). Ви не вказали визначення таблиці у своєму запитанні (що завжди гарна ідея). Ми повинні припустити, що це first_nameможе бути NULL (що не буде дивно для "імені"), тому запит повинен використовувати порівняння, безпечне для NULL :

UPDATE users
SET    first_name = 'Michael'
WHERE  id = 123
AND   first_name IS DISTINCT FROM 'Michael';

Якщо first_name IS NULLперед оновленням, тест з just first_name <> 'Michael'би оцінив NULL і виключив рядок із оновлення. Хитра помилка. Якщо стовпець визначенийNOT NULL , використовуйте просту перевірку рівності, оскільки це трохи дешевше.

Пов'язані:


1
Indexes entries and TOASTed columns where none of the involved columns are changed can stay the sameАле чи не доведеться їх оновлювати, щоб вказувати на нове місце рядка?
dvtan

1
@dtgq: Не з актуальними оновленнями HOT, де індекс може постійно вказувати на старе місце розташування, і купі доводиться проходити ланцюжок HOT, щоб отримати живий кортеж. Я додав посилання на додаткові пояснення вище.
Ервін Брандштеттер

1
Що про MVCC вимагає оновлення noop, щоб написати новий кортеж?
jberryman

@jberryman: Не впевнений, що я розумію. У будь-якому випадку, будь ласка, поставте своє запитання як нове запитання . Ви завжди можете зв’язатись із цим для контексту. І ви можете залишити коментар тут, щоб посилання назад (і привернути мою увагу).
Ервін Брандстеттер

2
@jberryman: Насправді я не знаю причин, чому проект пішов саме таким шляхом. Це було встановлено давно. Але я припускаю, що було б зайво дорого перевіряти кожен рядок на рівність і мати окремий шлях коду для незмінних рядків. Поводження з ідентифікаторами транзакцій було б складніше - спеціальний корпус для rollbackобробки знімків, управління замком, WAL, а що ні ...
Ервін Брандстеттер

4

ORM, як пропозиція Ruby on Rail, відкладає виконання, яке позначає запис як змінений (або ні), а потім, коли це потрібно або викликається, потім подає зміни в базу даних.

PostgreSQL - це база даних, а не ORM. Це знизило б ефективність, якби знадобився час, щоб перевірити, чи нове значення збігається з оновленим значенням у вашому запиті.

Тому воно оновить значення незалежно від того, є воно таким же, як нове значення чи ні.

Якщо ви хочете запобігти цьому, ви можете використовувати код, як Макс Вернон, запропонований у своїй відповіді.


2

Ви можете просто додати до whereпункту:

UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123
    AND (first_name <> 'Michael' OR first_name IS NULL);

Якщо first_nameвизначено якNOT NULL , OR first_name IS NULLдеталь можна видалити.

Умова:

(first_name <> 'Michael' OR first_name IS NULL)

також може бути написано більш елегантно як (у відповідь Ервіна):

first_name IS DISTINCT FROM 'Michael'

Не знаючи, чи може стовпець бути NULL, це може ввести підлий помилку.
Ервін Брандстеттер

1
@ErwinBrandstetter Відповідь я оновлював - тоді я побачив коментар та вашу відповідь!
ypercubeᵀᴹ

дякую за редагування, @ypercube - і за коментар про NULL @erwin
Макс Вернон

1

З точки зору бази даних

Відповідь на ваше запитання ТАК. Оновлення відбудеться. База даних не перевіряє попереднє значення, вона лише встановлює нове значення.

Оскільки це відбувається в пам'яті (і буде записано у файли даних лише після видачі комісії), продуктивність не буде проблемою.

З точки зору ORM

Зазвичай у вас буде Об'єкт, що представляє один рядок бази даних (це може бути набагато складніше, ніж це, але давайте будемо простою). Цим об'єктом керується пам'ять (на рівні сервера додатків), і лише остання зафіксована версія цього об’єкта фактично перенесе його до бази даних у певний момент.

Це може пояснити різну поведінку.

Тепер не будемо порівнювати вантажне судно з 3D-принтером. Те, що ви можете надсилати 3D-принтери за допомогою вантажних суден, не означає, що між ними може бути якесь порівняння.

Насолоджуйтесь!

Я сподіваюся, що це прояснило деякі концепції.


4
Виконання є і питання. Кожне оновлення має бути записане на диск (журнал і таблиця).
ypercubeᵀᴹ

Це залежатиме від фактичного RDBMS, який ви використовуєте. Але більшість з них не здійснює кожне оновлення, а лише останній зафіксований блок, який вони мають у пам'яті. Ви ніколи не читаєте і не записуєте жодного рядка в базу даних. Ви читаєте / записуєте блоки та зберігаєте їх у пам’яті, поки вам не доведеться розмивати його, щоб поставити новий блок на тому ж місці. Незважаючи на те, що в пам’яті не всі зміни в рядку записуються на диск, а лише вміст блоку, коли в процесі «запису баз даних» подається сигнал про скидання цього блоку пам'яті у файл даних. Отже, ні ... Це не проблема, якщо ваша програма не надто довго тримає блок невпорядкованим.
Сільваріон

1
питання стосується Postgres, а не про будь-які довільні СУБД. І хоча оновлення не всі повинні писатися по одному, кожне записування в базі даних повинно бути записане в журнал. Якщо зміна не записана на постійному сховищі, як СУБД переживе збій у системі?
ypercubeᵀᴹ

Так, це записується в журнали, з пам'яті, а також під час контрольних пунктів. Якщо у вас є надзвичайно величезна кількість одночасних користувачів, це взагалі не повинно бути проблемою. Журнали записуються також партіями. Я думаю, ми говоримо про сервери. Якщо ви говорите про базу даних Postgres на ноутбуці з жорстким диском 5400RPM, так ... у вас завжди будуть проблеми з продуктивністю. Отже, остаточна відповідь була б першою ... Це залежить від занадто багато речей.
Сільваріон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.