Menambahkan kolom yang dapat dibatalkan ke tabel menghabiskan lebih dari 10 menit

11

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?

Matthieu Verrecchia
sumber
1
Yah, saya bisa memberi tahu Anda bagian dari itu: Mengubah jenis kolom ke jenis lain yang tidak kompatibel biner sebenarnya membuat kolom baru, menyalin data, dan menetapkan kolom lama sebagai dijatuhkan. Namun, SET NOT NULLtidak 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.
Craig Ringer
1
Sebelum mencurigai kinerjanya lambat, Anda perlu memastikan bahwa ALTER TABLE tidak hanya menunggu kunci. Sebutkan dalam pertanyaan jika Anda telah memeriksa.
Daniel Vérité
Terima kasih Craig dan Daniel. Ketika saya menjalankan perintah alter, itu muncul di pg_stat_activity dengan menunggu "true", saya kira itu berarti ia menunggu kunci!? Apakah ini cara yang baik untuk memeriksa? Ngomong-ngomong, sebelum menjalankan perubahan ini, semuanya berjalan dengan baik, tetapi beberapa detik setelah mulai, jumlah kunci bertambah
Coba kueri di wiki.postgresql.org/wiki/Lock_dependency_information untuk tampilan yang lebih baik. Entah Anda memiliki transaksi lama yang lupa untuk berkomitmen, atau aktivitas berat dengan tabel ini yang membuatnya selalu sibuk.
Daniel Vérité
Mungkin lebih cocok di dba.SE.
Erwin Brandstetter

Jawaban:

8

Ada beberapa kesalahpahaman di sini:

The nol bitmap adalah tidak bagian dari header tumpukan tupel. Per dokumentasi:

Ada header ukuran tetap (menempati 23 byte pada kebanyakan mesin), diikuti oleh bitmap nol opsional ...

32 kolom Anda yang tidak dapat dibatalkan tidak menguntungkan karena dua alasan:

  • Bitmap nol ditambahkan per baris , dan hanya jika ada setidaknya satu NULLnilai aktual di baris. Kolom nullable tidak memiliki dampak langsung, hanya NULLnilai 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:

    #define BITMAPLEN(NATTS) (((int)(NATTS) + 7) / 8)
  • Bitmap nol dialokasikan setelah header tumpukan tuple dan diikuti oleh OID opsional dan kemudian data baris. Awal OID atau data baris ditunjukkan oleh t_hoffdi header. Per kode sumber komentar :

    Perhatikan bahwa t_hoff harus merupakan kelipatan dari MAXALIGN.

  • 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_hoffmaju lagi MAXALIGNbyte (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:

    sudo /usr/lib/postgresql/9.3/bin/pg_controldata /var/lib/postgresql/9.3/main

Saya memperbarui instruksi dalam jawaban terkait yang Anda kutip .

Selain itu, bahkan jika ALTER TABLEpernyataan 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_activityberarti 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 mytableatau yang lebih agresif VACUUM 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;atau DROPsemuanya dan tambahkan kembali setelah ALTER 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.

Erwin Brandstetter
sumber
2
Hampir pasti kunci. Tapi, sebagai ujian, Anda selalu dapat membuat salinan tabel dan mencoba mengubahnya. Jika itu tidak butuh waktu lama maka Anda tahu itu bukan modifikasi sebenarnya yang menjadi masalah.
Terima kasih untuk penjelasannya Erwin. Saya pikir Anda benar, ini tampaknya merupakan masalah kunci. Ketika saya memeriksa pg_stat_activity, saya dapat melihat bahwa ALTER saya memiliki "menunggu" benar. Apa yang saya tidak tahu adalah mengapa ALTER tidak bisa mendapatkan kunci di atas meja, karena bahkan ketika saya tidak dapat menemukan kueri berjalan, tampaknya itu tidak bisa mendapatkannya. Tetapi begitu ALTER saya mulai berjalan, semua pertanyaan lain menunggu untuk menyelesaikannya. Jadi, aktivitas tersebut tampaknya menunjukkan bahwa ALTER mengunci semua pertanyaan lain, tetapi juga menunjukkan bahwa ALTER tidak mendapatkan kunci. Saya pikir ada sesuatu yang tidak saya mengerti dengan baik !?
@ MatthieuVerrecchia: Apakah Anda mencoba tes yang disarankan Richard?
Erwin Brandstetter
1
Saya baru saja mengkloning meja saya ke yang baru (dengan pg_dump -> pg_sql). Kolom baru ditambahkan dengan benar dalam 50ms, yang mengkonfirmasi masalah kunci. Ngomong-ngomong, masih tidak mengerti mengapa ALTER tidak dapat mengunci dengan aktivitas db yang benar-benar standar.
1
@ ErwinBrandstetter Saya telah mengikuti saran Anda dan mencoba VACUUM, lalu REINDEX. REINDEX juga memblokir, karena itu juga tidak dapat memperoleh kunci .. Setelah beberapa investigasi, masalahnya lebih sederhana daripada kita. Ada satu minggu tersisa <IDLE> dengan transaksi terbuka. Masalahnya diselesaikan, terima kasih untuk semuanya, informasi sangat berguna.