Saya memiliki masalah untuk menambahkan kolom baru di atas meja.
Saya mencoba menjalankannya beberapa kali, tetapi setelah lebih dari 10 menit berjalan, saya memutuskan untuk membatalkan permintaan karena waktu kunci.
ALTER TABLE mytable ADD mycolumn VARCHAR(50);
Informasi berguna:
- Versi PostgreSQL: 9.1
- Jumlah baris: ~ 250K
- Jumlah kolom: 38
- Jumlah kolom yang dapat dibatalkan: 32
- Jumlah kendala: 5 (1 PK, 3 FK, 1 UNIK)
- Jumlah indeks: 1
- Jenis OS: Debian Squeeze 64
Saya menemukan informasi menarik tentang cara PostgreSQL mengelola kolom nullable (via HeapTupleHeader).
Tebakan pertama saya adalah karena tabel ini sudah memiliki 32 kolom nullable dengan 8-bit MAXALIGN
, HeapTupleHeader panjangnya 4 Bytes (tidak diverifikasi, dan saya tidak tahu bagaimana melakukannya).
Jadi menambahkan kolom nullable baru bisa memerlukan pembaruan HeapTupleHeader di setiap baris untuk menambahkan 8-bit baru MAXALIGN
, yang dapat menyebabkan masalah kinerja.
Jadi saya mencoba untuk mengubah salah satu kolom nullable (yang sebenarnya tidak bisa nullable) untuk mengurangi hingga 31 jumlah kolom nullable, untuk memeriksa apakah tebakan saya benar.
ALTER TABLE mytable ALTER myothercolumn SET NOT NULL;
Sayangnya, perubahan ini juga membutuhkan waktu yang sangat lama, lebih dari 5 menit, jadi saya juga membatalkannya.
Apakah Anda memiliki gagasan tentang apa yang dapat menyebabkan biaya kinerja ini?
sumber
SET NOT NULL
tidak mengubah tipe, itu hanya menambah kendala - tetapi kendala harus diperiksa terhadap tabel, dan itu membutuhkan pemindaian tabel penuh. 9.4 meningkatkan beberapa kasus ini dengan mengambil kunci yang lebih lemah, tetapi masih cukup berat.Jawaban:
Ada beberapa kesalahpahaman di sini:
The nol bitmap adalah tidak bagian dari header tumpukan tupel. Per dokumentasi:
32 kolom Anda yang tidak dapat dibatalkan tidak menguntungkan karena dua alasan:
Bitmap nol ditambahkan per baris , dan hanya jika ada setidaknya satu
NULL
nilai aktual di baris. Kolom nullable tidak memiliki dampak langsung, hanyaNULL
nilai aktual yang melakukannya. Jika bitmap nol dialokasikan, selalu dialokasikan sepenuhnya (semua atau tidak sama sekali). Ukuran sebenarnya dari bitmap null adalah 1 bit per kolom, dibulatkan ke byte berikutnya . Per kode sumber saat ini:Bitmap nol dialokasikan setelah header tumpukan tuple dan diikuti oleh OID opsional dan kemudian data baris. Awal OID atau data baris ditunjukkan oleh
t_hoff
di header. Per kode sumber komentar :Ada satu byte gratis setelah header heap tuple, yang menempati 23 byte. Jadi bitmap nol untuk baris hingga 8 kolom secara efektif datang tanpa biaya tambahan. Dengan kolom ke-9 dalam tabel,
t_hoff
maju lagiMAXALIGN
byte (biasanya 8) untuk menyediakan 64 kolom lainnya. Jadi perbatasan berikutnya adalah 72 kolom.Untuk menampilkan informasi kontrol cluster database PostgreSQL (termasuk
MAXALIGN
), contoh untuk instalasi khas Postgres 9.3 pada mesin Debian:Saya memperbarui instruksi dalam jawaban terkait yang Anda kutip .
Selain itu, bahkan jika
ALTER TABLE
pernyataan Anda memicu penulisan seluruh tabel (yang mungkin memang mengubah tipe data), 250K benar-benar tidak terlalu banyak dan hanya hitungan detik pada mesin setengah jalan yang layak (kecuali jika barisnya luar biasa besar) . 10 menit atau lebih mengindikasikan masalah yang sama sekali berbeda. Pernyataan Anda sedang menunggu untuk mendapatkan kunci di atas meja, kemungkinan besar.Semakin banyak entri
pg_stat_activity
berarti transaksi yang lebih terbuka - menunjukkan akses bersamaan pada tabel (kemungkinan besar) yang harus menunggu operasi selesai.Beberapa tembakan dalam gelap
Periksa kemungkinan mengasapi meja, cobalah yang lembut
VACUUM mytable
atau yang lebih agresifVACUUM FULL mytable
- yang mungkin menghadapi masalah konkurensi yang sama, karena formulir ini juga memperoleh kunci eksklusif. Anda dapat mencoba pg_repack sebagai gantinya ...Saya akan mulai dengan memeriksa kemungkinan masalah dengan indeks, pemicu, kunci asing atau kendala lainnya, terutama yang melibatkan kolom. Terutama indeks yang rusak mungkin terlibat? Coba
REINDEX TABLE mytable;
atauDROP
semuanya dan tambahkan kembali setelahALTER TABLE
dalam transaksi yang sama .Coba jalankan perintah di malam hari atau kapan pun bebannya tidak banyak.
Metode brute-force adalah menghentikan akses ke server, lalu coba lagi:
Tanpa dapat menjelaskannya, peningkatan ke versi saat ini atau yang akan datang pada 9.4 khususnya dapat membantu. Ada beberapa perbaikan untuk tabel besar dan untuk mengunci detail. Tetapi jika ada sesuatu yang rusak di DB Anda, Anda mungkin harus mencari tahu dulu.
sumber