Bisakah seseorang menjelaskan perilaku ini kepada saya? Saya menjalankan query berikut pada Postgres 9.3 yang berjalan secara native di OS X. Saya mencoba mensimulasikan beberapa perilaku di mana ukuran indeks bisa tumbuh jauh lebih besar daripada ukuran tabel, dan malah menemukan sesuatu yang bahkan lebih aneh.
CREATE TABLE test(id int);
CREATE INDEX test_idx ON test(id);
CREATE FUNCTION test_index(batch_size integer, total_batches integer) RETURNS void AS $$
DECLARE
current_id integer := 1;
BEGIN
FOR i IN 1..total_batches LOOP
INSERT INTO test VALUES (current_id);
FOR j IN 1..batch_size LOOP
UPDATE test SET id = current_id + 1 WHERE id = current_id;
current_id := current_id + 1;
END LOOP;
END LOOP;
END;
$$ LANGUAGE plpgsql;
SELECT test_index(500, 10000);
Saya membiarkan ini berjalan sekitar satu jam di mesin lokal saya, sebelum saya mulai mendapatkan peringatan masalah disk dari OS X. Saya perhatikan bahwa Postgres menyedot sekitar 10MB / s dari disk lokal saya, dan bahwa database Postgres mengkonsumsi total besar 30GB dari mesin saya. Saya akhirnya membatalkan permintaan. Bagaimanapun, Postgres tidak mengembalikan ruang disk kepada saya dan saya menanyakan database untuk statistik penggunaan dengan hasil sebagai berikut:
test=# SELECT nspname || '.' || relname AS "relation",
pg_size_pretty(pg_relation_size(C.oid)) AS "size"
FROM pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_relation_size(C.oid) DESC
LIMIT 20;
relation | size
-------------------------------+------------
public.test | 17 GB
public.test_idx | 14 GB
Namun, memilih dari tabel tidak membuahkan hasil.
test=# select * from test limit 1;
id
----
(0 rows)
Menjalankan 10.000 kumpulan 500 adalah 5.000.000 baris, yang seharusnya menghasilkan ukuran tabel / indeks yang cukup kecil (pada skala MB). Saya menduga Postgres membuat versi baru dari tabel / indeks untuk setiap INSERT / UPDATE yang terjadi dengan fungsi, tetapi ini tampaknya aneh. Seluruh fungsi dijalankan secara transaksi, dan tabel kosong untuk memulai.
Adakah pemikiran mengapa saya melihat perilaku ini?
Secara khusus, dua pertanyaan yang saya miliki adalah: mengapa ruang ini belum direklamasi oleh database dan yang kedua adalah mengapa database membutuhkan ruang sebanyak ini? 30GB tampak seperti banyak bahkan ketika menghitung MVCC
sumber
Angka aktual setelah menganalisis fungsi jauh lebih besar karena semua baris tabel mendapatkan nilai yang sama yang diperbarui beberapa kali dalam setiap iterasi.
Ketika kami menjalankannya dengan parameter
n
danm
:ada
m
sisipan dann * (m^2 + m) / 2
pembaruan baris . Jadi, untukn = 500
danm = 10000
, Postgres perlu memasukkan hanya baris 10K tetapi melakukan pembaruan tuple ~ 25G (25 miliar).Mempertimbangkan bahwa sebuah baris di Postgres memiliki 24 byte overhead, sebuah tabel dengan hanya satu
int
kolom akan membutuhkan 28 byte per baris plus overhead halaman. Jadi, agar operasi selesai, kita membutuhkan sekitar 700GB plus ruang untuk indeks (yang akan menjadi beberapa ratus GB juga).Pengujian
Untuk menguji teorinya, kami membuat tabel lain
test_test
dengan satu baris.Kemudian kami menambahkan pemicu
test
sehingga setiap pembaruan akan menambah penghitung sebesar 1. (Kode dihilangkan). Kemudian kita jalankan fungsinya, dengan nilai lebih kecil,n = 50
danm = 100
.Teori kami memprediksi :
Tes 1 (
test
tabel asli , dengan indeks)Setelah selesai, kami memeriksa konten tabel:
Dan penggunaan disk (kueri di bawah Indeks ukuran / statistik penggunaan dalam Pemeliharaan Indeks ):
The
test
meja telah digunakan hampir 9MB untuk meja dan 5MB untuk indeks. Perhatikan bahwatest_test
tabel telah menggunakan 9MB lagi! Itu diharapkan karena ia juga melalui pembaruan 250 ribu (pemicu kedua kami memperbarui satu baristest_test
untuk setiap pembaruan satu baris ditest
.)Perhatikan juga jumlah pemindaian di atas meja
test
(10K) dan tupel berbunyi (500K).Tes 2 (
test
tabel tanpa indeks)Persis sama dengan di atas, kecuali bahwa tabel tidak memiliki indeks.
Kami mendapatkan ukuran yang sama untuk penggunaan disk tabel dan tentu saja tidak ada penggunaan disk untuk indeks. Jumlah pemindaian di atas meja
test
adalah nol dan tupel juga berbunyi.Tes 3 (dengan fillfactor rendah)
Mencoba dengan fillfactor 50 dan serendah mungkin, 10. Tidak ada perbaikan sama sekali. Penggunaan disk hampir identik dengan tes sebelumnya (yang menggunakan fillfactor default, 100 persen)
sumber