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 users
table 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.
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
.
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
.