Няма грешка и не мисля, че разбирате нещо погрешно; пропускате само няколко части от пъзела.
Външните ключове се изпълняват вътрешно чрез заключване на ниво ред; започвайки от Postgres 8.1 и до 9.2, когато актуализирате референтната таблица (apples
в този случай), се задейства заявка, която прави SELECT FOR SHARE
върху таблицата с препратки (trees
). Така че SELECT FOR UPDATE
в първата транзакция блокира SELECT FOR SHARE
на референтната цялост за втората транзакция. Това е причината за блокирането във втората команда.
Сега те чувам да крещиш:„Чакай! Защо блокира на втората команда, а не на първата? Обяснението е просто, наистина - това е само защото има проста оптимизация, която пропуска вътрешния SELECT FOR SHARE
когато ключът не се променя. Това обаче е опростено, тъй като ако актуализирате кортеж втори път, тази оптимизация няма да се задейства, защото е по-трудно да се проследят оригиналните стойности. Оттук и блокирането.
Може също да се чудите защо казах, че това е до 9.2 --- какво става с 9.3? Основната разлика е, че в 9.3 той използва SELECT FOR KEY SHARE
, което е ново, по-леко ниво на заключване; позволява по-добра едновременност. Ако опитате вашия пример в 9.3 и също промените SELECT FOR UPDATE
до SELECT FOR NO KEY UPDATE
(който е по-лек режим от SELECT FOR UPDATE
което казва, че може би ще актуализирате кортежа, но обещавате да не променяте първичния ключ и обещавате да не го изтривате), трябва да видите, че не блокира. (Също така можете да опитате АКТУАЛИЗАЦИЯ на посочения ред и ако не промените първичния ключ, той също няма да блокира.)
Тези неща за 9.3 бяха въведени чрез кръпка от вас наистина като http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=0ac5ad5134f2769ccbaefec73844f8504c4d6182 и мисля, че беше доста готин хак (Съобщението за ангажиране има още подробности, ако ви интересуват такива неща). Но внимавайте, не използвайте версии преди 9.3.4, тъй като тази корекция беше толкова изключително сложна, че няколко сериозни грешки останаха незабелязани и ние поправихме едва наскоро.