簡潔な答え
3番目のオプション: Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
$teamMorphType = Relation::getMorphedModel('team');
$groupMorphType = Relation::getMorphedModel('group');
$formMorphType = Relation::getMorphedModel('form');
$permissible = [
$teamMorphType => [$user->team_id],
$groupMorphType => [],
$formMorphType => [],
];
foreach ($user->permissible as $permissible) {
switch ($permissible->permissible_type) {
case $teamMorphType:
case $groupMorphType:
case $formMorphType:
$permissible[$permissible->permissible_type][] = $permissible->permissible_id;
break;
}
}
$forms = Form::query()
->where('user_id', '=', $user->id)
->orWhereIn('id', $permissible[$fromMorphType])
->orWhereIn('team_id', $permissible[$teamMorphType])
->orWhereIn('group_id', $permissible[$groupMorphType])
->get();
長い答え
一方では、(ほとんど)コードで実行できることはすべて、クエリで実行するよりもパフォーマンスの点で優れています。
一方、データベースから必要以上のデータを取得すると、すでにデータが多すぎます(RAM使用率など)。
私の観点からは、あなたはその間に何かが必要であり、あなただけが、数に応じて、どこがバランスになるかを知っています。
いくつかのクエリを実行することをお勧めします。最後に提案したオプション(Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
):
- すべての権限について、すべての識別子をクエリします(5クエリ)
- すべてのフォーム結果をメモリにマージし、一意の値を取得します
array_unique($ids)
- IN()ステートメントで識別子を使用して、フォームモデルをクエリします。
いくつかのツールを使用してクエリを複数回実行して、提案した3つのオプションを試してパフォーマンスを監視できますが、最後のオプションが最高のパフォーマンスを発揮すると確信しています。
これは、使用しているデータベースに応じて大幅に変わる可能性がありますが、たとえば、MySQLについて話している場合は、非常に大きなクエリでは、より多くのデータベースリソースを使用するため、単純なクエリよりも時間がかかるだけでなく、テーブルが書き込みからロックされ、これによりデッドロックエラーが発生する可能性があります(スレーブサーバーを使用しない場合)。
一方、フォームIDの数が非常に多い場合は、プレースホルダーが多すぎるとエラーが発生する可能性があるため、たとえば500個のIDのグループでクエリをチャンクにしたい場合があります(これは制限として多くの場合に依存します)サイズであり、バインディングの数ではありません)、結果をメモリにマージします。データベースエラーが発生しなくても、パフォーマンスにも大きな違いが見られる場合があります(私はまだMySQLについて話しています)。
実装
これがデータベーススキームであると想定します。
users
- id
- team_id
forms
- id
- user_id
- team_id
- group_id
permissible
- user_id
- permissible_id
- permissible_type
したがって、許容されるのは、すでに構成された多態的な関係です。
したがって、関係は次のようになります。
- 所有フォーム:
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'
バージョンを簡略化:
$teamMorphType = Relation::getMorphedModel('team');
$groupMorphType = Relation::getMorphedModel('group');
$formMorphType = Relation::getMorphedModel('form');
$permissible = [
$teamMorphType => [$user->team_id],
$groupMorphType => [],
$formMorphType => [],
];
foreach ($user->permissible as $permissible) {
switch ($permissible->permissible_type) {
case $teamMorphType:
case $groupMorphType:
case $formMorphType:
$permissible[$permissible->permissible_type][] = $permissible->permissible_id;
break;
}
}
$forms = Form::query()
->where('user_id', '=', $user->id)
->orWhereIn('id', $permissible[$fromMorphType])
->orWhereIn('team_id', $permissible[$teamMorphType])
->orWhereIn('group_id', $permissible[$groupMorphType])
->get();
詳細バージョン:
// Owns Form
// users.id <-> forms.user_id
$userId = $user->id;
// Team owns Form
// users.team_id <-> forms.team_id
// Initialise the array with a first value.
// The permissions polymorphic relationship will have other teams ids to look at
$teamIds = [$user->team_id];
// Groups owns Form was not mention, so I assume there is not such a relation in user.
// Just initialise the array without a first value.
$groupIds = [];
// Also initialise forms for permissions:
$formIds = [];
// Has permissions to a group that owns a Form
// permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
$teamMorphType = Relation::getMorphedModel('team');
// Has permissions to a team that owns a Form
// permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
$groupMorphType = Relation::getMorphedModel('group');
// Has permission to a Form
// permissible.user_id <-> users.id && permissible.permissible_type = 'App\Form'
$formMorphType = Relation::getMorphedModel('form');
// Get permissions
$permissibles = $user->permissible()->whereIn(
'permissible_type',
[$teamMorphType, $groupMorphType, $formMorphType]
)->get();
// If you don't have more permissible types other than those, then you can just:
// $permissibles = $user->permissible;
// Group the ids per type
foreach ($permissibles as $permissible) {
switch ($permissible->permissible_type) {
case $teamMorphType:
$teamIds[] = $permissible->permissible_id;
break;
case $groupMorphType:
$groupIds[] = $permissible->permissible_id;
break;
case $formMorphType:
$formIds[] = $permissible->permissible_id;
break;
}
}
// In case the user and the team ids are repeated:
$teamIds = array_values(array_unique($teamIds));
// We assume that the rest of the values will not be repeated.
$forms = Form::query()
->where('user_id', '=', $userId)
->orWhereIn('id', $formIds)
->orWhereIn('team_id', $teamIds)
->orWhereIn('group_id', $groupIds)
->get();
使用されるリソース:
データベースのパフォーマンス:
- データベースへのクエリ(ユーザーを除く):2 ; 1つは許可を取得するためのもので、もう1つはフォームを取得するためのものです。
- 参加しません!!
- 可能な最小ORs(
user_id = ? OR id IN (?..) OR team_id IN (?...) OR group_id IN (?...)
。
PHP、メモリ内、パフォーマンス:
- 内部のスイッチでforeachループが許容されます。
array_values(array_unique())
IDの繰り返しを避けるため。
- IDSのメモリにおいて、3つのアレイ(
$teamIds
、$groupIds
、$formIds
)
- メモリ内で、関連するアクセス許可の雄弁なコレクション(これは、必要に応じて最適化できます)。
長所と短所
長所:
- 時間:単一のクエリの合計時間は、結合とORを使用した大きなクエリの時間よりも短くなります。
- DBリソース:結合やステートメントを含むクエリで使用されるMySQLリソースは、個別のクエリの合計で使用されるものよりも大きくなります。
- お金:データベースリソース(プロセッサ、RAM、ディスク読み取りなど)が少なく、PHPリソースよりも高価です。
- ロック:読み取り専用のスレーブサーバーにクエリを実行しない場合、クエリは読み取りロックの行数を減らします(読み取りロックはMySQLで共有されるため、別の読み取りはロックされませんが、書き込みはブロックされます)。
- スケーラブル:このアプローチにより、クエリのチャンクなど、より多くのパフォーマンス最適化を行うことができます。
短所:
- コードリソース:データベースではなくコードで計算を行うと、コードインスタンス、特にRAMで中間の情報を保存するためにより多くのリソースが消費されます。私たちの場合、これは単なるidの配列ですが、これは実際には問題になりません。
- メンテナンス:Laravelのプロパティとメソッドを使用し、データベースに変更を加える場合、より明示的なクエリと処理を行うよりもコードで更新する方が簡単です。
- 殺し過ぎ?:場合によっては、データがそれほど大きくない場合、パフォーマンスの最適化が過剰になる可能性があります。
パフォーマンスを測定する方法
パフォーマンスを測定する方法についての手がかりはありますか?
- クエリログが遅い
- 分析テーブル
- 表の状態を表示
- 説明する ; 拡張EXPLAIN出力フォーマット ; 説明を使用して ; 出力を説明する
- 警告を表示
いくつかの興味深いプロファイリングツール: