Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Laporan itu membentangkan beberapa pendekatan yang membolehkan memantau prestasi pertanyaan SQL apabila terdapat berjuta-juta pertanyaan setiap hari, dan terdapat ratusan pelayan PostgreSQL yang dipantau.

Apakah penyelesaian teknikal yang membolehkan kami memproses jumlah maklumat sedemikian dengan cekap, dan bagaimana ini menjadikan kehidupan pembangun biasa lebih mudah?


Siapa berminat? analisis masalah khusus dan pelbagai teknik pengoptimuman Pertanyaan SQL dan menyelesaikan masalah DBA biasa dalam PostgreSQL - anda juga boleh membaca satu siri artikel mengenai topik ini.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)
Nama saya Kirill Borovikov, saya mewakili syarikat Tensor. Khususnya, saya pakar dalam bekerja dengan pangkalan data di syarikat kami.

Hari ini saya akan memberitahu anda cara kami mengoptimumkan pertanyaan, apabila anda tidak perlu "memisahkan" prestasi satu pertanyaan, tetapi menyelesaikan masalah secara beramai-ramai. Apabila terdapat berjuta-juta permintaan, dan anda perlu mencari beberapa pendekatan penyelesaian masalah besar ini.

Secara umum, Tensor untuk sejuta pelanggan kami adalah VLSI ialah aplikasi kami: rangkaian sosial korporat, penyelesaian untuk komunikasi video, untuk aliran dokumen dalaman dan luaran, sistem perakaunan untuk perakaunan dan gudang,... Iaitu, "mega-combine" untuk pengurusan perniagaan bersepadu, di mana terdapat lebih daripada 100 berbeza projek dalaman.

Untuk memastikan mereka semua berfungsi dan membangun seperti biasa, kami mempunyai 10 pusat pembangunan di seluruh negara, dengan lebih banyak lagi di dalamnya 1000 pemaju.

Kami telah bekerja dengan PostgreSQL sejak 2008 dan telah mengumpul sejumlah besar perkara yang kami proses - data pelanggan, statistik, analisis, data daripada sistem maklumat luaran - lebih daripada 400TB. Terdapat kira-kira 250 pelayan dalam pengeluaran sahaja, dan secara keseluruhan terdapat kira-kira 1000 pelayan pangkalan data yang kami pantau.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

SQL ialah bahasa deklaratif. Anda tidak menerangkan "bagaimana" sesuatu harus berfungsi, tetapi "apa" yang ingin anda capai. DBMS lebih mengetahui cara membuat JOIN - cara menyambungkan jadual anda, syarat yang perlu dikenakan, perkara yang akan melalui indeks, perkara yang tidak...

Sesetengah DBMS menerima petunjuk: "Tidak, sambungkan kedua-dua jadual ini dalam baris gilir begini dan begitu," tetapi PostgreSQL tidak boleh melakukan ini. Ini adalah kedudukan sedar pembangun terkemuka: "Kami lebih suka menyelesaikan pengoptimuman pertanyaan daripada membenarkan pembangun menggunakan beberapa jenis petunjuk."

Tetapi, walaupun pada hakikatnya PostgreSQL tidak membenarkan "luar" mengawal dirinya, ia membenarkannya dengan sempurna lihat apa yang berlaku dalam dirinyaapabila anda menjalankan pertanyaan anda, dan di mana ia menghadapi masalah.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Secara umum, apakah masalah klasik yang biasanya dihadapi oleh pembangun [kepada DBA]? “Di sini kami memenuhi permintaan, dan semuanya lambat dengan kami, segala-galanya tergantung, sesuatu sedang berlaku... Semacam masalah!”

Sebabnya hampir selalu sama:

  • algoritma pertanyaan yang tidak cekap
    Pembangun: "Sekarang saya memberinya 10 jadual dalam SQL melalui JOIN..." - dan menjangkakan bahawa keadaannya secara ajaib akan "diikat" dengan berkesan dan dia akan mendapat segala-galanya dengan cepat. Tetapi keajaiban tidak berlaku, dan mana-mana sistem dengan kebolehubahan sedemikian (10 jadual dalam satu DARI) sentiasa memberikan beberapa jenis ralat. [artikel]
  • statistik lapuk
    Perkara ini sangat relevan khusus untuk PostgreSQL, apabila anda "menuangkan" set data yang besar ke pelayan, membuat permintaan, dan ia "menyesuaikan" tablet anda. Kerana semalam terdapat 10 rekod di dalamnya, dan hari ini terdapat 10 juta, tetapi PostgreSQL masih belum mengetahui perkara ini, dan kami perlu memberitahunya mengenainya. [artikel]
  • "palam" pada sumber
    Anda telah memasang pangkalan data yang besar dan banyak dimuatkan pada pelayan lemah yang tidak mempunyai prestasi cakera, memori atau pemproses yang mencukupi. Dan itu sahaja... Di suatu tempat terdapat siling prestasi di atasnya yang anda tidak boleh lompat lagi.
  • menyekat
    Ini adalah perkara yang sukar, tetapi ia paling relevan untuk pelbagai pertanyaan pengubahsuaian (INSERT, UPDATE, DELETE) - ini adalah topik besar yang berasingan.

Mendapatkan rancangan

... Dan untuk semua yang lain kita perlukan rancangan! Kita perlu melihat apa yang berlaku di dalam pelayan.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Pelan pelaksanaan pertanyaan untuk PostgreSQL ialah pepohon algoritma pelaksanaan pertanyaan dalam perwakilan teks. Ia adalah tepat algoritma yang, sebagai hasil analisis oleh perancang, didapati paling berkesan.

Setiap nod pokok ialah operasi: mendapatkan semula data daripada jadual atau indeks, membina peta bit, mencantumkan dua jadual, mencantum, bersilang atau mengecualikan pilihan. Melaksanakan pertanyaan melibatkan berjalan melalui nod pokok ini.

Untuk mendapatkan pelan pertanyaan, cara paling mudah ialah melaksanakan pernyataan EXPLAIN. Untuk mendapatkan semua atribut sebenar, iaitu, untuk benar-benar melaksanakan pertanyaan pada pangkalan - EXPLAIN (ANALYZE, BUFFERS) SELECT ....

Bahagian buruk: apabila anda menjalankannya, ia berlaku "di sini dan sekarang", jadi ia hanya sesuai untuk nyahpepijat setempat. Jika anda mengambil pelayan yang sarat tinggi yang berada di bawah aliran perubahan data yang kuat, dan anda melihat: “Oh! Di sini kita mempunyai pelaksanaan yang perlahansya permintaan." Setengah jam, sejam yang lalu - semasa anda menjalankan dan mendapatkan permintaan ini daripada log, membawanya kembali ke pelayan, keseluruhan set data dan statistik anda berubah. Anda menjalankannya untuk nyahpepijat - dan ia berjalan dengan cepat! Dan anda tidak boleh memahami mengapa, mengapa было perlahan-lahan.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Untuk memahami apa yang berlaku tepat pada masa permintaan itu dilaksanakan pada pelayan, orang pintar menulis modul auto_explain. Ia hadir dalam hampir semua pengedaran PostgreSQL yang paling biasa, dan hanya boleh diaktifkan dalam fail konfigurasi.

Jika ia menyedari bahawa sesetengah permintaan berjalan lebih lama daripada had yang anda katakan, ia akan berlaku "snapshot" pelan permintaan ini dan menulisnya bersama-sama dalam log.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Semuanya nampak baik-baik saja sekarang, kita pergi ke log dan lihat di sana... [teks kain kaki]. Tetapi kami tidak boleh mengatakan apa-apa mengenainya, selain fakta bahawa ia adalah rancangan yang sangat baik kerana ia mengambil masa 11ms untuk dilaksanakan.

Segala-galanya nampaknya baik-baik saja - tetapi tiada yang jelas apa yang sebenarnya berlaku. Selain daripada masa umum, kita tidak nampak apa-apa. Kerana melihat "kambing biri-biri" teks biasa secara amnya bukan visual.

Tetapi walaupun ia tidak jelas, walaupun ia menyusahkan, terdapat lebih banyak masalah asas:

  • Nod menunjukkan jumlah sumber keseluruhan subpokok bawah dia. Maksudnya, anda tidak boleh mengetahui berapa banyak masa yang dibelanjakan untuk Imbasan Indeks tertentu ini jika terdapat beberapa keadaan bersarang di bawahnya. Kita mesti melihat secara dinamik untuk melihat sama ada terdapat "kanak-kanak" dan pembolehubah bersyarat, CTE di dalam - dan tolak semua ini "dalam fikiran kita".
  • Titik kedua: masa yang ditunjukkan pada nod ialah masa pelaksanaan nod tunggal. Jika nod ini dilaksanakan sebagai hasil daripada, sebagai contoh, satu gelung melalui jadual merekodkan beberapa kali, maka bilangan gelung—kitaran nod ini—meningkat dalam pelan. Tetapi masa pelaksanaan atom itu sendiri tetap sama dari segi rancangan. Iaitu, untuk memahami berapa lama nod ini dilakukan secara keseluruhan, anda perlu mendarabkan satu perkara dengan yang lain - sekali lagi, "di kepala anda."

Dalam situasi sedemikian, fahami "Siapakah pautan paling lemah?" hampir mustahil. Oleh itu, walaupun pemaju sendiri menulis dalam "manual" itu “Memahami rancangan adalah seni yang mesti dipelajari, pengalaman...”.

Tetapi kami mempunyai 1000 pembangun, dan anda tidak boleh menyampaikan pengalaman ini kepada setiap daripada mereka. Saya, awak, dia tahu, tetapi seseorang di sana tidak tahu lagi. Mungkin dia akan belajar, atau mungkin tidak, tetapi dia perlu bekerja sekarang - dan di manakah dia akan mendapat pengalaman ini?

Visualisasi rancangan

Oleh itu, kami menyedari bahawa untuk menangani masalah ini, kami memerlukan visualisasi rancangan yang baik. [artikel]

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Kami mula-mula pergi "melalui pasaran" - mari lihat di Internet untuk melihat apa yang wujud.

Tetapi ternyata terdapat sangat sedikit penyelesaian yang agak "hidup" yang lebih kurang berkembang - secara harfiah, hanya satu: explain.depesz.com oleh Hubert Lubaczewski. Apabila anda memasukkan medan "suapan" perwakilan teks rancangan, ia menunjukkan kepada anda jadual dengan data yang dihuraikan:

  • masa pemprosesan nod sendiri
  • jumlah masa untuk keseluruhan subpokok
  • bilangan rekod yang telah diambil semula yang dijangkakan secara statistik
  • badan nod itu sendiri

Perkhidmatan ini juga mempunyai keupayaan untuk berkongsi arkib pautan. Anda melemparkan rancangan anda ke sana dan berkata: "Hei, Vasya, ini pautan, ada sesuatu yang tidak kena di sana."

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Tetapi terdapat juga masalah kecil.

Pertama, sejumlah besar "copy-paste". Anda mengambil sekeping kayu balak, lekatkannya di sana, dan sekali lagi, dan sekali lagi.

Kedua, tiada analisis jumlah data yang dibaca — penimbal yang sama yang dikeluarkan EXPLAIN (ANALYZE, BUFFERS), kami tidak nampak di sini. Dia hanya tidak tahu bagaimana untuk membongkarnya, memahaminya dan bekerja dengan mereka. Apabila anda membaca banyak data dan menyedari bahawa anda mungkin salah memperuntukkan cakera dan cache memori, maklumat ini sangat penting.

Perkara negatif ketiga ialah pembangunan projek ini yang sangat lemah. Komit adalah sangat kecil, adalah baik jika sekali setiap enam bulan, dan kod itu dalam Perl.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Tetapi ini semua adalah "lirik", entah bagaimana kami boleh hidup dengan ini, tetapi ada satu perkara yang sangat menjauhkan kami daripada perkhidmatan ini. Ini adalah ralat dalam analisis Ungkapan Jadual Biasa (CTE) dan pelbagai nod dinamik seperti InitPlan/SubPlan.

Jika anda mempercayai gambar ini, maka jumlah masa pelaksanaan setiap nod individu adalah lebih besar daripada jumlah masa pelaksanaan keseluruhan permintaan. Ia mudah - masa penjanaan CTE ini tidak ditolak daripada nod Imbasan CTE. Oleh itu, kami tidak lagi mengetahui jawapan yang betul tentang berapa lama imbasan CTE itu sendiri mengambil masa.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Kemudian kami menyedari bahawa sudah tiba masanya untuk menulis kami sendiri - hore! Setiap pembangun berkata: "Sekarang kami akan menulis sendiri, ia akan menjadi sangat mudah!"

Kami mengambil timbunan biasa untuk perkhidmatan web: teras berdasarkan Node.js + Express, Bootstrap dan D3.js yang digunakan untuk gambar rajah yang cantik. Dan jangkaan kami adalah wajar sepenuhnya - kami menerima prototaip pertama dalam 2 minggu:

  • penghurai pelan tersuai
    Iaitu, kini kita boleh menghuraikan sebarang rancangan daripada yang dihasilkan oleh PostgreSQL.
  • analisis yang betul bagi nod dinamik - Imbasan CTE, InitPlan, SubPlan
  • analisis taburan penimbal - di mana halaman data dibaca dari memori, di mana dari cache tempatan, di mana dari cakera
  • mendapat kejelasan
    Supaya tidak "menggali" semua ini dalam log, tetapi untuk melihat "pautan paling lemah" dengan segera dalam gambar.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Kami mendapat sesuatu seperti ini, dengan penyerlahan sintaks disertakan. Tetapi biasanya pembangun kami tidak lagi berfungsi dengan perwakilan lengkap rancangan itu, tetapi dengan yang lebih pendek. Lagipun, kami telah menghuraikan semua nombor dan melemparkannya ke kiri dan kanan, dan di tengah kami hanya meninggalkan baris pertama, jenis nodnya: CTE Scan, penjanaan CTE atau Seq Scan mengikut beberapa tanda.

Ini adalah perwakilan singkatan yang kami panggil templat rancangan.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Apa lagi yang sesuai? Adalah mudah untuk melihat bahagian jumlah masa kita yang diperuntukkan kepada nod mana - dan hanya "melekatkannya" ke tepi carta pai.

Kami menunjuk pada nod dan melihat - ternyata Seq Scan mengambil masa kurang daripada satu perempat daripada jumlah masa, dan baki 3/4 telah diambil oleh CTE Scan. Seram! Ini ialah nota kecil tentang "kadar kebakaran" CTE Scan jika anda secara aktif menggunakannya dalam pertanyaan anda. Mereka tidak begitu pantas - mereka lebih rendah daripada pengimbasan meja biasa. [artikel] [artikel]

Tetapi biasanya gambar rajah seperti itu lebih menarik, lebih kompleks, apabila kita segera menunjuk pada segmen dan melihat, sebagai contoh, bahawa lebih daripada separuh masa beberapa Seq Scan "makan". Lebih-lebih lagi, terdapat beberapa jenis Penapis di dalamnya, banyak rekod dibuang mengikutnya... Anda boleh terus membuang gambar ini kepada pemaju dan berkata: "Vasya, semuanya tidak baik untuk anda! Fikirkan, lihat - ada sesuatu yang tidak kena!”

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Sememangnya, terdapat beberapa "rakes" yang terlibat.

Perkara pertama yang kami temui ialah masalah pembundaran. Masa setiap nod individu dalam pelan ditunjukkan dengan ketepatan 1 μs. Dan apabila bilangan kitaran nod melebihi, sebagai contoh, 1000 - selepas pelaksanaan PostgreSQL dibahagikan "dalam ketepatan", maka apabila mengira kembali kita mendapat jumlah masa "di suatu tempat antara 0.95ms dan 1.05ms". Apabila kiraan pergi ke mikrosaat, tidak mengapa, tetapi apabila sudah [mili] saat, anda perlu mengambil kira maklumat ini apabila "membuka ikatan" sumber pada nod pelan "siapa yang menggunakan berapa banyak".

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Perkara kedua, lebih kompleks, ialah pengagihan sumber (penampan tersebut) di antara nod dinamik. Ini menelan belanja 2 minggu pertama prototaip ditambah 4 minggu lagi.

Agak mudah untuk mendapatkan masalah seperti ini - kami melakukan CTE dan kononnya membaca sesuatu di dalamnya. Malah, PostgreSQL adalah "pintar" dan tidak akan membaca apa-apa secara langsung di sana. Kemudian kami mengambil rekod pertama daripadanya, dan kepadanya rekod seratus dan pertama daripada CTE yang sama.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Kami melihat pelan itu dan faham - pelik, kami mempunyai 3 penimbal (halaman data) "digunakan" dalam Seq Scan, 1 lagi dalam CTE Scan dan 2 lagi dalam CTE Scan kedua. Iaitu, jika kita ringkaskan semuanya, kita akan mendapat 6, tetapi dari tablet kita hanya membaca 3! CTE Scan tidak membaca apa-apa dari mana-mana, tetapi berfungsi secara langsung dengan memori proses. Iaitu, sesuatu yang jelas tidak kena di sini!

Malah, ternyata di sini adalah semua 3 halaman data yang diminta daripada Seq Scan, pertama 1 meminta Imbasan CTE pertama, dan kemudian yang ke-1, dan 2 lagi dibacakan kepadanya. Iaitu, sejumlah 2 muka surat telah membaca data, bukan 3.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Dan gambar ini membawa kita kepada pemahaman bahawa pelaksanaan rancangan bukan lagi pokok, tetapi hanya sejenis graf akiklik. Dan kami mendapat gambar rajah seperti ini, supaya kami memahami "apa yang datang dari mana pada mulanya." Iaitu, di sini kami mencipta CTE daripada pg_class, dan memintanya dua kali, dan hampir semua masa kami dihabiskan di cawangan apabila kami memintanya untuk kali ke-2. Jelas bahawa membaca entri ke-101 adalah lebih mahal daripada hanya membaca entri pertama dari tablet.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Kami menghembus nafas seketika. Mereka berkata: “Sekarang, Neo, anda tahu kung fu! Kini pengalaman kami ada pada skrin anda. Sekarang anda boleh menggunakannya." [artikel]

Penyatuan log

1000 pembangun kami menarik nafas lega. Tetapi kami faham bahawa kami hanya mempunyai beratus-ratus pelayan "pertempuran", dan semua "salin-tampal" ini di pihak pembangun tidak sesuai sama sekali. Kami menyedari bahawa kami perlu mengumpulnya sendiri.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Secara umum, terdapat modul standard yang boleh mengumpul statistik, bagaimanapun, ia juga perlu diaktifkan dalam konfigurasi - ini modul pg_stat_statements. Tetapi dia tidak sesuai dengan kita.

Pertama, ia memberikan kepada pertanyaan yang sama menggunakan skema yang berbeza dalam pangkalan data yang sama QueryId yang berbeza. Iaitu, jika anda mula-mula melakukannya SET search_path = '01'; SELECT * FROM user LIMIT 1;dan kemudian SET search_path = '02'; dan permintaan yang sama, maka statistik modul ini akan mempunyai rekod yang berbeza, dan saya tidak akan dapat mengumpul statistik umum secara khusus dalam konteks profil permintaan ini, tanpa mengambil kira skema.

Perkara kedua yang menghalang kami daripada menggunakannya ialah kekurangan rancangan. Maksudnya, tiada rancangan, yang ada hanyalah permintaan itu sendiri. Kami melihat apa yang perlahan, tetapi kami tidak faham mengapa. Dan di sini kita kembali kepada masalah set data yang berubah dengan pantas.

Dan saat terakhir - kekurangan "fakta". Iaitu, anda tidak boleh menangani contoh pelaksanaan pertanyaan tertentu - tidak ada, hanya ada statistik agregat. Walaupun ada kemungkinan untuk bekerja dengan ini, ia hanya sangat sukar.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Oleh itu, kami memutuskan untuk melawan copy-paste dan mula menulis pengumpul.

Pengumpul menyambung melalui SSH, mewujudkan sambungan selamat ke pelayan dengan pangkalan data menggunakan sijil, dan tail -F "berpaut" padanya dalam fail log. Jadi dalam sesi ini kami mendapat "cermin" lengkap bagi keseluruhan fail log, yang dijana oleh pelayan. Beban pada pelayan itu sendiri adalah minimum, kerana kami tidak menghuraikan apa-apa di sana, kami hanya mencerminkan lalu lintas.

Memandangkan kami sudah mula menulis antara muka dalam Node.js, kami terus menulis pengumpul di dalamnya. Dan teknologi ini telah membenarkan dirinya sendiri, kerana ia sangat mudah untuk menggunakan JavaScript untuk bekerja dengan data teks yang diformat dengan lemah, iaitu log. Dan infrastruktur Node.js itu sendiri sebagai platform bahagian belakang membolehkan anda dengan mudah dan senang bekerja dengan sambungan rangkaian, dan sememangnya dengan mana-mana aliran data.

Oleh itu, kami "meregangkan" dua sambungan: yang pertama untuk "mendengar" log itu sendiri dan membawanya kepada diri kita sendiri, dan yang kedua untuk bertanya pangkalan secara berkala. "Tetapi log menunjukkan bahawa tanda dengan oid 123 disekat," tetapi ini tidak bermakna apa-apa kepada pembangun, dan adalah baik untuk bertanya kepada pangkalan data, "Apakah OID = 123 pula?" Oleh itu, kami secara berkala bertanya kepada asas apa yang kami belum tahu tentang diri kami sendiri.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

"Hanya satu perkara yang anda tidak ambil kira, ada spesies lebah seperti gajah!.." Kami mula membangunkan sistem ini apabila kami ingin memantau 10 pelayan. Yang paling kritikal dalam pemahaman kami, di mana timbul beberapa masalah yang sukar untuk ditangani. Tetapi pada suku pertama, kami menerima seratus untuk pemantauan - kerana sistem itu berfungsi, semua orang mahukannya, semua orang selesa.

Semua ini perlu ditambah, aliran data adalah besar dan aktif. Sebenarnya, apa yang kita pantau, apa yang boleh kita tangani, itulah yang kita gunakan. Kami juga menggunakan PostgreSQL sebagai storan data. Dan tiada yang lebih pantas untuk "menuangkan" data ke dalamnya daripada pengendali COPY Belum lagi.

Tetapi hanya "menuangkan" data bukanlah teknologi kami sebenarnya. Kerana jika anda mempunyai kira-kira 50k permintaan sesaat pada seratus pelayan, maka ini akan menjana 100-150GB log setiap hari. Oleh itu, kami perlu berhati-hati "memotong" pangkalan.

Pertama, kami lakukan pembahagian mengikut hari, kerana, pada umumnya, tiada siapa yang berminat dengan korelasi antara hari. Apakah perbezaannya dengan apa yang anda miliki semalam, jika malam ini anda melancarkan versi baharu aplikasi - dan sudah pun beberapa statistik baharu.

Kedua, kami belajar (dipaksa) sangat, sangat pantas untuk menulis menggunakan COPY. Iaitu, bukan sahaja COPYkerana dia lebih pantas daripada INSERT, dan lebih pantas lagi.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Perkara ketiga - saya terpaksa tinggalkan pencetus, masing-masing, dan kunci asing. Iaitu, kita tidak mempunyai integriti rujukan sama sekali. Kerana jika anda mempunyai jadual yang mempunyai sepasang FK, dan anda berkata dalam struktur pangkalan data bahawa "di sini ialah rekod log yang dirujuk oleh FK, sebagai contoh, kepada sekumpulan rekod," maka apabila anda memasukkannya, PostgreSQL tiada apa yang tinggal melainkan bagaimana untuk mengambilnya dan melakukannya dengan jujur SELECT 1 FROM master_fk1_table WHERE ... dengan pengecam yang anda cuba masukkan - hanya untuk memeriksa sama ada rekod ini ada di sana, bahawa anda tidak "mematahkan" Kunci Asing ini dengan sisipan anda.

Daripada satu rekod ke jadual sasaran dan indeksnya, kami mendapat faedah tambahan membaca daripada semua jadual yang dirujuknya. Tetapi kami tidak memerlukan ini sama sekali - tugas kami adalah untuk merakam sebanyak mungkin dan secepat mungkin dengan beban paling sedikit. Jadi FK - turun!

Perkara seterusnya ialah pengagregatan dan pencincangan. Pada mulanya, kami melaksanakannya dalam pangkalan data - lagipun, ia mudah untuk segera, apabila rekod tiba, melakukannya dalam beberapa jenis tablet "tambah satu" betul-betul di picu. Nah, ia mudah, tetapi perkara buruk yang sama - anda memasukkan satu rekod, tetapi terpaksa membaca dan menulis sesuatu yang lain dari jadual lain. Selain itu, bukan sahaja anda membaca dan menulis, anda juga melakukannya setiap masa.

Sekarang bayangkan anda mempunyai jadual di mana anda hanya mengira bilangan permintaan yang telah melalui hos tertentu: +1, +1, +1, ..., +1. Dan anda, pada dasarnya, tidak memerlukan ini - semuanya mungkin jumlah dalam ingatan pada pengumpul dan hantar ke pangkalan data sekali gus +10.

Ya, dalam kes beberapa masalah, integriti logik anda mungkin "berpecah", tetapi ini adalah kes yang hampir tidak realistik - kerana anda mempunyai pelayan biasa, ia mempunyai bateri dalam pengawal, anda mempunyai log transaksi, log pada sistem fail... Secara umum, ia tidak berbaloi. Kehilangan produktiviti yang anda perolehi daripada menjalankan pencetus/FK tidak berbaloi dengan perbelanjaan yang anda tanggung.

Ia sama dengan pencincangan. Permintaan tertentu terbang kepada anda, anda mengira pengecam tertentu daripadanya dalam pangkalan data, menulisnya ke pangkalan data dan kemudian memberitahunya kepada semua orang. Semuanya baik-baik saja sehingga, pada masa rakaman, orang kedua datang kepada anda yang ingin merakam perkara yang sama - dan anda disekat, dan ini sudah teruk. Oleh itu, jika anda boleh memindahkan penjanaan beberapa ID kepada pelanggan (berbanding dengan pangkalan data), adalah lebih baik untuk melakukan ini.

Ia adalah sesuai untuk kami menggunakan MD5 daripada teks - permintaan, pelan, templat,... Kami mengiranya di sebelah pengumpul, dan "menuangkan" ID siap pakai ke dalam pangkalan data. Panjang MD5 dan pembahagian harian membolehkan kami tidak bimbang tentang kemungkinan perlanggaran.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Tetapi untuk merakam semua ini dengan cepat, kami perlu mengubah suai prosedur rakaman itu sendiri.

Bagaimanakah anda biasanya menulis data? Kami mempunyai beberapa jenis set data, kami membahagikannya kepada beberapa jadual, dan kemudian SALIN - pertama ke yang pertama, kemudian ke yang kedua, ke yang ketiga... Ia menyusahkan, kerana kami nampaknya menulis satu aliran data dalam tiga langkah secara berurutan. Tidak menyenangkan. Bolehkah ia dilakukan dengan lebih cepat? Boleh!

Untuk melakukan ini, cukup hanya untuk menguraikan aliran ini selari antara satu sama lain. Ternyata kami mempunyai ralat, permintaan, templat, penyekatan, ... terbang dalam benang berasingan - dan kami menulis semuanya secara selari. Cukup untuk ini pastikan saluran COPY sentiasa terbuka untuk setiap jadual sasaran individu.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Iaitu, pada pengumpul sentiasa ada aliran, di mana saya boleh menulis data yang saya perlukan. Tetapi supaya pangkalan data melihat data ini, dan seseorang tidak terperangkap menunggu data ini ditulis, COPY mesti diganggu pada selang masa tertentu. Bagi kami, tempoh yang paling berkesan ialah kira-kira 100ms - kami menutupnya dan segera membukanya semula ke meja yang sama. Dan jika kami tidak mempunyai cukup satu aliran semasa beberapa puncak, maka kami melakukan pengumpulan sehingga had tertentu.

Selain itu, kami mendapati bahawa untuk profil beban sedemikian, sebarang pengagregatan, apabila rekod dikumpulkan dalam kelompok, adalah jahat. Kejahatan klasik adalah INSERT ... VALUES dan 1000 rekod lagi. Kerana pada ketika itu anda mempunyai puncak tulis pada media, dan orang lain yang cuba menulis sesuatu ke cakera akan menunggu.

Untuk menghilangkan anomali seperti itu, jangan agregat apa-apa, tidak menampan sama sekali. Dan jika penimbal ke cakera berlaku (nasib baik, Stream API dalam Node.js membolehkan anda mengetahuinya) - tangguhkan sambungan ini. Apabila anda menerima acara bahawa ia adalah percuma lagi, tulis kepadanya daripada baris gilir terkumpul. Dan semasa sibuk, ambil yang seterusnya percuma dari kolam dan tulis kepadanya.

Sebelum memperkenalkan pendekatan ini kepada rakaman data, kami mempunyai kira-kira 4K op tulis, dan dengan cara ini kami mengurangkan beban sebanyak 4 kali. Kini mereka telah berkembang 6 kali lagi kerana pangkalan data yang dipantau baharu - sehingga 100MB/s. Dan kini kami menyimpan log selama 3 bulan terakhir dalam jumlah kira-kira 10-15TB, dengan harapan bahawa dalam masa tiga bulan sahaja mana-mana pembangun akan dapat menyelesaikan sebarang masalah.

Kami memahami masalah

Tetapi hanya mengumpul semua data ini adalah baik, berguna, relevan, tetapi tidak mencukupi - ia perlu difahami. Kerana ini adalah berjuta-juta rancangan yang berbeza setiap hari.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Tetapi berjuta-juta tidak terurus, kita mesti terlebih dahulu melakukan "lebih kecil". Dan, pertama sekali, anda perlu memutuskan bagaimana anda akan mengatur perkara "lebih kecil" ini.

Kami telah mengenal pasti tiga perkara utama:

  • yang menghantar permintaan ini
    Iaitu, dari aplikasi apakah ia "tiba": antara muka web, bahagian belakang, sistem pembayaran atau sesuatu yang lain.
  • mana ia berlaku
    Pada pelayan khusus apa? Kerana jika anda mempunyai beberapa pelayan di bawah satu aplikasi, dan tiba-tiba satu "menjadi bodoh" (kerana "cakera busuk", "memori bocor", beberapa masalah lain), maka anda perlu menangani pelayan secara khusus.
  • sebagai masalah itu nyata dalam satu cara atau yang lain

Untuk memahami "siapa" yang menghantar permintaan kepada kami, kami menggunakan alat standard - menetapkan pembolehubah sesi: SET application_name = '{bl-host}:{bl-method}'; — kami menghantar nama hos logik perniagaan dari mana permintaan itu datang, dan nama kaedah atau aplikasi yang memulakannya.

Selepas kami telah meluluskan "pemilik" permintaan, ia mesti dikeluarkan ke log - untuk ini kami mengkonfigurasi pembolehubah log_line_prefix = ' %m [%p:%v] [%d] %r %a'. Siapa peduli, mungkin lihat dalam manualapa erti semua itu. Ternyata kita lihat dalam log:

  • masa
  • pengecam proses dan transaksi
  • nama pangkalan data
  • IP orang yang menghantar permintaan ini
  • dan nama kaedah

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Kemudian kami menyedari bahawa tidak begitu menarik untuk melihat korelasi untuk satu permintaan antara pelayan yang berbeza. Tidak selalunya anda mengalami situasi di mana satu aplikasi berpusing sama rata di sana sini. Tetapi walaupun ia sama, lihat mana-mana pelayan ini.

Jadi inilah potongannya "satu pelayan - satu hari" ternyata cukup untuk kami untuk sebarang analisis.

Bahagian analisis pertama adalah sama "sampel" - bentuk ringkasan pembentangan pelan, dibersihkan daripada semua penunjuk berangka. Pemotongan kedua ialah aplikasi atau kaedah, dan pemotongan ketiga ialah nod pelan khusus yang menyebabkan masalah kepada kami.

Apabila kami beralih daripada contoh tertentu kepada templat, kami mendapat dua kelebihan sekaligus:

  • pengurangan berganda dalam bilangan objek untuk analisis
    Kita perlu menganalisis masalah itu bukan lagi dengan beribu-ribu pertanyaan atau rancangan, tetapi dengan berpuluh-puluh templat.
  • garis masa
    Iaitu, dengan meringkaskan "fakta" dalam bahagian tertentu, anda boleh memaparkan penampilan mereka pada siang hari. Dan di sini anda boleh memahami bahawa jika anda mempunyai beberapa jenis corak yang berlaku, sebagai contoh, sekali sejam, tetapi ia sepatutnya berlaku sekali sehari, anda harus memikirkan apa yang salah - siapa yang menyebabkannya dan mengapa, mungkin ia sepatutnya berada di sini tidak sepatutnya. Ini adalah satu lagi kaedah analisis bukan berangka, visual semata-mata.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Kaedah yang selebihnya adalah berdasarkan penunjuk yang kami ekstrak daripada pelan: berapa kali corak sedemikian berlaku, jumlah dan purata masa, berapa banyak data dibaca dari cakera, dan berapa banyak dari memori...

Kerana, sebagai contoh, anda datang ke halaman analitik untuk hos, lihat - sesuatu mula membaca terlalu banyak pada cakera. Cakera pada pelayan tidak dapat mengendalikannya - siapa yang membaca daripadanya?

Dan anda boleh mengisih mengikut mana-mana lajur dan memutuskan perkara yang akan anda hadapi sekarang - beban pada pemproses atau cakera, atau jumlah bilangan permintaan... Kami mengisihnya, melihat yang "atas", membetulkannya dan melancarkan versi baharu aplikasi.
[syarahan video]

Dan serta-merta anda boleh melihat aplikasi berbeza yang datang dengan templat yang sama daripada permintaan seperti SELECT * FROM users WHERE login = 'Vasya'. Bahagian hadapan, bahagian belakang, pemprosesan... Dan anda tertanya-tanya mengapa pemprosesan akan membaca pengguna jika dia tidak berinteraksi dengannya.

Cara yang bertentangan adalah dengan segera melihat dari aplikasi apa yang dilakukannya. Sebagai contoh, bahagian hadapan ialah ini, ini, ini dan ini sekali sejam (garis masa membantu). Dan persoalan segera timbul: nampaknya bukan tugas bahagian hadapan untuk melakukan sesuatu sekali sejam...

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Selepas beberapa lama, kami menyedari bahawa kami kekurangan agregat statistik mengikut nod pelan. Kami mengasingkan daripada pelan hanya nod yang melakukan sesuatu dengan data jadual itu sendiri (baca/tulisnya mengikut indeks atau tidak). Malah, hanya satu aspek yang ditambah berbanding gambar sebelumnya - berapa banyak rekod yang dibawa oleh nod ini kepada kami?, dan bilangan yang dibuang (Baris Dialih Keluar oleh Penapis).

Anda tidak mempunyai indeks yang sesuai pada plat, anda membuat permintaan kepadanya, ia melepasi indeks, jatuh ke dalam Seq Scan... anda telah menapis semua rekod kecuali satu. Mengapa anda memerlukan 100M rekod yang ditapis setiap hari? Bukankah lebih baik untuk menggulung indeks?

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Setelah menganalisis semua pelan nod demi nod, kami menyedari bahawa terdapat beberapa struktur tipikal dalam pelan yang berkemungkinan besar kelihatan mencurigakan. Dan adalah baik untuk memberitahu pemaju: "Rakan, di sini anda mula-mula membaca mengikut indeks, kemudian menyusun, dan kemudian memotong" - sebagai peraturan, terdapat satu rekod.

Setiap orang yang menulis pertanyaan mungkin pernah mengalami corak ini: “Beri saya pesanan terakhir untuk Vasya, tarikhnya.” Dan jika anda tidak mempunyai indeks mengikut tarikh, atau tiada tarikh dalam indeks yang anda gunakan, maka anda akan pijak "garu" yang sama persis.

Tetapi kita tahu bahawa ini adalah "rake" - jadi mengapa tidak memberitahu pemaju dengan segera apa yang perlu dilakukannya. Sehubungan itu, apabila membuka pelan sekarang, pembangun kami serta-merta melihat gambar yang cantik dengan petua, di mana mereka segera memberitahunya: "Anda mempunyai masalah di sana sini, tetapi ia diselesaikan dengan cara ini dan itu."

Akibatnya, jumlah pengalaman yang diperlukan untuk menyelesaikan masalah pada mulanya dan kini telah menurun dengan ketara. Ini adalah jenis alat yang kita ada.

Pengoptimuman pukal pertanyaan PostgreSQL. Kirill Borovikov (Tensor)

Sumber: www.habr.com

Tambah komen