Kami terus menerbitkan video dan transkrip laporan terbaik dari konferensi
PGConf.Russia 2019 .
Di bagian pertama pembicaraan Ivan Frolkov , ini tentang penamaan yang tidak konsisten, tentang kendala, tentang di mana lebih baik untuk berkonsentrasi logika - dalam database atau dalam aplikasi. Di bagian ini, Anda akan menemukan penanganan kesalahan parsing, akses bersamaan, operasi yang tidak dapat dibatalkan, CTE dan JSON.

Saya akan menceritakan kisah seperti itu. Klien kami mengatakan: βBasis data bekerja lambat, dan aplikasi kami terlibat dalam melayani populasi. Kami takut mereka akan mengangkat kami di sini untuk garpu. β Ternyata mereka memiliki banyak proses dalam keadaan
tidak aktif dalam keadaan
transaksi . Aplikasi memulai transaksi, tidak melakukan apa-apa, tetapi transaksi tidak selesai. Jika Anda berinteraksi dengan beberapa layanan eksternal, maka, pada prinsipnya, ini adalah situasi yang normal. Hal lain adalah jika
idle Anda
dalam status
transaksi bertahan lama (sudah mencurigakan selama lebih dari satu menit), maka ini buruk karena PostgreSQL benar-benar tidak menyukai transaksi lama: VACUUM tidak akan dapat menghapus semua baris yang dapat dilihatnya, dan menggantungnya dalam waktu yang lama. transaksi secara efektif memblokir VACUUM. Tabel mulai membengkak, indeks menjadi semakin tidak efektif.

Dalam hal ini, orang tidak menulis permintaan dengan benar dan menerima produk Cartesian - permintaan tersebut selesai selama beberapa hari. Nah, pengguna, dia akan menekan tombol, menunggu hasilnya dan, jika tidak ada hasil, tekan tombol lagi.
Tetapi ini tidak menjelaskan mengapa mereka memiliki begitu banyak proses dalam
menganggur dalam transaksi . Dan mereka muncul dalam situasi berikut: aplikasi merangkak ke dalam database, memulai transaksi, merangkak ke beberapa layanan eksternal, mendapat kesalahan di sana, dan kemudian semuanya hancur, kami mencetak ke log
jejak tumpukan , dan kami tenang dalam hal ini. Koneksi tetap ditinggalkan, tergantung dan mengganggu.
Apa yang harus dilakukan? Pertama, Anda harus selalu menangani kesalahan. Jika ada kesalahan, harap jangan abaikan. Ada baiknya jika PostgreSQL kehilangan koneksi: itu memutar kembali transaksi, kita selamat. Tentang ini saya akan berhenti. Nah, jika ada kode yang tidak punya waktu untuk mengedit sama sekali, maka kami masih memiliki
max idle dalam transaksi - Anda dapat memasukkannya, dan itu hanya akan mematikan transaksi yang tidak aktif.

Kasus penanganan kesalahan yang umum adalah: PENGECUALIAN KETIKA NILAI LAIN DARI NULL. Pernah kami berdebat dengan seorang kolega tentang terminologi. Saya mengatakan bahwa itu diterjemahkan sebagai "bakar semuanya dengan api biru," dan yang dia maksudkan "semuanya sia-sia." Jika sesuatu yang buruk terjadi pada kami, maka, bahkan jika semuanya memarahi log, itu masih lebih baik daripada diam sepenuhnya - seperti di sini.

Jika Anda tidak tahu apa yang harus dilakukan dengan kesalahan, maka jangan mencegatnya. Praktek yang sangat umum: mereka menangkap kesalahan, mencatatnya dan berlari seolah-olah tidak ada yang terjadi. Jika, sekali lagi, Anda terlibat dalam transaksi uang dan Anda memiliki kesalahan yang Anda abaikan, hasilnya mungkin tidak dapat diprediksi. Di tahun 90-an mereka bisa, misalnya, dibawa keluar ke dalam hutan di bagasi. Sekarang zaman telah menjadi lebih lembut, tetapi juga tidak terlalu menyenangkan.

Jika kami melakukan operasi pada klien, maka, sebagai aturan, kami mengembalikan nilai: semuanya berjalan dengan baik atau gagal. Dan kami memproses setiap kesalahan. Saya melihat bagaimana orang secara khusus menulis kode
plpgsql , di mana mereka menemukan kesalahan, menulis ke log itu, mereka berkata, ya, ada kesalahan dan cukup kasar, mereka memasukkan teks pesan mereka. Tapi SQLSTATE tidak kembali. Ini selalu dilakukan, jadi jika mereka lupa memeriksa sesuatu, maka mereka mulai mengalami masalah.
Semua orang, karena alasan tertentu, takut akan pengecualian - baik dalam
plpgsql dan dalam bahasa lain. Dan jika Anda tidak menciptakan sesuatu sendiri, tetapi menggunakan fitur standar bahasa, semuanya biasanya berjalan dengan baik. Terutama masalah ini sering terjadi ketika koneksi terputus. Telah jatuh, prosesnya
menganggur dalam transaksi , basis data terisi, kinerja menurun. Ngomong-ngomong, transaksi semacam itu mungkin masih meninggalkan kunci, tetapi untuk beberapa alasan ini tidak begitu umum. Oleh karena itu, tambahkan
akhirnya kesalahan pada kode pemrosesan dan di sana, bersihkan koneksi dan berikan kembali ke server.

Selain itu, jika Anda memiliki kendala yang terdefinisi dengan baik, Anda dapat melempar pengecualian bukan dari database, tetapi dari aplikasi saat memproses kesalahan. Di
musim semi ada
terjemahan pengecualian ,
di php , masing-masing,
set_exception_handler . Perhatikan alat-alat yang disediakan kerangka kerja Anda, mereka muncul di sana karena suatu alasan.
Jadi: jangan menangkap kesalahan yang Anda tidak tahu harus berbuat apa; kesalahan nama dengan cermat dan akurat; mengklasifikasikan kesalahan.

Secara pribadi, saya mengklasifikasikan berdasarkan kriteria tersebut: operasi dapat diulangi (misalnya, kami mengalami kebuntuan); operasi tidak dapat diulang, itu telah selesai; operasi tidak dapat dilakukan pada prinsipnya.
Paradoksnya, dari sudut pandang aplikasi, situasi ketika kebuntuan terjadi, ketika koneksi terputus dan ketika kita kehabisan uang untuk membayar adalah situasi yang sama: penangan kesalahan akan mencoba melakukan operasi lagi setelah beberapa saat.

Di sisi lain, apa yang mereka tulis dalam aplikasi, secara umum, bukan urusan saya: Saya terlibat dalam pangkalan. Saya hanya mendesak Anda untuk menangani kesalahan dengan hati-hati, jika tidak: menganggur dalam transaksi, baris terkunci, database bengkak dan sebagainya.
Sebagian besar pengembang percaya bahwa mereka bekerja dengan database saja, dan aplikasi mereka melakukan operasi secara berurutan. Dan ini merupakan nilai tambah untuk semua DBMS relasional karena, anehnya, semuanya berfungsi, sebagai aturan, sangat baik, bahkan dengan tingkat isolasi standar BACA BERKOMITMEN, dan tidak SERIALIZABLE. Pada saat yang sama, situasi terjadi ketika pembaruan hilang: satu memuat formulir, yang lain memuat formulir yang sama, satu menulis dan menyimpan, yang lain menyimpan yang lama - perubahan dihapus. Yang pertama bersumpah: "bagaimana begitu, saya menulis begitu banyak, dan semuanya hilang."

Dari pengalaman saya: sekali seminggu pada hari Jumat, dua manajer melakukan pembayaran. Mereka seharusnya
berubah setiap waktu, tetapi, bagaimanapun, pernah naik pada waktu yang sama dan melakukan dua pembayaran per orang. Jika Anda memiliki setidaknya beberapa kemungkinan kesalahan akses kompetitif, itu akan terjadi cepat atau lambat. Pertanyaannya adalah kapan.
Selain itu, saya menarik perhatian Anda pada keterbatasan. Saya telah berulang kali melihat bagaimana mereka mencoba memberikan keunikan dengan pemicu. Anda tidak akan memberikan keunikan dalam tabel dengan pemicu. Entah Anda harus memblokir seluruh tabel, atau melakukan beberapa gerakan kompleks lainnya. Anda akan menemukan ini cepat atau lambat.

Beberapa kali saya menemukan hal yang benar-benar mengerikan: layanan web eksternal dipanggil dari database. Ada beberapa operasi yang mengubah entitas eksternal. Ini buruk karena transaksi dapat digulirkan kembali ke dalam basis data, tetapi operasi pada layanan jarak jauh tidak akan ditolak.
Titik yang bahkan lebih halus adalah jalan buntu. Mari kita bayangkan: kita memproses transaksi, memanggil layanan web eksternal, mengubah sesuatu, setelah itu kita menemui jalan buntu, dan kita memutar balik, lalu kita mencoba melakukan operasi lagi, menelepon lagi, dalam keadaan baik, kebuntuan terjadi lagi, lagi putar kembali - itu bisa
terjadi berkali-kali (saya menemukan beberapa ratus pengulangan). Dan sekarang Anda memproses kebuntuan ini kurang lebih dengan benar, ulangi operasi dan tiba-tiba menemukan bahwa Anda telah membayar jumlah ganda kepada seseorang dalam waktu dua bulan.

Saya bertemu dengan layanan pembayaran yang memiliki API yang buruk: "bayar jumlah ini dan itu untuk pengguna ini dan itu"; fungsi mengembalikan hasilnya - dibayar / tidak dibayar. Pertama, ada masalah dalam hal pengulangan, dan kedua, tidak jelas apa yang harus dilakukan jika koneksi terputus. Untuk beberapa alasan, sangat sedikit orang yang peduli dengan masalah ini.

Contohnya ada di slide: operasi seperti itu harus dilakukan dalam dua tahap: seolah-olah peringatan - "kita akan melakukan sesuatu sekarang"; operasi itu sendiri.

Jika kami tiba-tiba menyela - Anda tidak pernah tahu, mematikan daya - kami dapat kembali menjalankan operasi. Jika kita mati pada tahap kedua, maka, di seluruh dunia, kedua kalinya kita tidak akan melakukannya, dan ini dapat dibongkar secara manual. Sebenarnya, sebagian besar operasi seperti itu biasanya berhasil untuk pertama kalinya, tetapi langkah-langkah ini bukan rekayasa teoritis. Semuanya bisa berfungsi normal selama berbulan-bulan, dan tiba-tiba admin mulai lebih bijak dengan jaringan, layanan mulai berkedip aktif - dan masalah dimulai.

Ada 4 jenis operasi yang tidak dapat dibatalkan pada slide. Yang terakhir adalah operasi non-idempoten. Ini adalah kasus yang sangat menyedihkan. Pada awalnya saya berbicara tentang seorang kawan yang melakukan semuanya dengan pemicu tepat untuk memastikan idempotensi operasinya.

Pada konferensi tersebut, orang-orang akan berbicara tentang Common Table Expressions, tentang betapa baiknya itu. Sayangnya, CTE PostgreSQL tidak gratis: mereka memerlukan work_mem sendiri. Jika Anda memiliki sampel kecil, maka secara umum tidak apa-apa. Dan jika Anda tiba-tiba memilikinya besar, maka masalah Anda mulai. Orang sangat sering menggunakan CTE sebagai semacam tampilan mini - sehingga Anda dapat membuat struktur aplikasi. CTE sangat diminati.


Anda dapat membuat tampilan sementara, tetapi, sayangnya, masing-masing mengambil baris di pg_class, dan jika ini sangat aktif digunakan, maka mungkin ada masalah dengan pembengkakan direktori.
Dalam hal ini, Anda dapat menyarankan untuk membuat tampilan parameter, atau secara dinamis membentuk kueri, tetapi, sayangnya, di PostgreSQL dari dalam, ini tidak terlalu keren.

JSON biasanya dibicarakan dengan nada yang sangat baik, tetapi ada kecenderungan dalam aplikasi di JSON untuk mendorong apa saja. Pada prinsipnya, semuanya bekerja dengan baik. Di sisi lain, data diambil dari JSON, meskipun cepat, tetapi tidak secepat dari kolom. Lebih buruk lagi, jika Anda memiliki JSON besar, dan diterbitkan dalam TOAST. Untuk mendapatkan JSON dari sana, Anda harus mengambilnya dari TOAST.
Jika semua kolom di JSON, indeks fungsional bahkan dibangun di atasnya, maka Anda masih perlu mengeluarkannya dari sana. Semakin buruk dengan volume besar, ketika database besar, ketika Anda memiliki
scan indeks bitmap . Kemudian kita memiliki tautan bukan ke string, tetapi ke seluruh halaman, dan untuk memahami apa yang harus diambil dari halaman, PostgreSQL akan membuat
Periksa ulang , yaitu, mengangkat garis dari TOAST dan memeriksa apakah nilai ini ada atau tidak, dan karenanya sudah melompat atau tidak melompat. Jika dengan kolom kecil ini berfungsi dengan baik, maka dengan JSON ini adalah masalah besar. Tidak perlu terlalu terbawa dengan JSON.
- Bagaimana cara memeriksa ketika beberapa pengguna bekerja dengan string? Opsi apa yang ada?- Pertama, Anda dapat mengurangi nilai semua kolom dan memastikan bahwa mereka tidak berubah sebelum menampilkan baris dalam formulir. Opsi kedua, lebih nyaman: hitung hash sama sekali
kolom, terutama karena kolom mungkin ada yang besar dan tebal. Dan hash tidak begitu besar.
- Anda mengatakan bahwa kendala harus disebut nama baik sehingga pengguna dapat memahami apa yang terjadi. Tetapi ada batas 60 karakter per nama kendala. Ini seringkali tidak cukup. Bagaimana cara mengatasinya?- Saya pikir untuk bertarung dengan menahan diri. Di PostgreSQL, ini adalah tipe khusus panjang 64. Pada prinsipnya, Anda dapat mengkompilasi ulang ke panjang yang lebih panjang, tetapi ini tidak terlalu baik.
- Dalam laporan itu, Anda membuat kami penasaran dengan fakta bahwa kami perlu melakukan sesuatu dengan arsip. Mekanisme apa yang dianggap paling benar untuk pengarsipan yang ketinggalan zaman?- Seperti yang saya katakan di awal, dengan ketekunan semuanya bekerja. Metode mana yang paling nyaman bagi Anda, jadi gunakan saja.
Waktu: Bagian 2 dari laporan ini dimulai pada 25:16- Ada prosedur tertentu yang dilakukan beberapa pengguna secara paralel. Bagaimana membatasi eksekusi paralel dari prosedur ini, yaitu, untuk membangun semua
pengguna dalam antrian sehingga sampai prosedur selesai, yang berikutnya tidak dapat mulai menggunakannya?- Justru prosedurnya? Atau cukup transaksi?
- Ini adalah prosedur yang disebut dalam beberapa transaksi.- Anda bisa meletakkan kunci pada objek. Akan sulit jika Anda memiliki kondisi, katakanlah, tidak lebih dari 3 pada saat yang sama. Tapi ini bisa diwujudkan. Saya biasanya menggunakan kunci transaksional, tetapi yang non-transaksional juga dimungkinkan.
- Saya masih ingin sekali lagi kembali ke data arsip. Kamu berbicara tentang
opsi penyimpanan arsip sehingga data dari aplikasi juga tersedia. Terpikir oleh saya untuk hanya membuat database arsip terpisah. Opsi apa lagi yang ada?- Ya, Anda bisa membuat database arsip. Anda dapat menulis fungsi dan membungkusnya dalam tampilan. Dalam suatu fungsi, Anda dapat melakukan apapun yang diperlukan: Anda dapat pergi ke database arsip, Anda dapat mengambil beberapa file dari disk, Anda dapat pergi ke layanan web eksternal, Anda dapat menggabungkan semua ini, Anda dapat menghasilkan beberapa data acak sendiri - pilihan hanya dibatasi oleh imajinasi.
- Untuk pertanyaan tentang data arsip: Anda dapat menggunakan partisi - chip baru dari versi ke-11, saat kami membuat seluruh tabel dipartisi, dan kemudian cukup merinci partisi dan membiarkannya sebagai arsip. Itu juga dapat diakses."Tentu saja, mengapa tidak." Saya memberi jalan kepada pembicara berikutnya.