49 votes

Quels schémas de pagination peuvent gérer des listes de contenu à évolution rapide?

La Pagination est dur lorsque votre contenu classement peut changer rapidement, et encore plus difficile lorsque ces classements diffèrent par utilisateur. (Nous allons traiter infini défilement comme un type de pagination, où les liens sont invisibles.) Il y a deux problèmes difficiles: nouveau contenu ajouté en haut, et reranked de contenu.

Oublions nouvellement ajouté de contenu, et d'accepter que vous allez avoir à actualiser la page 1 pour voir. Gardons-nous de prétendre que nous faisons pure, ORDER BY position; si vous êtes de la commande par quelque chose d'autre, vous pouvez utiliser les fonctions de la fenêtre. Nos pages ont 4 rangées d'animaux par page. Ils commencent à sortir:

+----+----------+-----------+
| id | position^|  animal   |
+----+----------+-----------+
|  1 |        1 | Alpacas   |
|  2 |        2 | Bats      |
|  3 |        3 | Cows      |
|  4 |        4 | Dogs      |
|  5 |        5 | Elephants |
|  6 |        6 | Foxes     |
|  7 |        7 | Giraffes  |
|  8 |        8 | Horses    |
+----+----------+-----------+

Après on va chercher la page 1, et avant de nous extraire de la page 2, un grand nombre d'éléments à déplacer. La DB est maintenant:

+----+----------+-----------+
| id | position^|  animal   |
+----+----------+-----------+
|  4 |        1 | Dogs      |
|  2 |        2 | Bats      |
|  1 |        3 | Alpacas   |
|  5 |        4 | Elephants |
|  6 |        5 | Foxes     |
|  7 |        6 | Giraffes  |
|  3 |        7 | Cows      |
|  8 |        8 | Horses    |
+----+----------+-----------+

Il existe trois approches:

Offset/limite approche

C'est typique de l'approche naïve; dans les Rails, c'est la façon dont will_paginate et Kaminari travail. Si je veux extraire de la page 2, je vais le faire

SELECT * FROM animals
ORDER BY animals.position
OFFSET ((:page_num - 1) * :page_size) 
LIMIT :page_size;

qui obtient les lignes 5-8. Je ne vais jamais voir les Éléphants, et je vais voir les Vaches deux fois.

Vu pour la dernière ID approche

Reddit prend une approche différente. Au lieu de calculer la première ligne en fonction de la taille de page, le client suit l'ID du dernier élément que vous avez vu, comme un signet. Lorsque vous appuyez sur "suivant", ils commencent à la recherche de ce signet et les années suivantes:

SELECT * FROM animals
WHERE position > (
  SELECT position FROM animals 
  WHERE id = :last_seen_id
) 
ORDER BY position
LIMIT :page_size;

Dans certains cas, cela fonctionne mieux que la page/offset. Mais dans notre cas, les Chiens, le dernier-vu le post, agrandie de droit de #1. Ainsi, le client envoie ?last_seen_id=4, et ma page 2 est les Chauves-souris, des Alpagas, des Éléphants et des Renards. Je n'ai pas manqué tous les animaux, mais j'ai vu des Chauves-souris et les Alpagas deux fois.

Côté serveur de l'état

HackerNews (et notre site, pour l'instant) résout ce côté serveur continuations; ils stockent l' ensemble de l' ensemble de résultats pour vous (ou au moins plusieurs pages à l'avance?), et le "Plus" références de lien que la continuation. Lorsque j'extrais de la page 2, je demande "à la page 2 de mon original de la requête". Il utilise le même offset/calcul de la limite, mais vu que c'est à l'encontre de la requête d'origine, je n'ai tout simplement pas de soins que les choses ont maintenant déplacé autour de. Je vois des Éléphants, des Renards, des Girafes, et des Chevaux. Pas de dup, pas manquer des éléments.

L'inconvénient est que nous avons à stocker beaucoup de l'état sur le serveur. Sur HN, qui est stocké dans la RAM, et qu'en réalité, ces continuations souvent expire avant que vous pouvez appuyer sur le bouton "Plus", vous forçant à aller tout le chemin du retour à la page 1 de trouver un lien valide. Dans la plupart des applications, vous pouvez la stocker dans memcached, ou même dans la base de données elle-même (à l'aide de votre propre table, ou dans Oracle ou PostgreSQL, à l'aide de holdable les curseurs). Selon votre demande, il pourrait y avoir un gain de performance; dans PostgreSQL, au moins, vous devez trouver un moyen de frapper la bonne connexion de base de données à nouveau, ce qui nécessite beaucoup de collant-état ou astucieux back-end de routage.

Ces trois approches possibles? Si non, est-il de l'ordinateur-les concepts de la science, qui me donnerait à Google de jus de lire à ce sujet? Est-il possible de rapprocher la poursuite de l'approche sans stockage de l'intégralité du jeu de résultats? À Long terme, il y a des événements complexes-streaming/point-à-temps des systèmes, où "le jeu de résultat au moment où je l'ai récupéré à la page 1" est indéfiniment dérivable. Bref de ce que... ?

8voto

Todd Gibson Points 21

Oracle gère cela très bien. Tant que le curseur est ouvert, vous pouvez le récupérer autant de fois que nécessaire et les résultats sont toujours le reflet de la date à laquelle le curseur a été ouvert. Il utilise les données de l'annuler les journaux à pratiquement annulation des modifications qui ont été commis après le curseur a été ouvert.

Il fonctionnera aussi longtemps que la nécessaire restauration des données est toujours disponible. Finalement, les journaux recyclés et la restauration de données n'est plus disponible, il ya une certaine limite, selon le journal de l'espace, de l'activité du système, etc.

Malheureusement (OMI), je ne connais pas d'autre DB qui fonctionne comme ceci. Les autres bases de données que j'ai travaillé avec l'utilisation de verrous pour assurer la lecture de la cohérence, ce qui est problématique si vous voulez la cohérence de la lecture de plus de très courte durée.

6voto

Aurelien Porte Points 824

Solution 1: "le hacky solution"

Une solution pourrait consister à votre client de conserver une trace du déjà vu du contenu, une liste d'Id par exemple. Chaque fois que vous besoin d'une autre page, vous pouvez ajouter cette liste d'identification des paramètres de votre serveur d'appel. Votre serveur peut alors commander le contenu, supprimer déjà vu le contenu et appliquer le décalage pour obtenir la page de droite.

Je ne le recommanderais pas, toutefois, et j'insiste sur le hacky. Je viens de l'écrire ici, parce que c'est rapide et pourrait s'adapter à certains besoins. voici les mauvaises choses je pense:

1) Il a besoin de quelques travaux sur côté client pour obtenir la droite (ce qui ne "déjà-vu" dans ma phrase ci-dessus, que si je vais à une page précédente?)

2) L'ordonnance qui en résulte ne reflète pas votre véritable politique de commande. Un contenu peut être affiché dans la page 2 bien que la politique aurait dû le mettre sur la page 1. Il pourrait conduire à un utilisateur d'un malentendu. Prenons l'exemple de dépassement de pile avec son ancienne politique de commande, cela signifie que la plupart des upvoted réponses d'abord. Nous pourrions avoir une question avec 6 upvotes être en page 2 tout une question avec 4 upvotes serait en page 1. Cela arrive quand l'2 ou plus upvotes s'est produite pendant que l'utilisateur est toujours à la page 1. --> peut être surprenant pour l'utilisateur.

Solution 2: "la solution de client"

En gros, c'est le côté client de la solution équivalente à celle que vous appelez "côté serveur de l'état". Il est alors utile que si le fait de garder une trace de l'intégralité de la commande sur le côté serveur n'est pas pratique assez. Il fonctionne si la liste des éléments, n'est pas infinie.

  • Appelez votre serveur pour obtenir le plein (fini) liste de commande + le nombre d'éléments/page
  • L'enregistrer sur le côté client
  • Récupérer des éléments directement par le biais de l'id de votre contenu.

4voto

Jay Levitt Points 615

Nous allons avec le serveur-côté de l'état d'approche pour l'instant, la mise en cache l'ensemble du résultat de la première requête, nous avons donc toujours retourner une liste cohérente. Cela permettra de travailler aussi longtemps que notre requête déjà renvoie toutes les lignes; à terme, nous aurons besoin d'utiliser un plus proche voisin approche et que l'habitude de travailler.

Mais je pense qu'il y a une quatrième possibilité, qui adapte très bien, aussi longtemps que:

  1. Vous n'avez pas besoin d'une garantie de pas de doublons, seule une forte probabilité
  2. Vous n'êtes pas d'accord avec manque un peu de contenu au cours des parchemins, aussi longtemps que vous éviter les doublons

La solution est une variante de la "dernière fois" ID de solution: demander au client de conserver non pas un, mais 5 ou 10 ou 20 signets - peu assez que vous pouvez les stocker de manière efficace. La requête finit par ressembler à:

SELECT * FROM posts
WHERE id > :bookmark_1
AND id > :bookmark_2
...
ORDER BY id

Comme le nombre de signets grandit, les chances rapidement diminuer que vous êtes (a) commençant à un certain point-delà de toutes les signets, mais (b) voir le contenu dupliqué de toute façon parce qu'ils étaient tous reranked.

Si il y a des trous, ou de meilleures réponses dans le futur, je vais joyeusement unaccept cette réponse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X