MySQL - 基于 UUID/created_at 游标的分页?

Won*_*nka 7 mysql cursors select paging mysql-5.7

对于大型数据集,使用 an 进行分页OFFSET是众所周知的,并且不是最好的分页方式。更好的分页方式是使用游标,它只是行上的一个唯一标识符,因此我们知道从最后一个光标位置上次离开的位置继续分页的位置。

当涉及到一个自动递增id值的游标时,实现起来相当容易:

SELECT * FROM users
WHERE id <= %cursor // cursor is the auto incrementing id, ex. 100000
ORDER BY id DESC
LIMIT %limit
Run Code Online (Sandbox Code Playgroud)

我们不确定的是,如果不是自动递增id游标,游标的唯一唯一顺序标识符是表行上的uuidcreated_at

我们当然可以根据 查询uuid得到created_at,然后选择所有的users<= created_at但问题是如果表中有多个相同created_at时间戳的实例users怎么办?知道如何users根据uuid/created_at游标组合查询表以确保我们获得正确的数据集(就像我们使用自动递增一样id)?再次,只有独特的领域是uuid因为created_at可能是重复的,但他们的组合是每行唯一的。

Fed*_*oli 6

我会回答你的问题,但首先让我告诉你,我不明白你为什么要这样做。自动增量 ID 非常适合此任务。但同时使用时间戳列是正确的,因为依赖 id 进行排序是一种不好的做法。为什么?因为在某些情况下,其顺序可能不按时间顺序排列 - 例如,如果您使用 Galera 集群并且有故障转移。

要执行您要求的操作,首先创建此索引:

ALTER TABLE users
    ADD INDEX idx_created_at_uuid (created_at, uuid);
Run Code Online (Sandbox Code Playgroud)

顺序列很重要。如果颠倒过来,索引就没有用了。

现在您只需要运行如下查询:

SELECT some_columns
    FROM users
    WHERE (created_at, uuid) < (x, y)
    ORDER BY created_at DESC;
Run Code Online (Sandbox Code Playgroud)

uuid只是因为created_at不唯一才需要。上面的表达式使用称为行构造函数的语法。, unless these values are equal, in which case it compares 它很直观:它与created_at < x uuid y`相同and

如果created_at不是索引中的第一列,MySQL 将必须读取所有行并将它们复制到临时表(可以在内存中或在磁盘上)以对它们进行排序。

如果您决定使用 id,只需保留上面的片段,但替换uuidid.

编辑 11 月 22 日 16 日:WHERE 子句不正确。我修复了它并解释了语法。


Ric*_*mes 6

WHERE   created_at <= x
  AND ( created_at < x OR uuid < y )
ORDER BY created_at DESC,
         uuid       DESC
Run Code Online (Sandbox Code Playgroud)

或这个等价物:

WHERE (     created_at < x
       OR ( created_at = x AND uuid < y )
      )
ORDER BY created_at DESC,
         uuid       DESC
Run Code Online (Sandbox Code Playgroud)

这种技术适用于任何一对列,其中第一个 ( created_at) 可能有重复,第二个是唯一的 (uuidid)。

这是必需的:

INDEX(created_at, uuid)
Run Code Online (Sandbox Code Playgroud)

请注意, 的两个部分WHERE都是DESC。混合ASCDESC将击败的可用性INDEX。(MySQL 8.0 可以解决这个问题。)

另请注意,这假设您不关心行在created_at重复时的顺序,但您确实需要一致的顺序。请注意,这uuid似乎是随机的,但仍然是一致的。话虽如此,id(有或没有 Galera)并且uuid工作得同样好。

(UUID 很烂,但这是一个不同的讨论。)

更多关于不使用OFFSET.