Краткият отговор е да, първичният ключ има ред, всички индекси имат ред, а първичният ключ е просто уникален индекс.
Както правилно казахте, не трябва да разчитате на данните, които се връщат в реда, в който се съхраняват данните, оптимизаторът е свободен да ги връща във всеки ред, който желае, и това ще зависи от плана на заявката. Въпреки това ще се опитам да обясня защо вашата заявка е работила в продължение на 12 години.
Вашият клъстерен индекс е само данните от вашата таблица и вашият клъстерен ключ определя реда, в който се съхраняват. Данните се съхраняват на листа и клъстерният ключ помага на корена (и междинните бележки) да действат като указатели за бързо достигане до десен лист за извличане на данните. Неклъстерният индекс е много подобна структура, но най-ниското ниво просто съдържа указател към правилната позиция върху листа на клъстерирания индекс.
В MySQL първичният ключ и клъстерираният индекс са синоними, така че първичният ключ е подреден, но те са фундаментално две различни неща. В други СУБД можете да дефинирате както първичен ключ, така и клъстерен индекс, когато направите това, вашият първичен ключ става уникален неклъстерен индекс с указател обратно към клъстерния индекс.
Най-просто казано, можете да си представите таблица с колона 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 няма да направят това в сценариите, които съм тествал.
Накратко, просто добавете поръчка по или използвайте MIN(ID) и готово. Не бих загубил твърде много сън, опитвайки се да навляза във вътрешната работа на оптимизатора на заявки, за да видя какъв вид фрагментация или диапазони от данни ще са необходими, за да се наблюдава различно подреждане на клъстерирания индекс в рамките на плана на заявката.