Halo! Saya Valerio, seorang pengembang dan CTO dari Italia untuk platform ini
Inspector.dev
.
Pada artikel ini, saya akan membagikan sekumpulan strategi pengoptimalan ORM yang saya gunakan saat mengembangkan layanan backend.
Saya yakin masing-masing dari kita pernah mengeluh bahwa server atau aplikasi berjalan lambat (atau bahkan tidak berfungsi sama sekali), dan sambil menyia-nyiakan waktu di mesin kopi, menunggu hasil permintaan yang lama.
Bagaimana memperbaikinya?
Ayo cari tahu!
Basis data adalah sumber daya bersama
Mengapa database menyebabkan begitu banyak masalah kinerja?
Kami sering lupa bahwa tidak ada kueri yang independen dari orang lain.
Kami berpikir bahwa meskipun beberapa kueri lambat, hal itu hampir tidak memengaruhi yang lain ... Tetapi benarkah demikian?
Database adalah resource bersama yang digunakan oleh semua proses yang berjalan di aplikasi Anda. Bahkan satu metode yang dirancang dengan buruk untuk mengakses database dapat mengganggu kinerja seluruh sistem.
Oleh karena itu, jangan lupa tentang kemungkinan konsekuensinya, dengan berpikir: "Tidak apa-apa jika potongan kode ini tidak dioptimalkan!" Satu akses lambat ke database dapat menyebabkan kelebihan beban, dan ini, pada gilirannya, dapat berdampak negatif pada pengalaman pengguna.
N + 1 masalah kueri database
Apa masalah N + 1?
Ini adalah masalah umum saat menggunakan ORM untuk berinteraksi dengan database. Ini bukan tentang menulis kode SQL.
Saat menggunakan sistem ORM seperti Eloquent, tidak selalu jelas kueri mana yang akan dijalankan dan kapan. Dalam konteks masalah khusus ini, mari kita bicara tentang hubungan dan eager loading.
Semua sistem ORM memungkinkan Anda untuk mendeklarasikan hubungan antar entitas dan menyediakan API yang bagus untuk menavigasi struktur database Anda.
Di bawah ini adalah contoh yang baik untuk entitas "Artikel" dan "Penulis".
/*
* Each Article belongs to an Author
*/
$article = Article::find("1");
echo $article->author->name;
/*
* Each Author has many Articles
*/
foreach (Article::all() as $article)
{
echo $article->title;
}
Namun, saat menggunakan hubungan dalam satu loop, Anda harus menulis kode Anda dengan hati-hati.
Lihat contoh di bawah ini.
Kami ingin menambahkan nama penulis di sebelah judul artikel. Dengan ORM, Anda bisa mendapatkan nama penulis menggunakan hubungan satu-ke-satu antara artikel dan penulis.
Semuanya tampak sederhana:
// Initial query to grab all articles
$articles = Article::all();
foreach ($articles as $article)
{
// Get the author to print the name.
echo $article->title . ' by ' . $article->author->name;
}
Tapi kemudian kami jatuh ke dalam jebakan!
Loop ini menghasilkan satu permintaan awal untuk mendapatkan semua artikel:
SELECT * FROM articles;
dan N kueri lagi untuk mendapatkan penulis setiap artikel dan menampilkan nilai bidang "nama", meskipun penulis selalu sama.
SELECT * FROM author WHERE id = [articles.author_id]
Kami menerima persis N + 1 permintaan.
Ini mungkin tidak terlihat seperti masalah besar. Baiklah, mari kita buat lima belas atau dua puluh permintaan tambahan - bukan masalah besar. Namun, mari kembali ke bagian pertama artikel ini:
- โ , .
- , , .
- , .
:
Menurut dokumen Laravel, ada kemungkinan besar Anda akan mengalami masalah kueri N + 1, karena saat Anda mengakses relasi Eloquent sebagai properti (
$article->author
), data relasi dimuat lambat.
Ini berarti bahwa data relasi tidak dimuat hingga Anda pertama kali mengakses properti.
Namun, dengan menggunakan metode sederhana, kita dapat memuat semua data hubungan sekaligus. Kemudian, saat mengakses relasi Eloquent sebagai properti, sistem ORM tidak akan mengeksekusi query baru karena datanya sudah dimuat.
Taktik ini disebut "eager loading" dan didukung oleh semua ORM.
// Eager load authors using "with".
$articles = Article::with('author')->get();
foreach ($articles as $article)
{
// Author will not run a query on each iteration.
echo $article->author->name;
}
Eloquent menyediakan metode
with()
untuk memuat hubungan dengan penuh semangat.
Dalam kasus ini, hanya dua query yang akan dieksekusi.
Yang pertama diperlukan untuk mengunduh semua artikel:
SELECT * FROM articles;
Yang kedua akan dijalankan oleh metode
with()
dan akan mengambil semua penulis:
SELECT * FROM authors WHERE id IN (1, 2, 3, 4, ...);
Mesin internal Eloquent akan memetakan data dan dapat diakses dengan cara biasa:
$article->author->name;
Optimalkan operator Anda select
Untuk waktu yang lama, saya berpikir bahwa secara eksplisit menyatakan jumlah bidang dalam kueri pengambilan tidak akan menghasilkan peningkatan kinerja yang signifikan, jadi untuk kesederhanaan saya mendapatkan semua bidang dalam kueri saya.
Selain itu, pengodean keras daftar bidang dalam pernyataan pilih tertentu membuat lebih sulit untuk mempertahankan potongan kode tersebut lebih lanjut.
Jebakan terbesar dari argumen ini adalah bahwa dari perspektif database, ini mungkin benar.
Namun, kami bekerja dengan ORM, jadi data yang dipilih dari database akan dimuat ke dalam memori di sisi PHP sehingga sistem ORM akan mengelolanya lebih lanjut. Semakin banyak bidang yang kita tangkap, semakin banyak memori yang dibutuhkan untuk proses tersebut.
Laravel Eloquent menyediakan metode pemilihan untuk membatasi kueri hanya ke kolom yang kita butuhkan:
$articles = Article::query()
->select('id', 'title', 'content') // The fields you need
->latest()
->get();
Dengan mengecualikan kolom, interpreter PHP tidak harus memproses data yang tidak perlu, sehingga Anda dapat mengurangi konsumsi memori secara signifikan.
Menghindari pengambilan penuh juga dapat meningkatkan kinerja pengurutan, pengelompokan, dan penggabungan, karena database itu sendiri dapat menghemat memori.
Gunakan tampilan di MySQL
Tampilan adalah kueri SELECT berdasarkan tabel lain dan disimpan dalam database.
Saat kita MEMILIH satu atau beberapa tabel, database mengompilasi pernyataan SQL kita terlebih dahulu, memastikannya bebas dari kesalahan, lalu mengambil datanya.
View adalah pernyataan SELECT yang telah dikompilasi sebelumnya, yang ketika diproses, MySQL segera mengeksekusi query internal yang mendasari view.
Selain itu, MySQL biasanya lebih pintar daripada PHP dalam hal memfilter data. Ada keuntungan kinerja yang signifikan saat menggunakan tampilan atas penggunaan fungsi PHP untuk memproses koleksi atau array.
Jika Anda ingin mempelajari lebih lanjut tentang kemampuan MySQL untuk mengembangkan aplikasi intensif basis data, lihat situs hebat ini: www.mysqltutorial.org
Tautkan Model Fasih ke Tampilan
Tampilan juga disebut sebagai "tabel virtual". Dari sudut pandang ORM, mereka terlihat seperti tabel biasa.
Oleh karena itu, Anda dapat membuat model Eloquent untuk membuat kueri data yang ada dalam tampilan.
class ArticleStats extends Model
{
/**
* The name of the view is the table name.
*/
protected $table = "article_stats_view";
/**
* If the resultset of the View include the "author_id"
* we can use it to retrieve the author as normal relation.
*/
public function author()
{
return $this->belongsTo(Author::class);
}
}
Hubungan berfungsi seperti biasa, seperti halnya paksaan, pagination, dan sebagainya. Dan tidak ada penalti performa.
Kesimpulan
Saya harap tips ini akan membantu Anda mengembangkan perangkat lunak yang lebih andal dan skalabel.
Semua contoh kode ditulis menggunakan Eloquent sebagai ORM, namun perlu diingat bahwa strategi ini bekerja sama untuk semua ORM utama.
Seperti yang sering saya katakan, kita membutuhkan alat untuk mengimplementasikan strategi yang efektif. Dan jika tidak ada strategi, maka tidak ada yang perlu dibicarakan.
Terima kasih banyak telah membaca artikel sampai akhir. Jika Anda ingin mempelajari lebih lanjut tentang Inspektur, saya mengundang Anda ke situs web kami www.inspector.dev . Jangan ragu untuk menulis ke obrolan jika Anda memiliki pertanyaan!
Sebelumnya diposting di sini: www.inspector.dev/make-your-application-scalable-optimizing-the-orm-performance
Baca lebih banyak: