Saya sedang mengerjakan aplikasi di mana pengguna dapat memiliki akses ke berbagai bentuk melalui banyak skenario yang berbeda. Saya mencoba membangun pendekatan dengan kinerja terbaik saat mengembalikan indeks formulir kepada pengguna.
Seorang pengguna dapat memiliki akses ke formulir melalui skenario berikut:
- Formulir Milik
- Tim memiliki Formulir
- Memiliki izin ke grup yang memiliki Formulir
- Memiliki izin ke tim yang memiliki Formulir
- Memiliki izin untuk Formulir
Seperti yang Anda lihat ada 5 cara yang memungkinkan pengguna dapat mengakses formulir. Masalah saya adalah bagaimana cara paling efisien mengembalikan array formulir yang dapat diakses kepada pengguna.
Formulir Kebijakan:
Saya telah mencoba untuk mendapatkan semua Formulir dari model dan kemudian menyaring formulir dengan kebijakan formulir. Ini tampaknya menjadi masalah kinerja karena pada setiap iterasi filter formulir diteruskan melalui metode () fasih 5 kali seperti yang ditunjukkan di bawah ini. Semakin banyak formulir dalam database berarti ini menjadi lebih lambat.
FormController@index
public function index(Request $request)
{
$forms = Form::all()
->filter(function($form) use ($request) {
return $request->user()->can('view',$form);
});
}
FormPolicy@view
public function view(User $user, Form $form)
{
return $user->forms->contains($form) ||
$user->team->forms->contains($form) ||
$user->permissible->groups->forms($contains);
}
Meskipun metode di atas berhasil, itu adalah kinerja botol leher.
Dari apa yang saya dapat melihat opsi saya berikut ini adalah:
- Filter FormPolicy (pendekatan saat ini)
- Permintaan semua izin (5) dan gabungkan menjadi satu koleksi
- Query semua pengidentifikasi untuk semua izin (5), kemudian permintaan model Form menggunakan pengidentifikasi dalam IN () pernyataan
Pertanyaan saya:
Metode mana yang akan memberikan kinerja terbaik dan apakah ada opsi lain yang akan memberikan kinerja yang lebih baik?
user_form_permission
tabel yang berisi hanyauser_id
danform_id
. Ini akan membuat izin membaca menjadi lebih mudah, namun memperbarui izin akan lebih sulit.Jawaban:
Saya akan mencari untuk melakukan SQL Query karena itu akan melakukan jauh lebih baik daripada php
Sesuatu seperti ini:
Dari atas kepala saya dan belum teruji, ini akan memberi Anda semua formulir yang dimiliki oleh pengguna, grupnya, dan tim ini.
Namun itu tidak melihat izin formulir tampilan pengguna dalam grup dan tim.
Saya tidak yakin bagaimana Anda mengatur auth Anda untuk ini dan jadi Anda perlu memodifikasi permintaan untuk ini dan perbedaan dalam struktur DB Anda.
sumber
OR
klausa, yang saya curigai akan lambat. Jadi memukul ini pada setiap permintaan akan gila saya percaya.Jawaban singkat
Opsi ketiga:
Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
Jawaban panjang
Di satu sisi, (hampir) semua yang dapat Anda lakukan dalam kode, adalah kinerja yang lebih baik, daripada melakukannya dalam kueri.
Di sisi lain, mendapatkan lebih banyak data dari basis data dari yang diperlukan akan terlalu banyak data (penggunaan RAM dan sebagainya).
Dari sudut pandang saya, Anda membutuhkan sesuatu di antaranya, dan hanya Anda yang akan tahu di mana keseimbangannya, tergantung pada jumlahnya.
Saya sarankan menjalankan beberapa kueri, opsi terakhir yang Anda usulkan (
Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
):array_unique($ids)
Anda dapat mencoba tiga opsi yang Anda usulkan dan memantau kinerja, menggunakan beberapa alat untuk menjalankan kueri beberapa kali, tetapi saya 99% yakin bahwa yang terakhir akan memberi Anda kinerja terbaik.
Ini juga dapat banyak berubah, tergantung pada basis data yang Anda gunakan, tetapi jika kita berbicara tentang MySQL, misalnya; Dalam Permintaan yang sangat besar akan menggunakan lebih banyak sumber daya database, yang tidak hanya akan menghabiskan lebih banyak waktu daripada permintaan sederhana, tetapi juga akan mengunci tabel dari menulis, dan ini dapat menghasilkan kesalahan kebuntuan (kecuali jika Anda menggunakan server slave).
Di sisi lain, jika jumlah bentuk id sangat besar, Anda dapat memiliki kesalahan untuk placeholder terlalu banyak, jadi Anda mungkin ingin memotong kueri dalam grup, katakanlah, 500 id (ini tergantung banyak, sebagai batas dalam ukuran, bukan dalam jumlah binding), dan menggabungkan hasilnya dalam memori. Bahkan jika Anda tidak mendapatkan kesalahan basis data, Anda mungkin melihat perbedaan besar dalam kinerja juga (saya masih berbicara tentang MySQL).
Penerapan
Saya akan berasumsi bahwa ini adalah skema basis data:
Jadi diizinkan adalah hubungan polimorfik yang sudah terkonfigurasi .
Karena itu, hubungannya adalah:
users.id <-> form.user_id
users.team_id <-> form.team_id
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
permissible.user_id <-> users.id && permissible.permissible_type = 'App\From'
Sederhanakan versi:
Versi detail:
Sumber daya yang digunakan:
Kinerja basis data:
user_id = ? OR id IN (?..) OR team_id IN (?...) OR group_id IN (?...)
.PHP, dalam memori, kinerja:
array_values(array_unique())
untuk menghindari pengulangan id.$teamIds
,$groupIds
,$formIds
)Pro dan kontra
PROS:
CONS:
Bagaimana mengukur kinerja
Beberapa petunjuk tentang cara mengukur kinerja?
Beberapa alat profil yang menarik:
sumber
array_merge()
danarray_unique()
banyak id, akan sangat memperlambat proses Anda.array_unique()
lebih cepat dariGROUP BY
/SELECT DISTINCT
pernyataan.Mengapa Anda tidak bisa langsung menanyakan Formulir yang Anda butuhkan, alih-alih melakukan
Form::all()
dan kemudian merantaifilter()
fungsi setelahnya?Seperti itu:
Jadi ya, ini beberapa pertanyaan:
$user
$user->team
$user->team->forms
$user->permissible
$user->permissible->groups
$user->permissible->groups->forms
Namun, sisi pro adalah bahwa Anda tidak perlu lagi menggunakan kebijakan , karena Anda tahu semua formulir di
$forms
parameter diperbolehkan untuk pengguna.Jadi solusi ini akan bekerja untuk berapa pun jumlah formulir yang Anda miliki di database.
Jika Anda menginginkannya menjadi lebih cepat, Anda harus membuat kueri khusus menggunakan fasad DB, sesuatu di sepanjang baris:
Permintaan Anda yang sebenarnya jauh lebih besar karena Anda memiliki begitu banyak hubungan.
Peningkatan kinerja utama di sini berasal dari fakta bahwa pekerjaan berat (subquery) sepenuhnya memotong logika model Eloquent. Maka yang tersisa untuk dilakukan adalah memasukkan daftar id ke dalam
whereIn
fungsi untuk mengambil daftarForm
objek Anda.sumber
Saya percaya Anda dapat menggunakan Koleksi Malas untuk itu (Laravel 6.x) dan ingin memuat hubungan sebelum mereka diakses.
sumber