2011-12-16 28 views
14

Tôi đang cố gắng tối ưu hóa một truy vấn phức tạp trong PostgreSQL 9.1.2, nó gọi một số chức năng. Các hàm này được đánh dấu STABLE hoặc IMMUTABLE và được gọi nhiều lần với cùng các đối số trong truy vấn. Tôi cho rằng PostgreSQL đủ thông minh để chỉ gọi chúng một lần cho mỗi bộ đầu vào - sau khi tất cả, đó là điểm của Ổn định và IMMUTABLE, phải không? Nhưng có vẻ như các chức năng đang được gọi nhiều lần. Tôi đã viết một chức năng đơn giản để kiểm tra điều này, trong đó khẳng định nó:Tại sao PostgreSQL lại gọi hàm STABLE/IMMUTABLE của tôi nhiều lần?

CREATE OR REPLACE FUNCTION test_multi_calls1(one integer) 
RETURNS integer 
AS $BODY$ 
BEGIN 
    RAISE NOTICE 'Called with %', one; 
    RETURN one; 
END; 
$BODY$ LANGUAGE plpgsql IMMUTABLE; 


WITH data AS 
(
    SELECT 10 AS num 
    UNION ALL SELECT 10 
    UNION ALL SELECT 20 
) 
SELECT test_multi_calls1(num) 
FROM data; 

Output:

NOTICE: Called with 10 
NOTICE: Called with 10 
NOTICE: Called with 20 

Tại sao điều này xảy ra và làm thế nào tôi có thể làm cho nó chỉ thực hiện chức năng một lần?

Trả lời

23

Phần mở rộng sau đây của mã kiểm tra của bạn là thông tin:

CREATE OR REPLACE FUNCTION test_multi_calls1(one integer) 
RETURNS integer 
AS $BODY$ 
BEGIN 
    RAISE NOTICE 'Immutable called with %', one; 
    RETURN one; 
END; 
$BODY$ LANGUAGE plpgsql IMMUTABLE; 
CREATE OR REPLACE FUNCTION test_multi_calls2(one integer) 
RETURNS integer 
AS $BODY$ 
BEGIN 
    RAISE NOTICE 'Volatile called with %', one; 
    RETURN one; 
END; 
$BODY$ LANGUAGE plpgsql VOLATILE; 

WITH data AS 
(
    SELECT 10 AS num 
    UNION ALL SELECT 10 
    UNION ALL SELECT 20 
) 
SELECT test_multi_calls1(num) 
FROM data 
where test_multi_calls2(40) = 40 
and test_multi_calls1(30) = 30 

OUTPUT:

NOTICE: Immutable called with 30 
NOTICE: Volatile called with 40 
NOTICE: Immutable called with 10 
NOTICE: Volatile called with 40 
NOTICE: Immutable called with 10 
NOTICE: Volatile called with 40 
NOTICE: Immutable called with 20 

Ở đây chúng ta có thể thấy rằng mặc dù trong danh sách lựa chọn các chức năng bất biến được gọi là nhiều lần, trong mệnh đề where nó được gọi một lần, trong khi biến động được gọi là ba lần.

Điều quan trọng không phải là PostgreSQL sẽ chỉ gọi một STABLE hoặc IMMUTABLE chức năng một lần với cùng một dữ liệu - ví dụ của bạn rõ ràng cho thấy rằng đây không phải là trường hợp - đó là nó có thể gọi nó là chỉ một lần. Hoặc có lẽ nó sẽ gọi nó hai lần khi nó sẽ phải gọi một phiên bản dễ bay hơi 50 lần, và cứ thế.

Có nhiều cách khác nhau trong đó tính ổn định và bất biến có thể được tận dụng, với chi phí và lợi ích khác nhau. Để cung cấp loại tiết kiệm bạn đang đề xuất nên chọn danh sách lựa chọn, nó sẽ phải lưu lại kết quả, sau đó tra cứu từng đối số (hoặc danh sách đối số) trong bộ đệm này trước khi trả về kết quả đã lưu trong bộ nhớ cache hoặc chức năng gọi trên bộ đệm -bỏ lỡ. Điều này sẽ đắt hơn gọi điện cho chức năng của bạn, ngay cả trong trường hợp có tỷ lệ truy cập bộ nhớ cache cao (có thể có 0% lần truy cập bộ nhớ cache có nghĩa là "tối ưu hóa" này đã làm thêm công việc để hoàn toàn không có được). Nó có thể lưu trữ có lẽ chỉ là tham số và kết quả cuối cùng, nhưng lại có thể hoàn toàn vô dụng.

Điều này đặc biệt để xem xét các chức năng ổn định và không thay đổi thường là các chức năng nhẹ nhất.

Với mệnh đề where Tuy nhiên, tính bất biến của test_multi_calls1 phép PostgreSQL để thực sự tái cấu trúc truy vấn từ ý nghĩa đơn giản của SQL đưa ra:

Đối với mỗi hàng tính toán test_multi_calls1 (30) và nếu kết quả là bằng 30 tiếp tục xử lý hàng trong câu hỏi

Đối với một kế hoạch truy vấn khác nhau hoàn toàn:

Tính test_multi_calls1 (30) và nếu nó là bằng 30 thì tiếp tục với các truy vấn khác trở lại một số không liên tiếp kết quả thiết lập mà không cần bất kỳ tính toán thêm

Đây là loại sử dụng mà PostgreSQL làm của ỔN ĐỊNH và IMMUTABLE - không phải là bộ nhớ đệm của kết quả, nhưng việc viết lại các truy vấn vào các truy vấn khác nhau có hiệu quả hơn nhưng cho kết quả tương tự.

Cũng lưu ý rằng test_multi_calls1 (30) được gọi trước test_multi_calls2 (40) bất kể thứ tự chúng xuất hiện trong mệnh đề where. Điều này có nghĩa là nếu cuộc gọi đầu tiên dẫn đến không có hàng nào được trả lại (thay thế = 30 với = 31 để kiểm tra) thì hàm không ổn định sẽ không được gọi - bất kể nó nằm ở bên nào của and.

Loại ghi đè cụ thể này tùy thuộc vào tính bất biến hoặc độ ổn định. Với where test_multi_calls1(30) != num truy vấn viết lại sẽ xảy ra cho bất biến nhưng không chỉ cho các chức năng đơn thuần ổn định. Với where test_multi_calls1(num) != 30 nó sẽ không xảy ra ở tất cả (nhiều cuộc gọi) mặc dù có các tối ưu hóa khác có thể:

Biểu thức chỉ có thể sử dụng chức năng Ổn định và IMMUTABLE. Biểu thức có chứa hàm VOLATILE không thể. Số lượng cuộc gọi có thể hoặc không giảm, nhưng quan trọng hơn nhiều là kết quả của các cuộc gọi sẽ được sử dụng theo cách hiệu quả hơn trong phần còn lại của truy vấn (chỉ thực sự quan trọng trên các bảng lớn) Sự khác biệt). Trong tất cả, đừng nghĩ đến các loại biến động về mặt memoisation, mà là về cơ hội lập kế hoạch truy vấn của PostgreSQL để tái cơ cấu toàn bộ truy vấn theo cách tương đương về mặt logic (cùng một kết quả) nhưng hiệu quả hơn nhiều.

+0

Thú vị –

0

Theo các hàm documentation IMMUTABLE sẽ trả về cùng một giá trị cho cùng một đối số. Vì bạn đang cho ăn các đối số động (Và thậm chí không cùng một lần) trình tối ưu hóa không có lý do để tin rằng nó sẽ nhận được kết quả tương tự và do đó gọi hàm. Qustion tốt hơn là: tại sao truy vấn của bạn gọi hàm nhiều lần nếu nó không cần?

+0

Ngay cả khi tôi mã hóa các đối số, ví dụ: 'SELECT test_multi_calls1 (10), test_multi_calls1 (10)' thư vẫn được in hai lần. Vậy thì điểm sửa đổi 'IMMUTABLE' là gì? – EMP

+0

tốt, nếu điều này xảy ra với các đối số được mã hóa cứng, bạn nên liên hệ với các nhà phát triển PostgreSQL. –

+1

Bạn có thể nghĩ ra một trường hợp 'SELECT f (x), f (x)' có giá trị thực tế nào khác ngoài việc chứng minh rằng 'f (x)' được gọi là hai lần không? Trình tối ưu hóa truy vấn chi phí cpu và bộ nhớ, và phải kiếm tiền. Tìm kiếm các trường hợp như thế trong mọi truy vấn trong trường hợp chúng có thể làm suy giảm mọi truy vấn từng chạy ngoại trừ truy vấn đó. –

Các vấn đề liên quan