阅读本文大概需要 7 分钟。
mysql -h$host -P$port -u$user -p$pwd -e "select * from db1.t" > $target_file
获取一行,写到net_buffer。这块内存的大小是由参数net_buffer_length定义,默认16k
重复获取行,直到net_buffer写满,调用网络接口发出去
若发送成功,就清空net_buffer,然后继续取下一行,并写入net_buffer
若发送函数返回EAGAIN或WSAEWOULDBLOCK,就表示本地网络栈(socket send buffer)写满了,进入等待。直到网络栈重新可写,再继续发送
查询结果发送流程
一个查询在发送过程中,占用的MySQL内部的内存最大就是net_buffer_length这么大,不会达到200G
socket send buffer 也不可能达到200G(默认定义/proc/sys/net/core/wmem_default),若socket send buffer被写满,就会暂停读数据的流程
服务端发送阻塞
MySQL查询语句进入执行阶段后,先把状态设置成 Sending data
然后,发送执行结果的列相关的信息(meta data) 给客户端
再继续执行语句的流程
执行完成后,把状态设置成空字符串。
Sending data状态
仅当一个线程处于“等待客户端接收结果”的状态,才会显示"Sending to client"
若显示成“Sending data”,它的意思只是“正在执行”
由于WAL,当事务提交时,磁盘上的数据页是旧的,若这时马上有个查询来读该数据页,是不是要马上把redo log应用到数据页?不需要。因为此时,内存数据页的结果是最新的,直接读内存页即可。这时查询无需读磁盘,直接从内存取结果,速度很快。所以,Buffer Pool能加速查询。
基本LRU算法
TODO
state1,链表头部是P1,表示P1是最近刚被访问过的数据页
此时,一个读请求访问P3,因此变成状态2,P3被移到最前
状态3表示,这次访问的数据页不存在于链表,所以需要在BP中新申请一个数据页Px,加到链表头。但由于内存已满,不能申请新内存。于是清空链表末尾Pm数据页内存,存入Px的内容,放到链表头部
改进的LRU算法
状态1,要访问P3,由于P3在New区,和优化前LRU一样,将其移到链表头部 =》状态2
之后要访问一个新的不存在于当前链表的数据页,这时依然是淘汰掉数据页Pm,但新插入的数据页Px,是放在LRU_old处
处于old区的数据页,每次被访问的时候都要做如下判断:
若该数据页在LRU链表中存在的时间超过1s,就把它移动到链表头部
若该数据页在LRU链表中存在的时间短于1s,位置保持不变。1s是由参数innodb_old_blocks_time控制,默认值1000,单位ms。
该策略,就是为了处理类似全表扫描的操作量身定制。还是扫描200G历史数据表:
推荐阅读:
内容包含Java基础、JavaWeb、MySQL性能优化、JVM、锁、百万并发、消息队列、高性能缓存、反射、Spring全家桶原理、微服务、Zookeeper、数据结构、限流熔断降级......等技术栈!
⬇戳阅读原文领取! 朕已阅