Optimisation des requêtes pour l'élément suivant et précédent

voix
28

Je cherche la meilleure façon de récupérer les enregistrements suivants et précédents d'un enregistrement sans exécuter une requête complète. J'ai une solution pleinement mis en œuvre en place, et je voudrais savoir s'il y a des meilleures approches pour le faire là-bas.

Disons que nous sommes en train de construire un site web pour une fruiterie fictive. En plus de ses pages HTML, chaque semaine, il veut publier une liste des offres spéciales sur son site. Il veut que ces offres de résider dans une table de base de données réelle, et les utilisateurs doivent pouvoir trier les offres de trois façons.

Chaque article doit également avoir une page de détail plus, des informations textuelles sur l'offre et les boutons « précédent » et « suivant ». Le « précédent » et les boutons « suivant » doivent pointer vers les entrées voisines en fonction du tri de l'utilisateur avait choisi pour la liste .

alt texte http://www.pekkagaiser.com/stuff/Sort.gif?

De toute évidence, le bouton « Suivant » pour « Tomates, classe I » doit être « pommes, classe 1 » dans le premier exemple, « poires, classe I » dans la seconde, et aucun dans le troisième.

La tâche dans la vue détaillée est de déterminer les éléments suivants et précédents sans exécuter une requête à chaque fois , avec l'ordre de tri de la liste que la seule information disponible (Disons que nous obtenons que grâce à un paramètre GET ?sort=offeroftheweek_price, et ignorer les implications de sécurité) .

De toute évidence, en passant simplement les ID des éléments suivants et précédents en tant que paramètre est la première solution qui vient à l'esprit. Après tout, nous savons déjà les ID à ce point. Mais, ce n'est pas une option ici - il travaillerait dans cet exemple simplifié, mais pas dans beaucoup de mon vrai cas d'utilisation du monde.

Mon approche actuelle dans mon CMS utilise quelque chose que j'ai appelé « cache de tri ». Lorsqu'une liste est chargée, je stocke les positions de poste dans les enregistrements dans une table nommée sortingcache.

name (VARCHAR)             items (TEXT)

offeroftheweek_unsorted    Lettuce; Tomatoes; Apples I; Apples II; Pears
offeroftheweek_price       Tomatoes;Pears;Apples I; Apples II; Lettuce
offeroftheweek_class_asc   Apples II;Lettuce;Apples;Pears;Tomatoes

De toute évidence, la itemscolonne est vraiment peuplé avec les ID numériques.

Dans la page de détail, j'accéder maintenant approprié sortingcacheenregistrement, chercher la itemscolonne, exploser, rechercher l'ID de l' article, et le retour du voisin précédent et suivant.

array(current   => Tomatoes,
      next      => Pears,
      previous  => null
      );

Ceci est évidemment cher, travaille pour un nombre limité d'enregistrements uniquement et crée des données redondantes, mais supposons que dans le monde réel, la requête pour créer les listes est très cher (il est), il en cours d' exécution dans chaque vue détaillée est de la question, et une mise en cache est nécessaire.

Mes questions:

  • Pensez-vous que cela est une bonne pratique pour connaître les dossiers voisins pour faire varier les commandes de requête?

  • Savez-vous de meilleures pratiques en termes de performance et de simplicité? Savez-vous quelque chose qui fait cela complètement obsolète?

  • Dans la théorie de la programmation, est-il un nom pour ce problème?

  • Est-ce le nom de « tri cache » est approprié et compréhensible pour cette technique?

  • Y at-il reconnu, des modèles communs pour résoudre ce problème? Que sont-ils appelés?

Remarque: Ma question ne porte pas sur la construction de la liste, ou comment afficher la vue détaillée. Ce sont là que quelques exemples. Ma question est la fonctionnalité de base de la détermination des voisins d'un enregistrement quand une nouvelle requête est impossible, et le plus rapide et le moins cher pour y arriver.

Si quelque chose ne sait pas, s'il vous plaît laisser un commentaire et je vais clarifier.

À partir d'une prime - peut-être il y a un peu plus d'informations sur ce là-bas.

Créé 22/02/2010 à 12:06
source utilisateur
Dans d'autres langues...                            


11 réponses

voix
-3

Vous avez donc deux tâches:

  1. construire la liste triée des éléments (SELECTs avec différents PAR ORDRE)
  2. afficher les détails sur chaque élément (SELECT détails de la base de données avec la mise en cache possible).

Quel est le problème?

PS: si la liste ordonnée peut être trop grand, vous avez juste besoin d'une fonctionnalité de PAGER mis en œuvre. Il pourrait y avoir différentes implémentations, par exemple, vous pouvez ajouter « LIMIT 5 » dans la requête et affiche le bouton « Afficher 5 suivant ». Lorsque ce bouton est enfoncé, condition comme « où le prix <0,89 LIMIT 5 » est ajouté.

Créé 22/02/2010 à 15:04
source utilisateur

voix
16

Voici une idée. Vous pouvez décharger les opérations coûteuses à une mise à jour lorsque les inserts épicier / mises à jour de nouvelles offres plutôt que lorsque l'utilisateur final sélectionne les données à afficher. Cela peut sembler une façon non dynamique pour gérer les données de tri, mais il peut augmenter la vitesse. Et, comme nous le savons, il y a toujours un compromis entre la performance et d'autres facteurs de codage.

Créer une table pour tenir suivant et précédent pour chaque offre et chaque option de tri. (Vous pouvez stocker ce dans le tableau de l'offre si vous aurez toujours trois options de tri - vitesse de requête est une bonne raison de dénormaliser votre base de données)

Vous auriez donc ces colonnes:

  • Type de tri (non triés, le prix, classe et Prix Desc)
  • ID d'offre
  • ID prev
  • ID suivant

Lorsque les informations détaillées pour la page de détail de l'offre est interrogé à partir de la base de données, le NextID et PrevID feraient partie des résultats. Donc, vous seulement besoin d'une requête pour chaque page de détail.

Chaque fois qu'une offre est insérée, mis à jour ou supprimé, vous devez exécuter un processus qui valide l'intégrité / précision de la table sorttype.

Créé 22/02/2010 à 20:20
source utilisateur

voix
1

Je ne sais pas si je comprends bien, si pas, me dis;)

Disons que les Givens sont la requête pour la liste triée et le courant de décalage dans cette liste, à savoir que nous avons un $queryet un $n.

Une solution très évidente pour minimiser les requêtes, serait de récupérer toutes les données à la fois:

list($prev, $current, $next) = DB::q($query . ' LIMIT ?i, 3', $n - 1)->fetchAll(PDO::FETCH_NUM);

Cette déclaration va chercher la précédente, les éléments sont présents en cours et de la base de données dans l'ordre actuel de tri et met les informations associées dans les variables correspondantes.

Mais comme cette solution est trop simple, je suppose que je mal compris quelque chose.

Créé 07/02/2011 à 20:31
source utilisateur

voix
2

J'ai eu des cauchemars avec celui - ci aussi. Votre approche actuelle semble être la meilleure solution , même pour les listes de 10k articles. Les ID de mettre en cache la vue de la liste dans la session http et ensuite utiliser ce pour afficher la page précédente / suivante (personnalisé à l' utilisateur en cours). Cela fonctionne bien surtout quand il y a trop de façons de filtrer et trier la liste initiale des éléments au lieu de seulement 3. En
outre, en stockant la liste complète des ID que vous obtenez pour afficher un "you are at X out of Y"texte améliorant la facilité d' utilisation.
JIRA de précédent / suivant

Soit dit en passant, c'est ce que JIRA fait aussi bien.

Pour répondre directement à vos questions:

  • Oui, c'est une bonne pratique car il adapte sans la complexité du code ajouté lorsque votre filtre / tri et types d'éléments corneille plus complexe. Je l'utilise dans un système de production avec 250k articles avec filtre « infini » / variations de tri. Recadrage des ID cacheable à 1000 est également une possibilité puisque l'utilisateur sera très probablement jamais cliquer sur prev ou suivant plus de 500 fois (Il va revenir en arrière et très probablement affiner la recherche ou paginera).
  • Je ne sais pas d'une meilleure façon. Mais si le genre où limité, ce qui était un site public (sans session http) alors je vous le plus probablement dénormaliser.
  • J'sais.
  • Oui, le cache de tri sonne bien. Dans mon projet, je l'appelle « précédent / suivant sur les résultats de recherche » ou « la navigation sur les résultats de la recherche ».
  • J'sais.
Créé 07/02/2011 à 21:04
source utilisateur

voix
2

En général, je dénormaliser les données des index. Ils peuvent être stockés dans les mêmes lignes, mais je récupérer presque toujours mes identifiants de résultats, puis faire un voyage séparé pour les données. Cela rend la mise en cache des données très simple. Il est pas si important en PHP où le temps de latence est faible et la bande passante élevée, mais une telle stratégie est très utile lorsque vous avez une latence élevée, les applications à faible bande passante, comme un site Web AJAX où une grande partie du site est rendu en JavaScript.

Je en cache toujours les listes de résultats, et les résultats eux-mêmes séparément. Si quelque chose affecte les résultats d'une requête de la liste, le cache de la liste des résultats est actualisée. Si quelque chose affecte les résultats eux-mêmes, ces résultats particuliers sont actualisés. Cela me permet de mettre à jour ou l'autre sans avoir à tout régénérer, ce qui cache efficace.

Étant donné que mes listes de résultats changent rarement, je produis toutes les listes en même temps. Cela peut rendre la réponse initiale un peu plus lent, mais il simplifie rafraîchissante du cache (toutes les listes sont stockées dans une seule entrée de cache).

Parce que j'ai l'ensemble du cache de la liste, il est trivial de trouver des objets voisins sans revisiter la base de données. Avec de la chance, les données relatives à ces éléments seront également mises en cache. Cela est particulièrement pratique lorsque le tri des données en JavaScript. Si je possède déjà une copie en cache sur le client, je peux recourir instantanément.

Pour répondre à vos questions en particulier:

  • Oui, il est une idée fantastique pour découvrir les voisins à l'avance, ou toute information le client est susceptible d'accéder à côté, surtout si le coût est faible et maintenant le coût est élevé à recalcule. Ensuite, il est tout simplement un métier hors de pré-calcul et de stockage par rapport à la vitesse supplémentaire.
  • En termes de performances et de simplicité, éviter les choses qui sont liés à l'articulation des choses logiquement différentes. Les index et les données sont différentes, sont susceptibles d'être modifiées à des moments différents (par exemple l'ajout d'une nouvelle donnée aura une incidence sur les indices, mais pas les données existantes), et doivent donc être accessibles séparément. Cela peut être un peu moins efficace du point de vue un seul thread, mais chaque fois que vous attachez quelque chose ensemble, vous perdez la mise en cache et l'efficacité asychronosity (la clé de mise à l'échelle est asychronosity).
  • Le terme pour obtenir des données à l'avance est préchargement. Le préchargement peut se produire au moment de l'accès ou en arrière-plan, mais avant que les données pré tiré par les cheveux est réellement nécessaire. De même avec pré-calcul. Il est un compromis du coût maintenant, le coût de stockage, et le coût pour obtenir en cas de besoin.
  • « Cache Tri » est un nom approprié.
  • Je ne sais pas.

Aussi, lorsque vous mettez en cache les choses, les mettre en cache au niveau le plus générique possible. Certaines choses pourraient être spécifiques à l'utilisateur (comme les résultats pour une requête de recherche), où d'autres pourraient être agnostique utilisateur, telles que la navigation d'un catalogue. Les deux peuvent bénéficier de la mise en cache. La requête de catalogue peut-être fréquentes et économiser un peu à chaque fois, et la requête de recherche peut être coûteux et d'économiser beaucoup plusieurs fois.

Créé 09/02/2011 à 08:00
source utilisateur

voix
0

Il y a autant de façons de le faire à la peau du chat proverbial. Alors, voici quelques moi.

Si votre requête initiale est cher, que vous dites, puis créer une autre table peut-être une table de mémoire peuplant les résultats de votre cher et exécutez rarement requête principale.

Cette deuxième table pourrait alors être interrogé sur tous les plans et le tri est aussi simple que de l'ordre de tri approprié.

Comme il est nécessaire repeupler la deuxième table avec des résultats de la première table, gardant ainsi les nouvelles données, mais réduisant au minimum l'utilisation de la requête coûteuse.

Alternativement, si vous voulez éviter même connexion à la DB, vous pouvez ensuite stocker toutes les données dans un tableau de php et de le stocker en utilisant memcached. ce serait très rapide et fourni vos listes ne sont pas trop grand serait efficace des ressources. et peuvent être facilement triés.

DC

Créé 11/02/2011 à 05:19
source utilisateur

voix
0

Hypothèses de base:

  • Promotions sont hebdomadaires
  • Nous pouvons nous attendre sur le site pour changer rarement ... probablement tous les jours?
  • Nous pouvons contrôler les mises à jour de la base de données avec de l'éther une API ou répondre par des déclencheurs

Si le site change sur une base quotidienne, je suggère que toutes les pages sont générées statiquement du jour au lendemain. Une requête pour chaque itération tri par ordre et fait toutes les pages liées. Même s'il y a des éléments dynamiques, les chances sont que vous pouvez les traiter en incluant les éléments de la page statique. Ceci fournirait la page optimale service et aucune charge de base de données. En fait, vous pourriez peut-être générer des pages séparées et les éléments suivant / précédent qui sont inclus dans les pages. Cela peut être plus fou avec 200 façons de trier, mais avec 3 je suis un grand fan de celui-ci.

?sort=price
include(/sorts/$sort/tomatoes_class_1)
/*tomatoes_class_1 is probably a numeric id; sanitize your sort key... use numerics?*/

Si pour une raison quelconque cela est impossible, je recourir à la mémorisation. Memcache est populaire pour ce genre de choses (jeu de mots!). Quand quelque chose est poussé à la base de données, vous pouvez émettre un déclencheur pour mettre à jour votre cache avec les valeurs correctes. Pour ce faire, de la même manière que vous le feriez si comme si votre élément mis à jour existait dans 3 listes chaînées - réassocier selon le cas (this.next.prev = this.prev, etc.). De là, aussi longtemps que votre cache ne surchargez pas, vous serez en tirant des valeurs simples de la mémoire d'une façon clé primaire.

Cette méthode prendra un certain codage supplémentaire sur les méthodes de sélection et de mise à jour / insertion, mais il devrait être assez minime. En fin de compte , vous regarderez en place [id of tomatoes class 1].price.next. Si cette clé est dans votre cache, or. Dans le cas contraire, insérez dans le cache et l' affichage.

  • Pensez - vous que cela est une bonne pratique pour connaître les dossiers voisins pour faire varier les commandes de requête? Oui. Il est sage d'effectuer regard sur les demandes discontinues effectuées à venir attendues.
  • Savez - vous de meilleures pratiques en termes de performance et de simplicité? Savez - vous quelque chose qui fait cela complètement obsolète? Espérons que ci - dessus
  • Dans la théorie de la programmation, est - il un nom pour ce problème? Optimisation?
  • Est -ce le nom de « tri cache » est approprié et compréhensible pour cette technique? Je ne suis pas sûr d'un nom approprié spécifique. Il est la mise en cache, il est un cache de toutes sortes, mais je ne suis pas sûr que me dire que vous avez un « cache tri » transmettrait compréhension immédiate.
  • Y at - il reconnu, des modèles communs pour résoudre ce problème? Que sont - ils appelés? Mise en cache?

Désolé mes réponses de résidus miniers sont un peu inutile, mais je pense que mes solutions narratives devraient être très utiles.

Créé 11/02/2011 à 18:13
source utilisateur

voix
0

Vous pouvez enregistrer les numéros de ligne des listes ordonnées en vue , et vous pouvez atteindre les éléments précédents et suivants dans la liste sous (current_rownum-1) et (current_rownum + 1) les numéros de ligne.

Créé 12/02/2011 à 14:01
source utilisateur

voix
0

Le problème / datastructur est nommé bidirectionnel graphique ou vous pouvez dire que vous avez plusieurs listes chaînées.

Si vous pensez comme une liste chaînée, vous pouvez simplement ajouter des champs à la table des objets pour chaque tri et prev / clé suivante. Mais la personne DB vous tuer pour cela, il est comme GOTO.

Si vous pensez comme un (bi-) graphique directionnel, vous allez avec la réponse de Jessica. Le principal problème est que les mises à jour il y a des commandes sont des opérations coûteuses.

 Item Next Prev
   A   B     -
   B   C     A
   C   D     B
   ...

Si vous changez de position un des éléments au nouvel ordre A, C, B, D, vous devez mettre à jour 4 lignes.

Créé 13/02/2011 à 02:20
source utilisateur

voix
4

J'ai une idée assez semblable à de Jessica. Cependant, au lieu de stocker des liens vers les articles de tri suivant et précédent, vous stockez l'ordre de tri pour chaque type de tri. Pour trouver l'enregistrement précédent ou suivant, juste obtenir la ligne avec SortX = currentSort ++ ou SortX = currentSort--.

Exemple:

Type     Class Price Sort1  Sort2 Sort3
Lettuce  2     0.89  0      4     0
Tomatoes 1     1.50  1      0     4
Apples   1     1.10  2      2     2
Apples   2     0.95  3      3     1
Pears    1     1.25  4      1     3

Cette solution donnerait des délais très courts de la requête, et prendrait moins d'espace disque que l'idée de Jessica. Cependant, comme je suis sûr que vous vous rendez compte, le coût de la mise à jour d'une ligne de données est nettement plus élevé, puisque vous devez recalcule et stocker tous les ordres de tri. Mais encore, en fonction de votre situation, si les données mises à jour sont rares et surtout si elles se produisent toujours en vrac, cette solution pourrait être le meilleur.

c'est à dire

once_per_day
  add/delete/update all records
  recalculate sort orders

Espérons que cela est utile.

Créé 13/02/2011 à 03:30
source utilisateur

voix
0

Toutes mes excuses si j'ai mal compris, mais je pense que vous voulez conserver la liste ordonnée entre l'utilisateur accède au serveur. Si oui, votre réponse réside peut-être dans votre stratégie de mise en cache et des technologies plutôt que dans la base de données optimisation des requêtes / de schéma.

Mon approche serait de sérialisation () le tableau une fois son premier extrait, puis cache dans une zone de stockage séparée; si c'est Memcached / APC / disque dur / MongoDB / etc, et conserver les détails de l'emplacement du cache pour chaque utilisateur individuellement par leurs données de session. Le backend de stockage réel serait naturellement en fonction de la taille du tableau, que vous ne rentrez pas dans beaucoup de détails sur, mais les échelles memcached grand sur plusieurs serveurs et mongo encore plus à un coût de latence légèrement supérieure.

Vous n'indiquez pas aussi combien de permutations genre il y a dans le monde réel; par exemple, avez-vous besoin pour mettre en cache des listes séparées par utilisateur, ou pouvez-vous globalement le cache par permutation de tri puis filtrer ce que vous n'avez pas besoin via PHP ?. Dans l'exemple que vous donnez, je voudrais simplement mettre en cache les permutations et stocker lequel des deux je devais unserialize () dans les données de session.

Lorsque l'utilisateur retourne sur le site, vérifiez le temps de vivre la valeur des données mises en cache et réutiliser si elle est encore valide. Je voudrais aussi avoir un déclencheur en cours d'exécution sur INSERT IGNORE / UPDATE / DELETE pour les offres spéciales qui définit simplement un champ d'horodatage dans une table séparée. Cela immédiatement indiquer si le cache était vicié et que la requête devait être relancée pour un très faible coût de la requête. La grande chose au sujet seulement en utilisant la gâchette pour définir un champ unique est qu'il n'y a pas besoin de se soucier la taille des valeurs anciennes / redondantes sur cette table.

Que ce soit approprié dépendra de la taille des données est revenu, à quelle fréquence il a été modifié, et quelles technologies de mise en cache sont disponibles sur votre serveur.

Créé 13/02/2011 à 15:47
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more