简短的回答是肯定的,主键有顺序,所有索引都有顺序,主键只是一个唯一索引。
正如您所说,您不应该依赖于按照数据存储的顺序返回数据,优化器可以自由地按照它喜欢的任何顺序返回数据,这将取决于查询计划。不过,我将尝试解释为什么您的查询已经有效了 12 年。
你的聚集索引只是你的表数据,你的聚集键定义了它的存储顺序。数据存储在叶子上,聚集键帮助根(和中间注释)充当指针来快速到达右叶检索数据。非聚集索引是一个非常相似的结构,但最低层仅包含一个指向聚集索引叶上正确位置的指针。
在 MySQL 中,主键和聚集索引是同义词,因此主键是有序的,但它们本质上是两个不同的东西。在其他 DBMS 中,您可以定义主键和聚集索引,当您执行此操作时,您的主键将成为唯一的非聚集索引,并带有返回聚集索引的指针。
用最简单的术语来说,您可以想象一个具有主键 ID 列和另一列 (A) 的表,您的聚集索引的 B 树结构将类似于:
Root Node
+---+
| 1 |
+---+
Intermediate Nodes
+---+ +---+ +---+
| 1 | | 4 | | 7 |
+---+ +---+ +---+
Leaf
+-----------+ +-----------+ +-----------+
ID -> | 1 | 2 | 3 | | 4 | 5 | 6 | | 7 | 8 | 9 |
A -> | A | B | C | | D | E | F | | G | H | I |
+-----------+ +-----------+ +-----------+
实际上,叶子页面会更大,但这只是一个演示。每个页面还有一个指向下一页和上一页的指针,以便于遍历树。因此,当您执行如下查询时:
SELECT ID, A
FROM T
WHERE ID > 5
LIMIT 1;
您正在扫描唯一索引,因此这很可能是顺序扫描。但很可能无法保证。
MySQL将扫描根节点,如果存在潜在的匹配,它将移动到中间节点,如果子句是这样的WHERE ID < 0
那么 MySQL 就会知道没有任何结果,而无需超出根节点。
一旦它移动到中间节点,它就可以识别出需要从第二页(4 到 7 之间)开始搜索ID > 5
。因此,它将从第二个叶子页开始顺序扫描叶子,已经识别出LIMIT 1
一旦找到匹配项(在本例中为 6),它将停止并从叶子返回此数据。在这样一个简单的例子中,这种行为似乎是可靠且合乎逻辑的。我尝试通过选择一个我知道位于叶页末尾的 ID 值来强制异常,以查看叶页是否会以相反的顺序扫描,但到目前为止还无法产生这种行为,但这并不意味着它不会发生,或者 MySQL 的未来版本不会在我测试过的场景中执行此操作。
简而言之,只需添加一个 order by,或使用 MIN(ID) 即可完成。我不会浪费太多时间去深入研究查询优化器的内部工作原理,以了解需要什么样的碎片或数据范围来观察查询计划中聚集索引的不同顺序。