全表扫描
概述:
InnoDB的数据是保存在主键索引上的,所以全表扫描实际上是直接扫描表t的主键索引。
如果没有其他的判断条件,所以查到的每一行都可以直接放到结果集里面,然后返回给客户端。
mysql 取数据和发数据的流程:
- 获取一行,写到net_buffer中。(内存大小由参数net_buffer_length定义,默认为16k)
- 重复获取行,直到net_buffer写满,调用网络接口发出去
- 如果发送成功,就清空net_buffer,然后继续取下一行,并写入net_buffer
- 如果发送函数返回EAGIN或WSAEWOULDBLOCK,就表示本地网络栈(socket send buffer)写满了,进入等待。直到网络栈重新可写,再继续发送。
一个查询语句的状态流转(简单流程)
- MySQL查询语句进入执行阶段后,首先把状态设置成“Sending data”;
- 发送执行结果的列相关的信息(meta data) 给客户端;
- 继续执行语句的流程
- 执行完成后,状态设置为空字符串
“Sending data”并不一定是指“正在发送数据”,而可能是处于执行器过程中的任意阶段。
对服务层的影响
- 一个查询在发送过程中,占用的MySQL内部的内存最大就是net_buffer_length这么大
- 如果socket send buffer被写满,就会暂停读数据的流程。
MySQL是“边读边发的”,如果客户端接受的慢,会导致mysql服务端由于结果发不出去,这个事务执行时间变长。 如果客户端使用–quick参数,会使用mysql_use_result方法。这个方法是读一行处理一行。
因此对于正常的线上业务来说,如果一个查询的返回结果不会很多的话,我都建议你使用mysql_store_result这个接口,直接把查询结果保存到本地内存。
LRU算法
概念: InnoDB内存管理用的是最近最少使用 (Least Recently Used, LRU)算法,这个算法的核心就是淘汰最久未使用的数据。LRU算法是用链表来实现的。
算法的核心思想是 每次访问会将对应的数据页Px刷新到链表头部,当内存已满的情况将会清空链表末尾Py这个数据页的内存,存入Pz的内容 然后放到链表头部。
算法优化:
在InnoDB实现上,按照5:3的比例把整个LRU链表分成了young区域和old区域。 靠近链表头部的5/8是young区域,靠近链表尾部的3/8是old区域。
- young区域,访问跟之前LRU算法操作方式一样,但是新插入的数据也,先放在old区域。
- old区域的数据页访问需要做判断 :在链表钟超过1s,会移动到表头; 在链表不超过1s,为止不变。(时间1s:innodb_old_blocks_time 控制默认1s)
- 淘汰依然是淘汰掉链表最尾端数据页
InnoDB的影响
- 按照传统的LRU算法扫描,全表扫描如果很大的话会把当前的缓存池的数据全部淘汰,存入扫描过程中的数据也内容。那么buffer pool的内存命中率几句下降,磁盘压力增加sql响应变慢。
- 按照传统的LRU算法扫描,扫描过程中,需要新插入的数据页,都被放到old区域;且都不超过一秒,那么数据页的淘汰只会存在old区域,那么对上述的淘汰当前缓存池的数据页不会进行干预和影响也就不会增加其他额外的压力了。