web-dev-qa-db-fra.com

Mysql - uuid / créé_at pagination à base de curseur?

Pour un grand jeu de données, la pagination avec un OFFSET est connue pour être lente et non la meilleure façon de paginer. Une bien meilleure façon de paginer est avec un curseur, qui est juste un identifiant unique sur la ligne afin que nous sachions où continuer la pagination de l'endroit où nous avons quitté la dernière position du curseur.

Lorsqu'il s'agit d'un curseur où il s'agit d'une valeur d'auto-incrémentation automatique id valeur, il est assez facile à mettre en œuvre:

SELECT * FROM users
WHERE id <= %cursor // cursor is the auto incrementing id, ex. 100000
ORDER BY id DESC
LIMIT %limit

Ce dont nous ne sommes pas certains, c'est si au lieu d'une incrémentation automatique id curseur, les seuls identifiants séquentiels uniques pour le curseur sont uuid et created_at sur les rangées de table.

Nous pouvons certainement interroger en fonction de la uuid pour obtenir le created_at, puis sélectionnez tout users qui sont <= created_at Mais la question est de savoir si il y a plusieurs instances de la même chose created_at horodatage dans la table users? Aucune idée de la question de la question userstable basée sur uuid/created_at Combinaison de curseur Pour que nous obtenons les jeux de données corrects (comme si nous utilisions l'incrémentation automatique id)? Encore une fois, le seul champ unique est uuid depuis created_at peut être dupliquer, mais leur combinaison serait unique par ligne.

3
Wonka

Je vais répondre à ce que vous avez demandé, mais laissez-moi d'abord vous dire que je ne comprends pas pourquoi vous voulez faire cela. Un identifiant d'autocramentaux est très bon pour cette tâche. Mais il est correct d'utiliser également une colonne TIMESTAMP, car elle est une mauvaise pratique de compter sur un identifiant pour le tri. Pourquoi? Parce qu'il y a des cas où sa commande pourrait ne pas être chronologique - par exemple, si vous utilisez Galera Cluster et que vous avez des basculement.

Pour faire ce que vous avez posé, créez d'abord cet index:

ALTER TABLE users
    ADD INDEX idx_created_at_uuid (created_at, uuid);

Les colonnes de commande sont importantes. Si vous inversez-le, l'index ne sera pas utile.

Maintenant, il vous suffit de faire une question comme celle-ci:

SELECT some_columns
    FROM users
    WHERE created_at <= x AND uuid = y
    ORDER BY created_at DESC;

uuid n'est nécessaire que parce que créé_at n'est pas unique. Si created_at N'est pas la première colonne, MySQL devra lire toutes les lignes et les copier sur une table temporaire (qui pourrait être en mémoire ou sur disque) pour les trier.

Si vous décidez d'utiliser l'identifiant, gardez simplement les extraits ci-dessus, mais remplacez uuid avec id.

3
Federico Razzoli
WHERE   created_at <= x
  AND ( created_at < x OR uuid < y )
ORDER BY created_at DESC,
         uuid       DESC

ou cet équivalent:

WHERE (     created_at < x
       OR ( created_at = x OR uuid < y )
      )
ORDER BY created_at DESC,
         uuid       DESC

Cette technique fonctionne pour toute paire de colonnes où le premier (created_at) Pourrait avoir des duplicats et la seconde est unique (uuid ou id).

Et cela est requis:

INDEX(created_at, uuid)

Notez que les deux parties du WHERE sont DESC. Mélange ASC et DESC vaincre la convivialité du INDEX. (MySQL 8.0 peut contourner cela.)

Notez également que cela suppose que vous vous souciez de ne pas vous soucier de l'ordre des lignes lorsque created_at Est dupliqué, mais vous voulez un cohérent Commander. Notez que uuid sera apparemment aléatoire, mais toujours cohérent. Avec cela dit, id (avec ou sans galera) et uuid fonctionne également bien.

(Uuids sucer, mais c'est un Différente discussion .)

Plus sur pagination sans utiliser OFFSET .

4
Rick James