MySQL深度翻页和底层执行原理之间的关联和优化思路

1.翻页参数详解

limit 5000 , 10;
5000 (offset):跨过5000条记录
10(每页记录数):取10条
也就是 5001~5010
相关公式推断:
offset-》页:每页10条,跨过5000条,也就是5000/10=500页,跨过500页,也就是现在取的 5001~5010是第501页 通用公式:页 = offset/每页记录数
页-》offset:第501页,要跨500页,即跨500*10=5000条记录(offset)通用公式:offset=(页-1)*10

2.“深度翻页”一词的来源

selct * from t_user limit 5000 , 10;
这就是深度翻页,也就是跨很多记录取取数据。

3.MySQL底层执行原理

客户端将sql嵌入MySQL的server端提供的api,server端根据执行计划通过执行器操作执行引擎,执行引擎通过I/O获取记录返回给server端,server端再返回给客户端。总得来说就是:客户端-》server-》执行引擎-》磁盘I/O。具体调用过程如下:

4.那它和MySQL底层执行原理有什么孽缘呢?

select * from t_user order by key1 limit 5000, 10;
如果使用idx_key1执行上述查询,那么MySQL会这样处理:

  1. server层向InnoDB要第1条记录,InnoDB从idx_key1中获取到第一条二级索引记录,然后进 行回表操作得到完整的聚簇索引记录,然后返回给server层。server层准备将其发送给客户 端,此时发现还有个 LIMIT 5000, 10的要求,意味着符合条件的记录中的第5001条~5010才可以真 正发送给客户端,所以在这里先做个统计,我们假设server层维护了一个称作limit_count的变 量用于统计已经跳过了多少条记录,此时就应该将limit_count设置为1。
  2. server层再向InnoDB要下一条记录,InnoDB再根据二级索引记录的next_record属性找到下 一条二级索引记录,再次进行回表得到完整的聚簇索引记录返回给server层。server层在将其 发送给客户端的时候发现limit_count才是1,所以就放弃发送到客户端的操作,将limit_count 加1,此时limit_count变为了2。
  3. … 重复上述操作
  4. 直到limit_count等于5010的时候,server层才会真正的将InnoDB返回的5001~5010条完整聚簇索引记录发 送给客户端。
    从上述过程中我们可以看到,由于MySQL中是在实际向客户端发送记录前才会去判断LIMIT子句 是否符合要求,所以如果使用二级索引执行上述查询的话,意味着要进行5010次回表操作。server层在进行执行计划分析的时候会觉得执行这么多次回表的成本太大了,还不如直接全表扫描 +filesort快呢,所以就选择了后者执行查询。

5.如何通过优化sql来减少深度翻页的查询用时

既然深度翻页的成本是回表次数,那么我们就想办法减少回表次数:
a. 覆盖索引+join内连接
select * from t_user
join
(select id from t_user order by key1 limit 5000, 10) t_user_temp
on t_user.id = t_user_temp.id;
解析:
既让成本在于select id from t_user order by key1 limit 5000, 10,那么这处就不让它回表,采用覆盖索引,减少5010次回表,然后 join全表扫描。思考:join底层执行流程是怎么样的,在哪棵树操作的?
b. 主键游标(主键需自增)
select * from t_user where id > 0 order by id limit 0 , 10; //第1页
select * from t_user where id > 10 order by id limit 0 , 10; //第2页
select * from t_user where id > 20 order by id limit 0 , 10; //第3页

select * from t_user where id > 5000 order by id limit 0 , 10; //第6页
思考:使用游标的方案,一定要记得对游标列排序。为什么要这么做,自增id已经排序了啊。

你可能感兴趣的:(Mysql,mysql,深度翻页,sql优化)