2011-06-08 4 views
18

Надеюсь, простой вопрос, но тот, для которого я не нашел подходящего ответа. Я уверенно информирован о том, что хранимые процедуры (пользовательские функции DB) в PostgreSQL (в частности, версия 9.0.4) по сути являются транзакционными, поскольку они вызываются с помощью инструкции SELECT, которая сама является транзакцией. Итак, как выбрать уровень изоляции хранимой процедуры? Я верю, что в других СУБД желаемый блок транзакций будет завернут в блок START TRANSACTION, для которого желаемый уровень изоляции является необязательным параметром.установить уровень изоляции для postgresql хранимых процедур

В качестве конкретного выдуманным, например, сказать, что я хочу сделать это:

CREATE FUNCTION add_new_row(rowtext TEXT) 
RETURNS VOID AS 
$$ 
BEGIN 
     INSERT INTO data_table VALUES (rowtext); 
     UPDATE row_counts_table SET count=count+1; 
END; 
$$ 
LANGUAGE plpgsql 
SECURITY DEFINER; 

И представьте себе, я хочу, чтобы убедиться, что эта функция всегда выполняется как сериализуемой сделки (да, да, PostgreSQL SERIALIZABLE ISN 'надлежащий сериализуемый, но это не главное). Я не хочу, чтобы он назывался

START TRANSACTION ISOLATION LEVEL SERIALIZABLE; 
SELECT add_new_row('foo'); 
COMMIT; 

Итак, как я могу вытащить необходимый уровень изоляции в функцию? Я считаю, что я не могу просто поставить уровень изоляции в BEGIN заявлении в качестве the manual says

Важно не путать использование из BEGIN/END для группировки операторов в PL/PgSQL с аналогичным названием SQL команды для управления транзакциями. PL/pgSQL BEGIN/END предназначены только для группы ; они не запускают и не завершают транзакцию . Функции и инициируют процедуры всегда выполняются в рамках сделки, установленной внешней запроса - они не могут начать или совершить , что сделка, так как не было бы никакого контекста для их выполнения в

Наиболее очевидным. подход ко мне было бы использовать SET TRANSACTION где-то в определении функции, например ,:

CREATE FUNCTION add_new_row(rowtext TEXT) 
RETURNS VOID AS 
$$ 
BEGIN 
     SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 
     INSERT INTO data_table VALUES (rowtext); 
     UPDATE row_counts_table SET count=count+1; 
END; 
$$ 
LANGUAGE plpgsql 
SECURITY DEFINER; 

в то время как это было принято, не ясно, чем я могу рассчитывать на эту работу. documentation для SET TRANSACTION говорит

Если SET TRANSACTION выполняется без предшествующего START TRANSACTION или начать, появится не иметь никакого эффекта, так как сделки будет немедленно положить конец.

Который оставляет меня озадачило, так как если я называю уединенную SELECT add_new_row('foo'); заявление, которое я бы ожидать (если я не отключили AutoCommit) ВЫБОР быть запущен как сделки однострочного с уровнем изоляции сеанса по умолчанию.

manual также говорит: Уровень изоляции

сделка не может быть изменен после первого запроса или данных модификации заявления (ВЫБОР, INSERT, DELETE, UPDATE, FETCH или копия) совершена транзакция .

Так что же происходит, если функция вызывается из транзакции с более низким уровнем изоляции, например,:.

START TRANSACTION ISOLATION LEVEL READ COMMITTED; 
UPDATE row_counts_table SET count=0; 
SELECT add_new_row('foo'); 
COMMIT; 

Для бонусного вопроса: имеет ли язык функции никакой разницы? Можно ли установить уровень изоляции по-разному в PL/pgSQL, чем в обычном SQL?

Я поклонник стандартов и задокументированных лучших практик, поэтому любые достойные ссылки будут оценены.

+1

Что произошло, когда вы пытались использовать 'SET TRANSACTION' внутри функции? –

+0

@a_horse_with_no_name: Как я уже упоминал, я считаю, что «SET TRANSACTION» - это то, что мне нужно, и функции с ним принимаются, но иногда это не означает много (некоторые параметры иногда проглатываются), поэтому я ищу документированный подход, а не нечто, что, похоже, работает. – beldaz

+1

Postgres редко проглатывает то, что вы ему рассказываете, - и когда это произойдет, я ожидаю, что он выдает предупреждение. –

ответ

15

Вы не можете этого сделать.

Что вы можете сделать, это проверить свою работоспособность на то, что текущий уровень изоляции транзакции и прервать, если он не тот, который вы хотите. Вы можете сделать это, запустив SELECT current_setting('transaction_isolation'), а затем проверив результат.

+0

Да, я скорее подумал, что это будет единственный подход. Я думаю, что ваш «анти-ответ» является самым близким к тому, что я хочу;) – beldaz

1

Язык функции не имеет никакого значения.

Это не удается:

test=# create function test() returns int as $$ 
    set transaction isolation level serializable; 
    select 1; 
$$ language sql; 
CREATE FUNCTION 
test=# select test(); 
ERROR: SET TRANSACTION ISOLATION LEVEL must be called before any query 
CONTEXT: SQL function "test" statement 1 

Обратите внимание, что в вашем конкретном примере, вы могли бы сделать это с помощью триггера на вашей первой таблице. Просто убедитесь, что обновления счетчика строк сделаны in a consistent order, чтобы избежать блокировок, и вы будете хорошо работать в режиме повторного чтения.

Я фанат стандартов

ФЛ/языки платформы.

+0

Не могли бы вы расширить свой ответ, показывая, где это должно быть установлено? Как я уже упоминал, пример был полностью составлен, поэтому я не собирался вводить вас в заблуждение, думая, что я столкнулся с другим подходом с триггерами. Что касается языков PL, которые являются специфичными для платформы, они могут иметь документацию. Я просто нахожу документацию postgres трудно окунуться в надлежащий ответ на что-то конкретное, поэтому указание того, где именно искать, было бы полезно для меня любых других читателей. – beldaz

+0

Просто включите его в тело функции ... Замените 'begin' на' начальный уровень изоляции serializable' (или добавьте дополнительный блок begin/end, если он кашляет ошибку). –

+0

@Denis: замена 'begin', как вы предполагаете, противоречит руководству postgres v9 39.2 -« НАЧАЛО/END PL/pgSQL предназначены только для группировки, они не запускают и не завершают транзакцию ». – beldaz

0

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

Если вы хотите сериализовать выполнение, вам необходимо использовать блокировки.

Вы можете использовать после запуска триггера и подсчета обновлений. «UPDATE row_counts_table» заблокирует таблицу, и все транзакции будут сериализованы. Это медленно.

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

+0

10 лет назад, возможно. Но если вы имели в виду вообще, то нет, сериализации не нужно использовать блокировки. В исходном стандарте SQL предполагалось, что будут использоваться блокировки, поэтому уровни изоляции основаны на том, какие аномалии, зависящие от блокировки, готовы принять. Тем не менее, подход MVCC от postgres и SQL Server принципиально отличается от блокировки, и хотя существует проблема перекоса записи, методы были разработаны (например, Fekete et al), чтобы избежать этого и, следовательно, обеспечить сериализуемое выполнение. – beldaz

0

В PG ваши процедуры не являются отдельными транзакциями. Это хранимая процедура принимает участие в существующей транзакции.

BEGIN TRAN 

SELECT 1; 
SELECT my_proc(99); 

ROLLBACK TRAN; 

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

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

+0

Увы, это побеждает преимущество хранимых процедур для меня, в том, что я хочу, чтобы некоторые операции выполнялись вместе в блоке на стороне сервера с конкретным уровень изоляции транзакций. Подумайте о кредитном счете с процентами, для которого вы не хотите использовать текущее значение текущей учетной записи, если какая-либо другая транзакция выполняла перенос баланса. Было бы позором, если бы приходилось прибегать к транзакциям на стороне клиента для этого, и надеясь, что уровень транзакций по умолчанию в сеансе был прав, не является абсолютно надежным. – beldaz

Смежные вопросы