Apache Spark, evaluasi malas, dan kueri multi-halaman SQL

Yang terkenal: percikan bekerja dengan kerangka data, yang merupakan algoritma transformasi. Algoritme diluncurkan pada saat terakhir untuk "memberikan lebih banyak ruang" untuk optimasi dan karena optimasi untuk menjalankannya seefisien mungkin.


Di bawah potongan, kami akan menganalisis cara menguraikan kueri multi-halaman SQL menjadi atom (tanpa kehilangan efisiensi) dan cara mengurangi secara signifikan waktu eksekusi pipa ETL karena hal ini.


Evaluasi malas


Fitur fungsional yang menarik dari percikan adalah evaluasi malas: transformasi hanya dilakukan ketika tindakan selesai. Cara kerjanya (kurang-lebih): algoritme untuk membangun bingkai data sebelum tindakan "macet bersama", pengoptimal membangun algoritme final yang paling efisien dari sudut pandangnya, yang dimulai dan memberikan hasilnya (yang diminta oleh tindakan).


Apa yang menarik di sini dalam konteks presentasi kami: kueri kompleks apa pun dapat diuraikan menjadi β€œatom” tanpa kehilangan efisiensi. Mari kita analisis sedikit lebih jauh.


SQL berganda


Ada banyak alasan mengapa kita menulis kueri SQL "multi-halaman", salah satu keengganan utama, mungkin, untuk membuat objek perantara (keengganan didukung oleh persyaratan efisiensi). Berikut ini adalah contoh dari kueri yang relatif kompleks (tentu saja, itu bahkan sangat sederhana, tetapi untuk keperluan presentasi lebih lanjut, kami akan memiliki cukup).


qSel = """ select con.contract_id as con_contract_id, con.begin_date as con_begin_date, con.product_id as con_product_id, cst.contract_status_type_id as cst_status_type_id, sbj.subject_id as sbj_subject_id, sbj.subject_name as sbj_subject_name, pp.birth_date as pp_birth_date from kasko.contract con join kasko.contract_status cst on cst.contract_status_id = con.contract_status_id join kasko.subject sbj on sbj.subject_id = con.owner_subject_id left join kasko.physical_person pp on pp.subject_id = con.owner_subject_id """ dfSel = sp.sql(qSel) 

Apa yang kita lihat:


  • data dipilih dari beberapa tabel
  • berbagai jenis gabung digunakan
  • kolom yang dapat dipilih didistribusikan oleh pilih bagian, gabung bagian (dan di mana bagian, tapi di sini tidak di sini - saya menghapusnya untuk kesederhanaan)

Kueri ini dapat didekomposisi menjadi yang sederhana (misalnya, pertama menggabungkan tabel kontrak dan kontrak_status, menyimpan hasilnya dalam tabel sementara, kemudian menggabungkannya dengan subjek, juga menyimpan hasilnya dalam tabel sementara, dll.). Tentunya, ketika kami membuat kueri yang sangat kompleks, kami melakukan ini, saat itu - setelah debugging - kami kumpulkan semua ini ke dalam blok multi-halaman.


Apa yang buruk di sini? Tidak ada, pada kenyataannya, semua orang bekerja seperti itu dan terbiasa dengannya.


Tetapi ada kekurangan - atau lebih tepatnya, apa yang harus diperbaiki - baca terus.


Permintaan yang sama dalam percikan


Saat menggunakan percikan untuk transformasi, tentu saja, Anda bisa menerima dan mengeksekusi permintaan ini (dan itu akan baik, pada kenyataannya, kami juga akan mengeksekusinya), tetapi Anda bisa ke arah lain, mari kita coba.


Mari kita dekomposisi kueri "kompleks" ini menjadi "atom" - bingkai data dasar. Kami akan mendapatkan sebanyak mungkin dari jumlah tabel yang terlibat dalam kueri (dalam hal ini, 4).


Inilah mereka - "atom":


 dfCon = sp.sql("""select contract_id as con_contract_id, begin_date as con_begin_date, product_id as con_product_id, owner_subject_id as con_owner_subject_id, contract_status_id as con_contract_status_id from kasko.contract""") dfCStat = sp.sql("""select contract_status_id as cst_status_id, contract_status_type_id as cst_status_type_id from kasko.contract_status""") dfSubj = sp.sql("""select subject_id as sbj_subject_id, subject_type_id as sbj_subject_type_id, subject_name as sbj_subject_name from kasko.subject""") dfPPers = sp.sql("""select subject_id as pp_subject_id, birth_date as pp_birth_date from kasko.physical_person""") 

Spark memungkinkan Anda untuk bergabung dengan mereka menggunakan ekspresi yang terpisah dari "atom" yang sebenarnya; mari kita lakukan ini:


 con_stat = f.col("cst_status_id")==f.col("con_contract_status_id") con_subj_own = f.col("con_owner_subject_id")==f.col("sbj_subject_id") con_ppers_own = f.col("con_owner_subject_id")==f.col("pp_subject_id") 

Maka "kueri kompleks" kami akan terlihat seperti ini:


 dfAtom = dfCon.join(dfCStat,con_stat, "inner")\ .join(dfSubj,con_subj_own,"inner") \ .join(dfPPers,con_ppers_own, "left") \ .drop("con_contract_status_id","sbj_subject_type_id", "pp_subject_id","con_owner_subject_id","cst_status_id") 

Apa yang baik di sini? Pada pandangan pertama, itu bukan apa-apa, justru sebaliknya: dengan SQL "kompleks" Anda dapat memahami apa yang terjadi, berdasarkan permintaan "atomik" kami, lebih sulit untuk dipahami, Anda perlu melihat "atom" dan ekspresi.


Pertama-tama mari kita pastikan bahwa kueri ini setara - dalam buku jupyter dengan referensi saya memberikan rencana untuk memenuhi kedua pertanyaan (yang penasaran dapat menemukan 10 perbedaan, tetapi esensi - kesetaraan - jelas). Ini, tentu saja, bukan keajaiban, seharusnya begitu (lihat di atas untuk evaluasi dan optimisasi malas).


Apa yang kita miliki pada akhirnya - permintaan "multi-halaman" dan permintaan "atom" bekerja dengan efisiensi yang sama (ini penting, tanpa pertimbangan lebih lanjut ini sebagian kehilangan artinya).


Nah, sekarang mari kita temukan yang baik dalam cara "atom" dalam membangun pertanyaan.


Apa itu "atom" (bingkai data dasar) adalah pengetahuan kita tentang subset dari area subjek (bagian dari tabel relasional). Dengan mengisolasi "atom" seperti itu, kita secara otomatis (dan, yang terpenting, secara algoritmik dan dapat direproduksi) memilih bagian penting dari hal tanpa batas untuk kita yang disebut "model data fisik".


Apa ekspresi yang kami gunakan saat bergabung? Ini juga pengetahuan tentang area subjek - ini adalah bagaimana (seperti yang ditunjukkan dalam ekspresi) entitas area subjek (tabel dalam database) saling berhubungan.


Saya ulangi - ini penting - "pengetahuan" ini (atom dan ekspresi) diwujudkan dalam kode yang dapat dieksekusi (bukan dalam diagram atau deskripsi verbal), ini adalah kode yang dieksekusi setiap kali pipa ETL dieksekusi (contoh diambil, omong-omong, dari kehidupan nyata).


Kode yang dapat dieksekusi - seperti yang kita ketahui dari clean coder - adalah salah satu dari dua artifak yang ada secara obyektif yang mengklaim sebagai "judul" dari dokumentasi. Artinya, penggunaan "atom" memungkinkan kita untuk mengambil langkah maju dalam proses penting seperti mendokumentasikan data.


Apa lagi yang bisa ditemukan dalam "atomicity"?


Optimalisasi konveyor


Dalam kehidupan nyata, seorang insinyur data - omong-omong, saya tidak memperkenalkan diri - sebuah pipa ETL terdiri dari puluhan transformasi yang mirip dengan yang di atas. Tabel sangat sering diulang di dalamnya (saya entah bagaimana dihitung dalam Excel - beberapa tabel digunakan dalam 40% kueri).


Apa yang terjadi dalam hal efisiensi? Kekacauan - tabel yang sama dibaca beberapa kali dari sumber ...


Bagaimana cara memperbaikinya? Spark memiliki mekanisme untuk caching frame data - kita dapat secara eksplisit menentukan frame data mana dan berapa banyak yang ingin kita simpan dalam cache.


Apa yang harus kita lakukan untuk ini adalah memilih tabel duplikat dan membangun kueri sedemikian rupa untuk meminimalkan ukuran cache total (karena semua tabel, menurut definisi, tidak akan cocok dengan itu, maka ada data besar).


Bisakah ini dilakukan menggunakan kueri SSQ multi-halaman? Ya, tapi ... sedikit rumit (kami tidak benar-benar memiliki bingkai data di sana, hanya tabel, mereka juga dapat di-cache - komunitas percikan sedang mengerjakan ini).


Bisakah ini dilakukan dengan menggunakan permintaan atom? Ya! Dan itu tidak sulit, kita hanya perlu menggeneralisasi "atom-atom" - tambahkan kolom yang digunakan dalam semua permintaan pipa kita ke mereka. Jika Anda memikirkannya, ini "benar" dari sudut pandang dokumentasi: jika kolom digunakan dalam beberapa kueri (bahkan jika di bagian mana), itu adalah bagian dari data area subjek yang menarik bagi kami.


Dan kemudian semuanya sederhana - kita menyimpan atom yang berulang (frame data), kita membangun rantai transformasi sehingga perpotongan frame data yang di-cache minimal (ini bukan sepele, tetapi algoritmik, omong-omong).


Dan kami mendapatkan konveyor yang paling efisien sepenuhnya "gratis". Dan selain itu, artefak yang berguna dan penting adalah "persiapan" untuk dokumentasi data pada area subjek.


Robotisasi dan Otomasi


Atom lebih rentan terhadap pemrosesan otomatis daripada "SQL yang hebat dan hebat" - strukturnya sederhana dan jelas, percikan mem-parsing kita (untuk itu terima kasih khusus untuknya), ia juga membangun rencana kueri, menganalisis yang dapat Anda susun ulang secara otomatis urutan pemrosesan kueri.


Jadi di sini Anda bisa memainkan sesuatu.


Kesimpulannya


Mungkin saya terlalu optimis - menurut saya jalur ini (atomisasi kueri) lebih berfungsi daripada mencoba menggambarkan sumber data setelah fakta. Selain itu - omong-omong, apa penggunaan "aditif" - kami mendapatkan peningkatan efisiensi. Mengapa saya menganggap pendekatan atom "bekerja"? Ini adalah bagian dari proses reguler, yang berarti artefak yang dijelaskan memiliki peluang nyata untuk menjadi relevan dalam jangka panjang.


Saya mungkin melewatkan sesuatu - bantuan menemukan (di komentar)?

Source: https://habr.com/ru/post/id481924/


All Articles