Mempertahankan nilai ringkasan itu rumit - mudah untuk menciptakan kemungkinan deadlock program Anda.
Jika Anda benar-benar harus melakukan ini, karena Anda tahu bahwa Anda akan mengalami masalah kinerja jika tidak (seperti nhunt dalam ratusan atau lebih), maka lebih baik untuk membuat tabel ringkasan terpisah untuk nhunts, seperti:
CREATE TABLE hunts_summary
(
id_hs bigserial primary key,
id_h integer NOT NULL,
nhunts integer NOT NULL
);
CREATE INDEX hunts_summary_id_h_idx on hunts_summary(id_h);
Pemicu perburuan:
- berjalan untuk setiap baris yang ditambahkan, dihapus, diperbarui;
- menambahkan baris
(id_h, nhunts) = (NEW.id_h, 1)
pada setiap sisipan; - menambahkan baris
(id_h, nhunts) = (OLD.id_h, -1)
pada setiap penghapusan; - kedua hal di atas pada pembaruan yang mengubah
id_h
.
Karena pemicu hanya akan menambahkan baris baru, pemicu tidak mengunci baris yang ada dan karena itu tidak dapat menemui jalan buntu.
Tapi ini tidak cukup - seperti yang dijelaskan di atas tabel ringkasan akan menumbuhkan baris secepat atau lebih cepat dari tabel berburu, jadi itu tidak terlalu membantu. Jadi kita perlu menambahkan beberapa cara untuk menggabungkan baris yang ada secara berkala - beberapa cara untuk mengubah:
id_h nhunts
1 1
1 1
2 1
2 -1
1 1
1 -1
2 1
1 1
2 1
Kepada:
id_h nhunts
1 3
2 2
Ini tidak boleh berjalan pada setiap pemanggilan pemicu, karena kemudian akan menjadi sangat lambat, tetapi dapat berjalan secara acak - misalnya setiap pemanggilan 1/1024 secara acak. Fungsi ini akan menggunakan kata kunci "lewati terkunci" untuk menghindari menyentuh baris yang sudah terkunci, menghindari kemungkinan kebuntuan.
Pemicu tersebut akan terlihat seperti ini:
create or replace function hunts_maintain() returns trigger
as $hunts_maintain$
begin
if (tg_op = 'INSERT') then
insert into hunts_summary(id_h, nhunts)
values (NEW.id_h, 1);
elsif (tg_op = 'DELETE') then
insert into hunts_summary(id_h, nhunts)
values (OLD.id_h, -1);
elsif (tg_op = 'UPDATE' and NEW.id_h!=OLD.id_h) then
insert into hunts_summary(id_h, nhunts)
values (OLD.id_h, -1), (NEW.id_h, 1);
end if;
if (random()*1024 < 1) then
with deleted_ids as (
select id_hs from hunts_summary for update skip locked
),
deleted_nhunts as (
delete from hunts_summary where id_hs in (select id_hs from deleted_ids) returning id_h, nhunts
)
insert into hunts_summary (id_h, nhunts) select id_h, sum(nhunts) from deleted_nhunts group by id_h;
end if;
return NEW;
end;
$hunts_maintain$ language plpgsql;
create trigger hunts_maintain
after insert or update or delete on hunts
for each row execute procedure hunts_maintain();
Pemicunya berjalan cukup cepat di laptop saya untuk menyisipkan 1 juta baris untuk berburu tabel dalam 45 detik.
Tampilan di bawah ini akan memudahkan untuk mengekstrak nhunts saat ini dari ringkasan. Menanyakannya akan membutuhkan sedikit atau ms bahkan jika tabel perburuan akan berjumlah miliaran:
create or replace view hunts_summary_view as
select id_h, sum(nhunts) as nhunts
from hunts_summary
group by id_h;