我查这么多数据,会不会把内存打爆?

2022/3/7 7:20:23

本文主要是介绍我查这么多数据,会不会把内存打爆?,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

全表扫描对server层的影响

mysql -h$host -P$port -u$user -p$pwd -e "select * from db1.t" > $target_file

innodb的数据保存在主键索引上,所以全表扫描就是直接扫描表t的主键索引。然后查询的每一行可以直接放到结果集里,然后返回给客户端。这个结果集存放在哪里呢?
实际上,服务器并不需要保存一个完整的结果集。取数据和发数据的流程是这样的:
1.获取一行写入到net_buffer中。
2.重复获取行,直到把net_buffer写满,调用网络接口把数据发出去。
3.如果发送成功,就清空net_buffer,然后继续读取下一行,直到把net_buffer写满。
4.如果发送函数返回eagain或者wsaewouldblock,就表示本地网络栈被写满了,进入等待状态,直到网络栈重新可写,再继续发送。

mysql是“边读边发”的,这就意味着如果客户端接收的慢就会导致由于Mysql发不出去,这个事务的执行时间会变长。

比如下面的这个状态就是我故意让客户端不去读socket receive buffer中的内容,然后在服务器端show processlist看到的结果。
在这里插入图片描述
如果你看到State的值一直处于“Sending to client”,就表示服务器端的网络栈被写满了。

mysql_use_result这个方法是读取一行处理一行。你可以想象假设有一个业务逻辑比较复杂,每读取一行数据以后,要处理的逻辑很慢就会导致客户端要很久才会去取下一行数据。
因此,对于正常的线上业务来说,如果一个查询的返回结果不会很多的话,我都建议你使用mysql_store_result这个接口,直接把查询结果保存到本地内存。

有时候我们会看到一个类似的状态“sending data”,它是指处于执行器过程中的任意阶段。
实际上,一个查询语句的状态变化是这样的:
1.mysql查询语句进入执行阶段后,会把状态设置为“sending data”
2.然后,发送执行结果的列相关信息给客户端;
3.再继续执行语句流程
4.执行完成后,把状态设置为空字符串。

全表扫描对Innodb的影响

wal机制的一个作用是保存更新的结果,在配合redo log就避免了随机写盘。
内存的数据页是在Buffer Pool中管理的,在wal中Buffer Pool起到加速更新的作用。而实际上Buffer还有另外一个作用就是加速查询。因为由于有wal机制的存在,所以磁盘上的数据页实际上是旧的,那如果马上有查询来读取这个数据页,是不是要把redo log应用到这个数据页上呢?
不是的,因为这时候内存中的数据页是最新的,直接读取内存页就可以了。所以说Buffer Pool还有加速查询的作用。

Buffer Pool对查询的加速依赖于一个重要的指标:内存命中率,通过show engine innodb status查看。

由于innodb_buffer_pool_size的大小远小于磁盘的数据量。如果一个buffer pool满了,但又要从磁盘读取一个数据页的话,那肯定要淘汰一个旧数据页的。这里采用的是LRU算法。
这个算法乍一看没有任何问题,但是如果要做一个全表扫描,会不会有问题呢?
假设按照这个算法,我们要访问一个200G的表,要访问一个历史数据表,这个表平时没有业务访问它。那么,按照这个算法扫描的话,就会把当前buffer pool里的数据全部清除掉,存入扫描过程中访问到的数据页的内容,也就是说Buffer Pool里面主要存放的是这个历史数据表的数据。对于一个正在做业务服务的库,这可不妙。你会看到,Buffer Pool的内存命中率急剧下降,磁盘压力增加,SQL语句响应变慢。

所以innodb对这个算法做了改进,将链表分为young区和old区:在这里插入图片描述



这篇关于我查这么多数据,会不会把内存打爆?的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程