Tôi sẽ cần gọi
REFRESH MATERIALIZED VIEW
trên mỗi thay đổi đối với các bảng có liên quan, phải không?
Có, bản thân PostgreSQL sẽ không bao giờ tự động gọi nó, bạn cần làm theo cách nào đó.
Tôi nên làm như thế nào để thực hiện việc này?
Nhiều cách để đạt được điều này. Trước khi đưa ra một số ví dụ, hãy nhớ rằng REFRESH MATERIALIZED VIEW
lệnh không chặn chế độ xem trong chế độ AccessExclusive, vì vậy trong khi nó đang hoạt động, bạn thậm chí không thể thực hiện SELECT
trên bàn.
Mặc dù, nếu bạn đang ở phiên bản 9.4 hoặc mới hơn, bạn có thể cung cấp cho nó CONCURRENTLY
tùy chọn:
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
Điều này sẽ có được ExclusiveLock và sẽ không chặn SELECT
truy vấn, nhưng có thể có chi phí lớn hơn (phụ thuộc vào lượng dữ liệu đã thay đổi, nếu một vài hàng đã thay đổi thì có thể nhanh hơn). Mặc dù bạn vẫn không thể chạy hai REFRESH
các lệnh đồng thời.
Làm mới theo cách thủ công
Đó là một lựa chọn để xem xét. Đặc biệt trong các trường hợp tải dữ liệu hoặc cập nhật hàng loạt (ví dụ:một hệ thống chỉ tải hàng tấn thông tin / dữ liệu sau một khoảng thời gian dài), thông thường sẽ có các hoạt động ở cuối để sửa đổi hoặc xử lý dữ liệu, vì vậy bạn có thể đơn giản bao gồm một REFRESH
hoạt động cuối cùng của nó.
Lập lịch hoạt động LÀM MỚI
Tùy chọn đầu tiên và được sử dụng rộng rãi là sử dụng một số hệ thống lập lịch để gọi làm mới, ví dụ:bạn có thể định cấu hình tương tự trong cron job:
*/30 * * * * psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv"
Và sau đó, chế độ xem cụ thể hóa của bạn sẽ được làm mới sau mỗi 30 phút.
Cân nhắc
Tùy chọn này thực sự tốt, đặc biệt với CONCURRENTLY
, nhưng chỉ khi bạn có thể chấp nhận dữ liệu luôn không được cập nhật 100%. Hãy ghi nhớ rằng ngay cả khi có hoặc không có CONCURRENTLY
, REFRESH
lệnh cần phải chạy toàn bộ truy vấn, vì vậy bạn phải dành thời gian cần thiết để chạy truy vấn bên trong trước khi xem xét thời gian lập lịch cho REFRESH
.
Làm mới bằng trình kích hoạt
Một tùy chọn khác là gọi REFRESH MATERIALIZED VIEW
trong một chức năng kích hoạt, như thế này:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
RETURN NULL;
END;
$$;
Sau đó, trong bất kỳ bảng nào liên quan đến các thay đổi trên chế độ xem, bạn thực hiện:
CREATE TRIGGER tg_refresh_my_mv AFTER INSERT OR UPDATE OR DELETE
ON table_name
FOR EACH STATEMENT EXECUTE PROCEDURE tg_refresh_my_mv();
Cân nhắc
Nó có một số cạm bẫy quan trọng đối với hiệu suất và tính đồng thời:
- Mọi thao tác CHÈN / CẬP NHẬT / XÓA sẽ phải thực hiện truy vấn (có thể chậm nếu bạn đang xem xét MV);
- Ngay cả với
CONCURRENTLY
, mộtREFRESH
vẫn chặn một cái khác, vì vậy mọi CHÈN / CẬP NHẬT / XÓA trên các bảng liên quan sẽ được tuần tự hóa.
Tình huống duy nhất tôi có thể nghĩ rằng đó là một ý kiến hay là nếu những thay đổi đó thực sự hiếm.
Làm mới bằng LISTEN / NOTIFY
Vấn đề với phương án trước là nó không đồng bộ và áp đặt chi phí lớn cho mỗi hoạt động. Để cải thiện điều đó, bạn có thể sử dụng trình kích hoạt như trước đây, nhưng trình kích hoạt chỉ gọi một NOTIFY
hoạt động:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NOTIFY refresh_mv, 'my_mv';
RETURN NULL;
END;
$$;
Vì vậy, bạn có thể tạo một ứng dụng duy trì kết nối và sử dụng LISTEN
hoạt động để xác định nhu cầu gọi REFRESH
. Một dự án tuyệt vời mà bạn có thể sử dụng để kiểm tra điều này là pgsidekick, với dự án này, bạn có thể sử dụng shell script để thực hiện LISTEN
, vì vậy bạn có thể lên lịch cho REFRESH
như:
pglisten --listen=refresh_mv --print0 | xargs -0 -n1 -I? psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY ?;"
Hoặc sử dụng pglater
(cũng bên trong pgsidekick
) để đảm bảo bạn không gọi REFRESH
rất thường xuyên. Ví dụ:bạn có thể sử dụng trình kích hoạt sau để làm cho nó REFRESH
, nhưng trong vòng 1 phút (60 giây):
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NOTIFY refresh_mv, '60 REFRESH MATERIALIZED VIEW CONCURRENLTY my_mv';
RETURN NULL;
END;
$$;
Vì vậy, nó sẽ không gọi REFRESH
cách nhau chưa đầy 60 giây và cả khi bạn NOTIFY
nhiều lần trong vòng chưa đầy 60 giây, REFRESH
sẽ chỉ được kích hoạt một lần.
Cân nhắc
Là tùy chọn cron, tùy chọn này cũng chỉ tốt nếu bạn có thể sử dụng với một ít dữ liệu cũ, nhưng điều này có lợi thế là REFRESH
chỉ được gọi khi thực sự cần thiết, vì vậy bạn có ít chi phí hơn và dữ liệu cũng được cập nhật gần hơn khi cần thiết.
OBS:Tôi chưa thực sự thử các mã và ví dụ, vì vậy nếu ai đó tìm thấy lỗi, đánh máy hoặc thử nó và hoạt động (hoặc không), vui lòng cho tôi biết.