Code前端首页关于Code前端联系我们

当数据请求过多时,MySQL OOM 服务器会崩溃吗? InnoDB如何处理全表扫描?

terry 2年前 (2023-09-26) 阅读数 53 #数据库

在线MySQL直接选择千万级100G数据,服务器会崩溃吗?

假设对100G表t进行全表扫描,扫描结果保存到客户端:

 # 该语句无任何判断条件,所以全表扫描,查到的每行都可直接放到结果集,然后返给客户端
 mysql -h$host -P$port -u$user -p$pwd -e
 "select * from t" > $target_file

1 那么这个“结果集”在哪里?

其实就是读取发送的过程数据到 MySQL 如下:

  1. 获取行并写入 net_buffer 内存大小由 net_buffer_length 决定,默认为 16k

    MySQL数据查询太多会OOM服务器裂开吗?InnoDB如何处理全表扫描?

  2. 不断获取行,直到 net_buffer 满后发送
  3. 如果发送成功,则清空 net_buffer,继续读取下一行,写入net_buffer
  4. 如果发送返回EAGAIN或WSAEWOULDBLOCK,则说明本地网络堆栈(socket send buffer)已满,进入等待,直到网络堆栈可重写,继续发送

上述流程执行流程图如下:

MySQL数据查询太多会OOM服务器裂开吗?InnoDB如何处理全表扫描?

可以看出,在发送请求的过程中:MySQL占用的最大内存为net_buffer_length,根本不可能是100G,同样,socket send buffer也无法接收。如果socket发送buffer已满,则停止读取数据。

所以MySQL同时读取和发送。如果客户端接收缓慢,MySQL Server 将无法发送结果,事务执行时间会变得很长。

经过分析,现在我们知道查询结果是分段发送给客户端的,所以通过扫描全表,即使查询返回大量数据,内存也不会被占满。

以上是server层处理逻辑。 InnoDB引擎层如何处理这个问题?

MySQL数据查询太多会OOM服务器裂开吗?InnoDB如何处理全表扫描?

2 InnoDB如何处理全表扫描?

内存中的数据页在缓冲池(以下简称BP)中进行管理,BP可以加快查询速度。由于WAL机制,事务执行时磁盘上的数据表是旧的。如果有立即请求读取数据表,是否立即redo lo? g应用于数据表?不!因为目前内存数据页的结果是最新的。直接读取内存页所以速度很快。缓冲池在这里加快了查询速度。

但实际上BP对查询的加速效果取决于内存命中率。可以使用以下命令查看当前BP命中率

show engine innodb status

一般情况下,对于业务稳定的网络系统,内存命中率必须在99%以上才能保证响应性能。

InnoDB 缓冲池 的大小由参数 innodb_buffer_pool_size 确定。建议设置为可用物理内存的60%~80%。

3 InnoDB 内存管理

使用最近最少使用(LRU)算法删除最长时间未使用的数据。如果这个时候我们进行全表扫描会怎么样呢?你想扫描200G的表,这是一个历史数据表,一般情况下任何公司都无法访问它。使用该算法进行扫描会丢弃当前BP中的所有数据,并保存扫描过程中访问的数据页的内容。这意味着历史数据表中的数据主要存储在BP中。

这对于提供商业服务的图书馆来说是不可能的。可以看到BP内存命中率急剧下降,磁盘压力增大,SQL语句响应变慢。因此,InnoDB原生的LRU不能直接使用。

LRU改进版

MySQL数据查询太多会OOM服务器裂开吗?InnoDB如何处理全表扫描?

InnoDB按照5:3将链表划分为新旧区域。改进版的LRU填充流程:

  • 首先在D1区域打开New,和普通LRU一样,将其移动到链的前面
  • 然后打开一个区域中不存在的新数据表。当前链表。此时,链尾的数据表P仍然被淘汰,但新插入的数据表DX被放在旧的数据表上
  • 每次发现旧的区域数据表时,必须对其进行评估:
    • 如果数据表在 LRE 链表中>1s,则将其移动到链表标题处
    • 当数据表在 PRE 链表中时 在 PRE 链表中停留时间

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门