TL;DR:Откриването на конфликти за сериализиране се подобри драстично в Pg 9.1, така че надстройте.
Трудно е да разберете от вашето описание какъв е действителният SQL и защо очаквате да получите връщане назад. Изглежда, че сте разбрали сериозно погрешно сериализируемата изолация, може би смятайки, че тества перфектно всички предикати, което не е така, особено не в Pg 8.4.
SERIALIZABLE
не гарантира напълно, че транзакциите се изпълняват така, сякаш се изпълняват последователно - тъй като това би било прекалено скъпо от гледна точка на производителността, ако изобщо беше възможно. Предоставя само ограничена проверка. Какво точно се проверява и как варира от база данни до база данни и версия до версия, така че трябва да прочетете документите за вашата версия на вашата база данни.
Възможни са аномалии, когато две транзакции се изпълняват в SERIALIZABLE
режим дава различен резултат от този, ако тези транзакции наистина са изпълнени в серия.
Прочетете документацията за изолиране на транзакции в Pg, за да научите повече. Обърнете внимание, че SERIALIZABLE
промени драматично поведението в Pg 9.1, така че не забравяйте да прочетете версията на ръководството, подходяща за вашата версия на Pg. Ето версията 8.4
. По-специално прочетете 13.2.2.1. Сериализираща се изолация срещу истинска сериализируемост
. Сега сравнете това със значително подобрената поддръжка за сериализация, базирана на заключване на предикат, описана в Страница 9.1 документи
.
Изглежда, че се опитвате да изпълните логически нещо подобно на този псевдокод:
count = query("SELECT count(*) FROM the_table");
if (count < threshold):
query("INSERT INTO the_table (...) VALUES (...)");
Ако е така, това няма да работи в Pg 8.4, когато се изпълнява едновременно - това е почти същото като примера за аномалия, използван в документацията, свързана по-горе. Удивително, той действително работи на Pg 9.1; Не очаквах дори предикатното заключване на 9.1 да хване използването на агрегати.
Вие пишете, че:
но 8.4 няма да открие, че двете транзакции са взаимозависими, нещо, което можете тривиално да докажете, като използвате два psql
сесии, за да го тествате. Само с нещата за истинска сериализация, въведени в 9.1, това ще работи - и честно казано, бях изненадан, че работи в 9.1.
Ако искате да направите нещо като налагане на максимален брой редове в Pg 8.4, трябва да LOCK
таблицата
за предотвратяване на едновременно INSERT
s, извършвайки заключването ръчно или чрез функция за задействане . Правенето му в тригер по своята същност ще изисква промоция на заключване и по този начин често ще блокира, но ще свърши успешно работата. По-добре е да го направите в приложението, където можете да издадете LOCK TABLE my_table IN EXCLUSIVE MODE
преди да получите дори SELECT
от масата, така че вече има най-високия режим на заключване, от който ще се нуждае на масата, и по този начин не трябва да се нуждае от насърчаване на заключване, склонно към блокиране. EXCLUSIVE
режимът на заключване е подходящ, защото позволява SELECT
но нищо друго.
Ето как да го тествате в две psql сесии:
SESSION 1 SESSION 2
create table ser_test( x text );
BEGIN TRANSACTION
ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION
ISOLATION LEVEL SERIALIZABLE;
SELECT count(*) FROM ser_test ;
SELECT count(*) FROM ser_test ;
INSERT INTO ser_test(x) VALUES ('bob');
INSERT INTO ser_test(x) VALUES ('bob');
COMMIT;
COMMIT;
Когато се изпълнява на Pg 9.1, st commits succeeds then the second
COMMIT` се проваля с:
regress=# COMMIT;
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt.
HINT: The transaction might succeed if retried.
но когато се изпълнява на 8.4, и двата ангажимента са успешни, тъй като 8.4 няма целия код за заключване на предикат за възможност за сериализиране, добавен в 9.1.