mysql查询时每条记录的原始返回顺序
mysql 查询时每条记录的原始返回顺序,由此sql语句的执行计划和内部存储引擎的实现决定【跟实现细节有关,跟sql语义无关=>不是 接口契约 即在sql语义上来说是未定义的】=>只有查询时加了 order by 后才根据order by字段 升序或者降序返回
即 原始返回顺序,由 MySQL 在该次查询中选择的"访问路径(access path)"决定,而不是 SQL 语义决定。
受执行计划的影响:
如果执行计划是 主键索引扫描:返回顺序 ≈ 主键 B+Tree 的叶子节点顺序【而不是插入到表时顺序】
如果 MySQL 走的是 二级索引:顺序是:二级索引 key 顺序 + 主键值顺序【而不是插入到表时顺序】
如果一条sql语句本身虽然没变,但是执行计划变了,比如索引变动;原始查询(不加order by 的查询)的返回顺序也会变
limit分页查询优化
LIMIT offset, count 在 offset 很大时变慢,是因为 MySQL 必须"先找到并丢弃 offset 行",这些工作量不会因为 LIMIT 而减少。
即mysql必须根据执行计划先找到前offset行 再丢弃;=>跟是否加索引无关,无论是否加索引都是先要找到前offset行 再丢弃 =>LIMIT 只限制返回行数,不限制扫描行数。
优化方案:
1.游标分页
如果能确定offset那一行的某个唯一列的值(比如自增主键ID),可以用 WHERE 子句来代替 OFFSET,从而避免扫描和丢弃大量行。 以及保证查询得到返回顺序结构不变【比如使用order by 前面的那个唯一的列】=>就可以使用 游标分页
SELECT *
FROM t
WHERE id > last_id 【范围查询】
ORDER BY id
LIMIT 20;
游标分页的条件是:只要使用范围查询的键(字段)满足 全序,稳定,可比较,唯一性 即可【不要求该键必须连续自增】=>雪花算法生成的ID 是可以的
2.覆盖索引+回表查询=>通常是在子查询中先使用 覆盖索引查询进行limit分页查询,虽然此时子查询中依然需要扫描offset的条数,但是因为是覆盖索引扫描,扫描效率高很多;然后再通过主键回表查询完整记录=>【即外部的查询通过子查询的结果进行回表查询查询完整数据】
相当于大量的offset扫描是在覆盖索引上进行的,回表查询只查询少量数据
SELECT *
FROM t
WHERE id IN (
SELECT id
FROM t
ORDER BY id
LIMIT 1000000, 20
)
ORDER BY id;
3.不允许用户查询过大的offset,必须通过时间,状态,关键字等条件进行过滤,只查询过滤后的数据减少offset的大小=>相当于一种泛化(模糊)版的游标分页