2011-06-08 6 views
18

Hy vọng một câu hỏi đơn giản, nhưng một câu hỏi mà tôi chưa sẵn sàng tìm thấy một câu trả lời hợp lý. Tôi được thông báo một cách đáng tin cậy rằng các thủ tục được lưu trữ (các hàm DB do người dùng định nghĩa) trong PostgreSQL (cụ thể, phiên bản 9.0.4) vốn đã được giao dịch, vì chúng được gọi thông qua câu lệnh SELECT mà chính nó là một giao dịch. Vậy làm thế nào người ta chọn mức cô lập của thủ tục lưu sẵn? Tôi tin rằng trong các DBMS khác, khối giao dịch mong muốn sẽ được bao bọc trong khối START TRANSACTION mà mức cô lập mong muốn là một tham số tùy chọn.thiết lập mức cô lập cho các thủ tục lưu trữ postgresql

Là một làm-up cụ thể ví dụ, nói tôi muốn làm điều này:

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; 

Và hãy tưởng tượng tôi muốn chắc chắn chức năng này luôn được thực hiện như một giao dịch serializable (vâng, vâng, PostgreSQL isn SERIALIZABLE không thể tuần tự đúng, nhưng đó không phải là vấn đề). Tôi không muốn yêu cầu nó được gọi là

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

Vậy làm cách nào để đẩy mức cách ly bắt buộc vào chức năng? Tôi tin rằng tôi không thể chỉ cần đặt mức cô lập trong báo cáo BEGIN, như the manual says

Điều quan trọng là không để nhầm lẫn giữa việc sử dụng của BEGIN/END cho nhóm báo cáo trong PL/pgSQL với cùng tên SQL lệnh để kiểm soát giao dịch. BEGIN/END của PL/pgSQL chỉ dành cho nhóm; họ không bắt đầu hoặc kết thúc giao dịch . Chức năng và kích hoạt thủ tục luôn thực hiện trong vòng một giao dịch được thiết lập bởi một truy vấn bên ngoài - họ không thể bắt đầu hoặc cam kết giao dịch đó, vì sẽ có no context cho họ để thực hiện trong

Rõ ràng nhất. cách tiếp cận đối với tôi sẽ được sử dụng SET TRANSACTION đâu đó trong định nghĩa hàm, ví dụ ,:

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; 

trong khi điều này sẽ được chấp nhận, nó không phải là rõ ràng hơn tôi có thể dựa vào điều này để làm việc. Các documentation cho SET TRANSACTION nói

Nếu SET GIAO DỊCH được thực hiện mà không cần một START GIAO DỊCH trước hoặc BEGIN, nó sẽ xuất hiện để không có hiệu lực, kể từ giao dịch ngay lập tức sẽ kết thúc.

Những lá tôi bối rối, vì nếu tôi gọi một SELECT add_new_row('foo'); tuyên bố đơn độc Tôi mong chờ (với điều kiện tôi chưa tắt autocommit) SELECT được chạy như một giao dịch duy nhất phù hợp với mức độ cô lập phiên mặc định.

Các manual cũng nói:

Giao dịch mức cô lập không thể được thay đổi sau khi truy vấn đầu tiên hoặc tuyên bố dữ liệu sửa đổi (SELECT, INSERT, DELETE, UPDATE, FETCH, hoặc COPY) của một giao dịch đã được thực hiện .

Vậy điều gì sẽ xảy ra nếu hàm được gọi từ bên trong một giao dịch với một mức độ cô lập thấp hơn, ví dụ,:.

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

Đối với một câu hỏi bonus: không ngôn ngữ của chức năng thực hiện bất kỳ sự khác biệt? Liệu một trong những thiết lập mức độ cô lập khác nhau trong PL/pgSQL hơn trong SQL đơn giản?

Tôi là người hâm mộ các tiêu chuẩn và các phương pháp hay nhất được viết thành tài liệu, vì vậy mọi tham chiếu phong nha sẽ được đánh giá cao.

+1

Điều gì đã xảy ra khi bạn cố sử dụng 'SET TRANSACTION' bên trong chức năng? –

+0

@a_horse_with_no_name: Như tôi đã đề cập, tôi thấy 'SET TRANSACTION' là những gì tôi cần, và chức năng với nó được chấp nhận, nhưng đôi khi điều đó không có nghĩa nhiều (một số tùy chọn chỉ thỉnh thoảng nuốt), vì vậy tôi đang tìm một cách tiếp cận tài liệu hơn là một cái gì đó mà dường như chỉ hoạt động. – beldaz

+1

Postgres hiếm khi nuốt những gì bạn bảo nó làm - và khi nào tôi mong đợi nó sẽ đưa ra cảnh báo. –

Trả lời

15

Bạn không thể làm điều đó.

Điều bạn có thể làm là để chức năng kiểm tra mức độ cách ly giao dịch hiện tại là gì và hủy bỏ nếu đó không phải là mức bạn muốn. Bạn có thể làm điều này bằng cách chạy SELECT current_setting('transaction_isolation') và sau đó kiểm tra kết quả.

+0

Vâng, tôi đã thay đổi để nghĩ rằng đó sẽ là cách tiếp cận duy nhất. Tôi nghĩ rằng "chống trả lời" là điều gần nhất với những gì tôi muốn;) – beldaz

1

Ngôn ngữ của hàm không tạo ra sự khác biệt nào.

này thất bại:

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 

Lưu ý rằng trong ví dụ cụ thể của bạn, bạn có thể làm điều này bằng cách sử dụng kích hoạt trên bảng đầu tiên của bạn. Chỉ cần chắc chắn rằng cập nhật số lượng hàng được thực hiện in a consistent order để tránh các khóa chết, và bạn sẽ làm tốt trong chế độ đọc lặp lại.

Tôi là một fan hâm mộ của tiêu chuẩn

PL/ngôn ngữ là nền tảng cụ thể.

+0

Bạn có muốn mở rộng câu trả lời của mình bằng cách cho biết vị trí này nên được đặt không? Như tôi đã đề cập, ví dụ này hoàn toàn được tạo nên, vì vậy tôi không có ý định đánh lừa bạn nghĩ rằng tôi đã theo một cách tiếp cận khác với những người kích hoạt. Đối với các ngôn ngữ PL là nền tảng cụ thể, chúng vẫn có thể có tài liệu. Tôi chỉ tìm thấy tài liệu postgres khó để nhúng vào cho một câu trả lời đúng cho một cái gì đó cụ thể, do đó, chỉ dẫn của nơi chính xác để xem sẽ hữu ích cho tôi bất kỳ độc giả khác. – beldaz

+0

Chỉ cần bao gồm nó trong cơ thể của hàm ... Thay thế 'bắt đầu' bằng' bắt đầu mức cô lập serializable' (hoặc thêm một khối bắt đầu/kết thúc bổ sung, nếu nó ho một lỗi). –

+0

@Denis: thay thế 'bắt đầu' như bạn đề xuất có vẻ sẽ xung đột với hướng dẫn sử dụng postgres v9 39.2 - "BEGIN/END của PL/pgSQL chỉ dành cho nhóm; chúng không bắt đầu hoặc kết thúc giao dịch". – beldaz

0

Cách ly giao dịch có nghĩa là những thay đổi được thực hiện trong các giao dịch bảo mật khác mà bạn có thể truy cập.

Nếu bạn muốn tuần tự hóa, bạn phải sử dụng khóa.

Bạn có thể sử dụng sau khi kích hoạt hàng và cập nhật số lượng. "UPDATE row_counts_table" sẽ khóa bảng và tất cả các giao dịch sẽ được tuần tự hóa. Nó chậm.

Trong ví dụ của bạn, bạn có hai câu lệnh. Chèn được thực hiện nhưng cập nhật phải đợi các giao dịch khác và số đếm không hợp lệ trong khoảng thời gian này.

+0

10 năm trước, có lẽ. Nhưng nếu bạn có nghĩa là nói chung, sau đó không, serialization không phải sử dụng ổ khóa. Chuẩn SQL ban đầu giả định rằng các khóa sẽ được sử dụng, đó là lý do tại sao các mức cô lập dựa trên các dị thường khóa cụ thể nào được chuẩn bị để chấp nhận. Tuy nhiên, cách tiếp cận MVCC của postgres và SQL Server cơ bản khác với khóa, và mặc dù có vấn đề về viết nghiêng, các phương thức đã được đưa ra (ví dụ, bởi Fekete et al) để tránh điều này và do đó cung cấp khả năng thực thi tuần tự. – beldaz

0

Trong PG, các thủ tục của bạn không phải là các giao dịch riêng biệt. Đó là thủ tục được lưu trữ tham gia vào một giao dịch hiện có.

BEGIN TRAN 

SELECT 1; 
SELECT my_proc(99); 

ROLLBACK TRAN; 

Với điều đó bạn phải đặt mức giao dịch nơi giao dịch bắt đầu nằm ngoài thủ tục được lưu trữ.

Một tùy chọn sẽ là định cấu hình máy chủ để chạy trong phần cách ly bạn chủ yếu muốn sử dụng và thực hiện SET cho các trường hợp cạnh khác với cài đặt máy chủ của bạn.

+0

Than ôi, đánh bại lợi ích của các thủ tục được lưu trữ cho tôi, trong đó tôi muốn một số hoạt động xảy ra cùng nhau trong một đơn vị phía máy chủ mức cô lập giao dịch. Hãy suy nghĩ về các tài khoản tín dụng có lãi, mà bạn không muốn sử dụng giá trị tài khoản hiện tại gián đoạn nếu một số giao dịch khác đang thực hiện chuyển khoản số dư. Nó sẽ là một sự xấu hổ nếu một trong những đã phải nghỉ mát cho các giao dịch phía khách hàng cho điều này, và hy vọng mức giao dịch mặc định phiên đã đúng là không chính xác đánh lừa bằng chứng. – beldaz