Le problème ici était celui que j'ai décrit dans la mise à jour 2 de ma question. MySQL utilise des index pour effectuer rapidement des opérations ORDER BY. Plus précisément, MySQL utilise B-trees pour indexer les colonnes (telles que les horodatages - p.time/r.time), qui utilisent un peu plus d'espace mais permettent un tri plus rapide.
Le problème avec ma requête était qu'elle était triée par la colonne de temps dans deux tables, en utilisant l'horodatage de la table de repost si disponible, et la table de publication sinon. Étant donné que MySQL ne peut pas combiner les arbres B des deux tables, il ne peut pas effectuer de tri d'index rapide sur les colonnes de deux tables différentes.
J'ai modifié ma structure de requête et de table de deux manières pour résoudre ce problème.
1) Effectuez d'abord un filtrage basé sur les utilisateurs bloqués, de sorte que la commande ne doit être effectuée que sur les publications accessibles par l'utilisateur actuel. Ce n'était pas la racine du problème, mais une optimisation pratique. ex.
SELECT * FROM (SELECT * FROM Post p WHERE p.author_id NOT IN (4, 5, 6...))...
2) Traitez chaque message comme un repost par son auteur, de sorte que chaque message est garanti d'avoir un repost joignable et repost.time sur lequel indexer et trier. ex.
SELECT * FROM (...) LEFT JOIN p.reposts repost ON (p.id = repost.post_id AND
repost.time = (
SELECT MIN(r.time) FROM Repost r WHERE p.id = r.post_id
AND r.user_id IN (1, 2, 3...) AND r.user_id NOT IN (4, 5, 6...))
))
WHERE (repost.id IS NOT NULL) ORDER BY repost.time DESC LIMIT 0, 10
En fin de compte, le problème se résumait à ORDER BY :cette approche a réduit le temps de requête d'environ 8 secondes à 20 ms.