Kolom / Baris
... Saya tidak memerlukan integritas transaksional untuk dipertahankan di seluruh operasi, karena saya tahu bahwa kolom yang saya ubah tidak akan ditulis atau dibaca selama pembaruan.
UPDATE
dalam model MVCC PostgreSQL menulis versi baru seluruh baris . Jika transaksi bersamaan mengubah apa saja kolom dari baris yang sama, masalah konkurensi yang memakan waktu muncul. Detail dalam manual. Mengetahui kolom yang sama tidak akan tersentuh oleh transaksi bersamaan menghindari beberapa kemungkinan komplikasi, tetapi tidak yang lain.
Indeks
Untuk menghindari dialihkan ke diskusi di luar topik, mari kita asumsikan bahwa semua nilai status untuk 35 juta kolom saat ini disetel ke nilai yang sama (bukan nol), sehingga menjadikan indeks tidak berguna.
Saat memperbarui seluruh tabel (atau sebagian besar) Postgres tidak pernah menggunakan indeks . Pemindaian berurutan lebih cepat ketika semua atau sebagian besar baris harus dibaca. Sebaliknya:Pemeliharaan indeks berarti biaya tambahan untuk UPDATE
.
Kinerja
Misalnya, katakanlah saya memiliki tabel bernama "pesanan" dengan 35 juta baris, dan saya ingin melakukan ini:
UPDATE orders SET status = null;
Saya mengerti Anda bertujuan untuk solusi yang lebih umum (lihat di bawah). Tapi untuk menjawab pertanyaan sebenarnya ditanya:Ini dapat ditangani dalam dalam hitungan milidetik , berapa pun ukuran tabelnya:
ALTER TABLE orders DROP column status
, ADD column status text;
Manual (hingga Postgres 10):
Ketika kolom ditambahkan dengan
ADD COLUMN
, semua baris yang ada dalam tabel diinisialisasi dengan nilai default kolom (NULL
jika tidak adaDEFAULT
klausa ditentukan). Jika tidak adaDEFAULT
klausa, ini hanyalah perubahan metadata [...]
Manual (sejak Postgres 11):
Ketika kolom ditambahkan dengan
ADD COLUMN
danDEFAULT
yang tidak mudah menguap ditentukan, default dievaluasi pada saat pernyataan dan hasilnya disimpan dalam metadata tabel. Nilai itu akan digunakan untuk kolom untuk semua baris yang ada. Jika tidak adaDEFAULT
ditentukan, NULL digunakan. Dalam kedua kasus tersebut, penulisan ulang tabel tidak diperlukan.Menambahkan kolom dengan
DEFAULT
yang mudah menguap atau mengubah tipe kolom yang ada akan membutuhkan seluruh tabel dan indeksnya untuk ditulis ulang. [...]
Dan:
DROP COLUMN
form tidak secara fisik menghapus kolom, tetapi hanya membuatnya tidak terlihat oleh operasi SQL. Operasi penyisipan dan pembaruan berikutnya dalam tabel akan menyimpan nilai nol untuk kolom tersebut. Dengan demikian, menghapus kolom dengan cepat tetapi tidak akan segera mengurangi ukuran pada disk tabel Anda, karena ruang yang ditempati oleh kolom yang dijatuhkan tidak diambil kembali. Ruang akan direklamasi seiring waktu karena baris yang ada diperbarui.
Pastikan Anda tidak memiliki objek yang bergantung pada kolom (batasan kunci asing, indeks, tampilan, ...). Anda harus menjatuhkan/membuat ulang itu. Kecuali itu, operasi kecil pada tabel katalog sistem pg_attribute
melakukan pekerjaan. Memerlukan kunci eksklusif di atas meja yang mungkin menjadi masalah untuk beban bersamaan yang berat. (Seperti yang ditekankan Buurman dalam komentarnya.) Selain itu, operasinya hanya dalam hitungan milidetik.
Jika Anda memiliki kolom default yang ingin Anda pertahankan, tambahkan kembali dalam perintah terpisah . Melakukannya dalam perintah yang sama segera menerapkannya ke semua baris. Lihat:
- Tambahkan kolom baru tanpa kunci tabel?
Untuk benar-benar menerapkan default, pertimbangkan untuk melakukannya secara berkelompok:
- Apakah PostgreSQL mengoptimalkan penambahan kolom dengan DEFAULT non-NULL?
Solusi umum
dblink
telah disebutkan dalam jawaban lain. Ini memungkinkan akses ke database Postgres "jarak jauh" dalam koneksi terpisah yang implisit. Basis data "jarak jauh" dapat menjadi basis data saat ini, sehingga mencapai "transaksi otonom" :apa yang ditulis fungsi di db "jarak jauh" telah dikomit dan tidak dapat dibatalkan.
Ini memungkinkan untuk menjalankan satu fungsi yang memperbarui tabel besar di bagian yang lebih kecil dan setiap bagian dilakukan secara terpisah. Menghindari membangun overhead transaksi untuk jumlah baris yang sangat besar dan, yang lebih penting, melepaskan kunci setelah setiap bagian. Hal ini memungkinkan operasi serentak berjalan tanpa banyak penundaan dan memperkecil kemungkinan terjadinya deadlock.
Jika Anda tidak memiliki akses bersamaan, ini hampir tidak berguna - kecuali untuk menghindari ROLLBACK
setelah pengecualian. Pertimbangkan juga SAVEPOINT
untuk kasus itu.
Penafian
Pertama-tama, banyak transaksi kecil sebenarnya lebih mahal. Ini hanya masuk akal untuk tabel besar . Sweet spot tergantung pada banyak faktor.
Jika Anda tidak yakin dengan apa yang Anda lakukan:satu transaksi adalah metode yang aman . Agar ini berfungsi dengan baik, operasi bersamaan di atas meja harus dimainkan bersama. Misalnya:menulis concurrent bersamaan dapat memindahkan baris ke partisi yang seharusnya sudah diproses. Atau pembacaan bersamaan dapat melihat status perantara yang tidak konsisten. Anda telah diperingatkan.
Petunjuk langkah demi langkah
Modul tambahan dblink perlu diinstal terlebih dahulu:
- Bagaimana cara menggunakan (menginstal) dblink di PostgreSQL?
Menyiapkan koneksi dengan dblink sangat bergantung pada penyiapan klaster DB Anda dan kebijakan keamanan yang diterapkan. Ini bisa rumit. Terkait jawaban nanti dengan lebih cara terhubung dengan dblink :
- Penyisipan persisten dalam UDF meskipun fungsi dibatalkan
Buat FOREIGN SERVER
dan USER MAPPING
seperti yang diinstruksikan di sana untuk menyederhanakan dan merampingkan koneksi (kecuali Anda sudah memilikinya).
Dengan asumsi serial PRIMARY KEY
dengan atau tanpa beberapa celah.
CREATE OR REPLACE FUNCTION f_update_in_steps()
RETURNS void AS
$func$
DECLARE
_step int; -- size of step
_cur int; -- current ID (starting with minimum)
_max int; -- maximum ID
BEGIN
SELECT INTO _cur, _max min(order_id), max(order_id) FROM orders;
-- 100 slices (steps) hard coded
_step := ((_max - _cur) / 100) + 1; -- rounded, possibly a bit too small
-- +1 to avoid endless loop for 0
PERFORM dblink_connect('myserver'); -- your foreign server as instructed above
FOR i IN 0..200 LOOP -- 200 >> 100 to make sure we exceed _max
PERFORM dblink_exec(
$$UPDATE public.orders
SET status = 'foo'
WHERE order_id >= $$ || _cur || $$
AND order_id < $$ || _cur + _step || $$
AND status IS DISTINCT FROM 'foo'$$); -- avoid empty update
_cur := _cur + _step;
EXIT WHEN _cur > _max; -- stop when done (never loop till 200)
END LOOP;
PERFORM dblink_disconnect();
END
$func$ LANGUAGE plpgsql;
Telepon:
SELECT f_update_in_steps();
Anda dapat membuat parameter bagian mana pun sesuai dengan kebutuhan Anda:nama tabel, nama kolom, nilai, ... pastikan untuk membersihkan pengidentifikasi untuk menghindari injeksi SQL:
- Nama tabel sebagai parameter fungsi PostgreSQL
Hindari UPDATE kosong:
- Bagaimana cara (atau dapatkah saya) PILIH BERBEDA pada beberapa kolom?