对一个数据量为200G的表做全表扫描,会把内存打爆吗?

不会,MySQL是边读边发送,查询结果是分段发送给客户端的。
1)获取一行,写到net_buffer中。这块net_buffer内存的大小由参数net_buffer_length定义,默认16K。
2)重复获取行,知道把net_buffer写满,调用网络接口发出去。
3)发送成功则清空net_buffer,继续获取下一行并写入net_buffer。
4)如果发送函数返回Eagain或WSAEWOULDBLOCK,表示本地网络栈socket send buffer写满了,进入等待。直到网络栈重新可写,再继续发送。(导致socket send buffer写满的原因,可能是客户端没有去读取socket receive buffer中内容,或者客户端读取的慢)
从中可以看出一个查询再发送过程中占用的MySQL内存最大就是net_buffer_length的大小。socket send buffer如果写满,就会暂停MySQL读取,通过show processlist,状态state处于Sending to clinet,就代表网络栈写满了。
image.png

全表扫描对InnoDB内存的影响

内存的数据页是放在BufferPool中管理的,如果查询的结果存在于数据页中,就会直接返回结果,不再读盘,加快了查询速度。所以,BufferPool的指标‘内存命中率’,是MySQL中对查询的加速效果很重要的指标。
通过show engine innodb status,可以查看Buffer pool hit rate的命中率。
InnoDB BufferPool的大小由参数innodb_buffer_pool_size控制,一般设置成物理内存的60%~80%。
InnoDB的内存管理使用的是改良的LRU算法。
image.png
在 InnoDB 实现上,按照 5:3 的比例把整个 LRU 链表分成了 young 区域和 old 区域。图中 LRU_old 指向的就是 old 区域的第一个位置,是整个链表的 5/8 处。也就是说,靠近链表头部的 5/8 是 young 区域,靠近链表尾部的 3/8 是 old 区域。
改进后的 LRU 算法执行流程变成了下面这样。
1)图 7 中状态 1,要访问数据页 P3,由于 P3 在 young 区域,因此和优化前的 LRU 算法一样,将其移到链表头部,变成状态 2。
2)之后要访问一个新的不存在于当前链表的数据页,这时候依然是淘汰掉数据页 Pm,但是新插入的数据页 Px,是放在 LRU_old 处。
3)处于 old 区域的数据页,每次被访问的时候都要做下面这个判断:
若这个数据页在 LRU 链表中存在的时间超过了 1 秒,就把它移动到链表头部;
如果这个数据页在 LRU 链表中存在的时间短于 1 秒,位置保持不变。1 秒这个时间,是由参数 innodb_old_blocks_time 控制的。其默认值是 1000,单位毫秒。
可以看到,这个策略最大的收益,就是在扫描这个大表的过程中,虽然也用到了 Buffer Pool,但是对 young 区域完全没有影响,从而保证了 Buffer Pool 响应正常业务的查询命中率。这个策略,就是为了处理类似全表扫描的操作量身定制的。