在线MySQL直选千万100G数据,服务器会破解吗?假设对一个100G的表t进行全表扫描,扫描结果保存在客户端:#该语句没有任何判断条件,所以全表扫描,找到的每一行都可以直接放入结果集中,然后返回给客户端mysql-h$host-P$port-u$user-p$pwd-e"select*fromt">$target_file1这个“结果集”存在哪里?其实MySQL读取和发送数据的过程是这样的:获取一行,写入net_buffer。内存大小由参数net_buffer_length定义,默认为16k,继续取行直到net_buffer满,发送出去!如果传输成功,清空net_buffer,继续读取下一行,写入net_buffer。如果发送返回EAGAIN或WSAEWOULDBLOCK,说明本地网络栈(socket发送缓冲区)已满,进入等待。继续发送,直到网络栈再次可写,再继续发送。上述过程的执行流程图如下:可以看出一个query在发送过程中:MySQL占用的内存最大为net_buffer_length,根本达不到100G。同样,无法到达套接字发送缓冲区。如果套接字发送缓冲区已满,读取数据将被暂停。因此,MySQL边读边发送。如果客户端接收速度慢,会导致MySQLServer无法发送结果,事务的执行时间会变得很长。经过分析,我们现在知道查询结果是分段发送给客户端的,所以即使查询返回大量数据,扫描全表也不会填满内存。以上就是server层的处理逻辑。InnoDB引擎层是如何处理的呢?2InnoDB如何处理全表扫描?内存中的数据页在BufferPool(以下简称BP)中进行管理,可以加快查询速度。由于WAL机制,当事务提交时,磁盘上的数据页是旧的。如果有立即读取数据页的查询请求,重做日志是否应该立即应用于数据页?不!因为这个时候内存数据页的结果是最新的,可以直接读取内存页,所以速度很快,BufferPool加快了这里的查询速度。但实际上,BP对查询的加速效果取决于内存命中率。可以使用如下命令查看当前BP命中率:showengineinnodbstatus对于服务一般稳定的在线系统,为保证响应性能,内存命中率必须在99%以上。InnoDBBufferPool的大小由参数innodb_buffer_pool_size决定,建议设置为可用物理内存的60%~80%。3InnoDB内存管理使用最近最少使用(LRU)算法来剔除最长未使用的数据。如果此时我们进行全表扫描呢?如果我们要扫描一个200G的表,而这个表是一个历史数据表,通常没有业务访问它。按照该算法进行扫描,会将当前BP中的所有数据剔除,存入扫描过程中访问的数据页的内容中。即历史数据表数据主要存储在BP中。对于做商业服务的图书馆来说,这是不能接受的。你会看到BP内存命中率急剧下降,磁盘压力增大,SQL语句响应变慢。因此,InnoDB不能直接使用原生的LRU。LRUInnoDB改进版将链表按照5:3分为New区和Old区。改进版的LRU执行流程:首先,在New区访问D1,像普通LRU一样将其移动到链头。然后,访问一个新的不存在的,此时,当前链表的数据页仍然从链尾的数据页P中剔除,但是新插入的数据页DX放在了旧的地方和数据页在老区。每次访问都要判断:如果数据页如果数据页在LRU链表中存在时间超过1s,则移到链表头部。如果数据页在LRU链表中存在的时间小于1s,则该位置会保持1s不变,由参数innodb_old_blocks_time控制。这个改进是专门为处理类似的全表扫描而设计的。手术。仍然扫描几百G的历史数据表:在扫描过程中,新插入的数据页放在旧区。一个数据页中有很多条记录,这个数据页第一次访问和最后一次访问的时间间隔不会超过1s,所以还是保留在old区,继续扫描后续数据。之前的数据页不会再被访问,所以一直没有机会移动到链表的头部(Newarea),很快就会被淘汰。可以看出,这种策略最大的好处是在扫描大表时,虽然也使用了BP,但是对young区没有影响,从而保证了bufferPool响应正常业务的查询命中率。参考文献:[1]。https://cloud.tencent.com/developer/article/1767570[2]。https://juejin.cn/post/6854573221258199048[3].https://time.geekbang.org/column/article/79407