MySQL “DANS” le rendement de l'opérateur sur des (grands?) nombre de valeurs
J'ai fait des expériences avec MongoDB et Redis ces derniers temps, et il semblerait qu'il y a souvent des cas où l'on serait de stocker un tableau de id dans MongoDB ou Redis. Je vais rester avec le Redis, pour cette question, depuis que j'ai fais une demande sur la base de DANS opérateur.
Je me demandais comment performant, il est de la liste d'un grand nombre (300-3000) de id à l'intérieur de l'opérateur, qui ressemblerait à quelque chose comme ceci:
SELECT id, name, price
FROM products
WHERE id IN (1, 2, 3, 4, ...... 3000)
Imaginer quelque chose d'aussi simple que d'une produits et catégories tableau que vous pourrait normalement s'unissent afin d'obtenir le produits à partir d'un certain catégorie. Dans l'exemple ci-dessus, vous pouvez voir qu'en vertu d'une catégorie donnée dans le Redis ( category:4:product_ids
) je retourne tous les id de produit à partir de la catégorie avec l'id 4, et placez-la au-dessus de SELECT
requête à l'intérieur de la IN
opérateur.
Comment performant est-ce?
Est-ce un "ça dépend" de la situation? Ou est-il un béton "c'est (in)acceptable" ou "rapide" ou "lent" ou devrais-je ajouter un LIMIT 25
, ou n'est-ce pas de l'aide?
SELECT id, name, price
FROM products
WHERE id IN (1, 2, 3, 4, ...... 3000)
LIMIT 25
Ou dois-je couper le tableau des id de produit renvoyé par le Redis à limiter à 25 ans et seulement ajouter 25 id de la requête plutôt que de 3000 et LIMIT
-ing à 25 à partir de l'intérieur de la requête?
SELECT id, name, price
FROM products
WHERE id IN (1, 2, 3, 4, ...... 25)
Des suggestions/commentaires est très apprécié!
- Je ne suis pas sûr exactement ce que vous demandez? Une requête avec "id IN(1,2,3, ...3000))" est plus rapide que 3000 requêtes avec "id = valeur". Mais une jointure avec la catégorie "= 4" sera plus rapide que les deux ci-dessus.
- Droit, mais, depuis un produit peut appartenir à plusieurs catégories I vous ne pouvez pas faire de la "catégorie = 4". Utilisation de Redis je voudrais stocker toutes les id des produits qui appartiennent à certaines catégories et ensuite interroger sur qui. Je suppose que la vraie question est de savoir comment le
id IN (1,2,3 ... 3000)
par rapport à la table de JOINTURE deproducts_categories
. Ou est-ce que vous avez dit? - Il suffit de faire attention à partir de ce bogue dans MySql stackoverflow.com/questions/3417074/...
- Bien sûr, il n'ya aucune raison pourquoi il ne devrait pas être aussi efficace que toute autre méthode de récupération de indexés lignes; tout dépend si la base de données auteurs ont testé et optimisé pour cela. En termes de complexité de calcul que nous allons faire, au pire, une O(n log N) tri sur le
IN
de l'alinéa (cela peut même être linéaire sur une liste triée comme vous le montrez, en fonction de l'algorithme), puis linéaire intersection/recherches.
Vous devez vous connecter pour publier un commentaire.
Règle générale, si l'
IN
liste devient trop grand (pour certains mal définie, la valeur de "trop grand", qui est généralement de l'ordre de 100 ou plus petit), il est plus efficace d'utiliser une jointure, la création d'une table temporaire en cas de besoin donc de tenir les nombres.Si les chiffres sont un ensemble dense (pas de lacunes dans laquelle l'échantillon de données suggère), alors vous pouvez faire encore mieux avec
WHERE id BETWEEN 300 AND 3000
.Toutefois, sans doute il y a des lacunes dans l'ensemble, à quel point il peut être préférable d'aller avec la liste des valeurs valides après tout (à moins que les écarts sont relativement peu nombreux, dans ce cas, vous pouvez utiliser:
Ou ce que sont les lacunes.
AND id NOT BETWEEN XXX AND XXX
ne fonctionnera pas et il est préférable de coller avec l'équivalent(x = 1 OR x = 2 OR x = 3 ... OR x = 99)
comme @David Abat écrit.J'ai fait quelques tests, et comme David Abat dit dans sa réponse, il est assez bien optimisé. Comme une référence, j'ai créé une table InnoDB avec 1 000 000 de registres et de faire un select avec l'opérateur "IN" avec 500 000 nombres aléatoires, il faut seulement 2,5 secondes sur mon MAC; en sélectionnant uniquement les registres prend 0,5 secondes.
Le seul problème que j'ai eu, c'est que j'ai dû augmenter la
max_allowed_packet
paramètre à partir de lamy.cnf
fichier. Si non, un mystérieux “MYSQL a disparu” d'erreur est généré.Voici le code PHP que j'utilise pour faire le test:
Et les résultats:
%
) avec un opérateur = (=
) au lieu deIN()
.Vous pouvez créer une table temporaire où vous pouvez mettre n'importe quel nombre d'Identifiants et d'exécuter des requêtes imbriquées
Exemple:
et sélectionnez:
IN
est très bien, et bien optimisé. Assurez-vous que vous l'utilisez sur un champ indexé et que vous êtes bien.Il est fonctionnellement équivalent à:
Autant que le moteur de base est concerné.
À l'aide de
IN
avec un grand jeu de paramètres sur une grande liste d'enregistrements seront en fait ralentir.Dans le cas que j'ai résolu récemment, j'ai eu deux clauses where, l'un avec 2,50 paramètres et l'autre avec 3 500 les paramètres, de l'interrogation d'une table de 40 Millions de disques.
Ma requête a pris 5 minutes à l'aide de la norme
WHERE IN
. En utilisant une sous-requête pour l' DANS déclaration (mettre les paramètres dans leurs propres indexés table), j'ai reçu la requête de DEUX secondes.Travaillé pour MySQL et Oracle dans mon expérience.
Lorsque vous fournir de nombreuses valeurs pour la
IN
opérateur, il faut d'abord trier supprimer les doublons. Au moins je soupçonne que. Donc, il serait pas bon de donner trop grand nombre de valeurs, comme le tri prend N log N fois.Mon expérience a prouvé que le découpage de l'ensemble des valeurs en sous-ensembles plus petits et en combinant les résultats de toutes les requêtes de l'application vous donne les meilleures performances. J'avoue que j'ai acquis de l'expérience sur une autre base de données (Omniprésente), mais la même chose peut s'appliquer à tous les moteurs. Mon nombre de valeurs par l'ensemble ont été 500-1000. Plus ou moins a été significativement plus lent.