Ce post prend beaucoup de temps à taper parce que j'essaie d'être le plus clair possible, alors s'il vous plaît, soyez patient si c'est encore flou.
En gros, j'ai un tableau de posts dans la base de données auxquels les utilisateurs peuvent ajouter des paramètres de confidentialité.
ID | owner_id | post | other_info | niveau_confidentialité (valeur int)
À partir de là, les utilisateurs peuvent ajouter leurs détails de confidentialité, permettant ainsi de les rendre visibles à tous [niveau_confidentialité = 0), aux amis (niveau_confidentialité = 1), à personne (niveau_confidentialité = 3), ou à des personnes spécifiques ou des filtres (niveau_confidentialité = 4). Pour les niveaux de confidentialité spécifiant des personnes spécifiques (4), la requête fera référence au tableau "post_privacy_includes_for" dans une sous-requête pour voir si l'utilisateur (ou un filtre auquel l'utilisateur appartient) existe dans une ligne du tableau.
ID | post_id | user_id | list_id
De plus, l'utilisateur a la possibilité d'empêcher certaines personnes de voir leur post au sein d'un groupe plus large en les excluant (par exemple, le configurer pour que tout le monde puisse voir sauf un utilisateur harceleur). Pour cela, une autre table de référence est ajoutée, "post_privacy_exclude_from" - elle ressemble en tout point à la configuration de "post_privacy_includes_for".
Mon problème est que cela ne met pas à l'échelle. Pas du tout. À l'heure actuelle, il y a environ 1 à 2 millions de posts, la majorité d'entre eux étant configurés pour être visibles par tout le monde. Pour chaque post sur la page, il doit vérifier s'il existe une ligne excluant le post d'être affiché à l'utilisateur - cela se déplace vraiment lentement sur une page qui peut être remplie de 100 à 200 posts. Cela peut prendre de 2 à 4 secondes, en particulier lorsque des contraintes supplémentaires sont ajoutées à la requête.
Cela crée également des requêtes extrêmement grandes et complexes qui sont simplement... maladroites.
SÉLECTIONNEZ t.*
DE posts t
OÙ ( (t.niveau_confidentialité = 3
ET t.owner_id = ?)
OU (t.niveau_confidentialité = 4
ET EXISTS
( SELECT i.id
DE PostPrivacyIncludeFor i
OÙ i.user_id = ?
ET i.thought_id = t.id)
OU t.niveau_confidentialité = 4
ET t.owner_id = ?)
OU (t.niveau_confidentialité = 4
ET EXISTS
(SELECT i2.id
DE PostPrivacyIncludeFor i2
OÙ i2.thought_id = t.id
ET EXISTS
(SELECT r.id
DE FriendFilterIds r
OÙ r.list_id = i2.list_id
ET r.friend_id = ?))
OU t.niveau_confidentialité = 4
ET t.owner_id = ?)
OU (t.niveau_confidentialité = 1
ET EXISTS
(SELECT G.id
DE Following G
OÙ follower_id = t.owner_id
ET following_id = ?
ET friend = 1)
OU t.niveau_confidentialité = 1
ET t.owner_id = ?)
OU (NON EXISTS
(SELECT e.id
DE PostPrivacyExcludeFrom e
OÙ e.thought_id = t.id
ET e.user_id = ?
ET NON EXISTS
(SELECT e2.id
DE PostPrivacyExcludeFrom e2
OÙ e2.thought_id = t.id
ET EXISTS
(SELECT l.id
DE FriendFilterIds l
OÙ l.list_id = e2.list_id
ET l.friend_id = ?)))
ET t.niveau_confidentialité DANS (0, 1, 4))
ET t.owner_id = ?
ORDRE PAR t.created_at LIMITE 100
(requête de simulation, similaire à la requête que j'utilise actuellement dans Doctrine ORM. C'est un gâchis, mais vous comprenez ce que je veux dire.)
Je suppose que ma question est la suivante : comment aborderiez-vous cette situation pour l'optimiser ? Y a-t-il une meilleure façon de configurer ma base de données ? Je suis prêt à abandonner complètement la méthode que j'ai actuellement mise en place, mais je ne saurais pas vers quoi me tourner.
Merci les gars.
Mise à jour : Corriger la requête pour refléter les valeurs que j'ai définies pour le niveau de confidentialité ci-dessus (j'ai oublié de la mettre à jour car j'ai simplifié les valeurs)