前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >MySQL 内存页淘汰策略

MySQL 内存页淘汰策略

作者头像
shysh95
发布2022-04-07 19:31:44
发布2022-04-07 19:31:44
1.5K00
代码可运行
举报
文章被收录于专栏:shysh95shysh95
运行总次数:0
代码可运行

quick参数是?

MySQL客户端在连接的时候可以添加一个-quick参数,MySQL客户端在发送请求后,接收服务端返回结果的方式有两种:

  • 本地缓存:在客户端本地开出一片内存,将结果进行缓存,如果用API开发,对应的就是mysql_store_result方法
  • 不缓存:读取一个处理一个,如果用API开发,对应的就是mysql_use_result方法

MySQL客户端默认采用本地缓存的方式,如果加上-quick参数,会使用第二种不缓存的方式。

quick参数的弊端?

如果采用了quick参数,如果客户端本地处理数据很慢,将会导致服务端发送结果被阻塞,因此会让服务端变慢。

MySQL服务端如何返回数据?

  1. 服务端获取一行,写到net_buffer中,net_buffer使用的内存大小是由参数net_buffer_length定义的,默认16kb
  2. 重复获取行,直到net_buffer写满,调用网络接口发送出去
  3. 如果发送成功,就清空net_buffer,继续取下一行,并写入net_buffer
  4. 如果发送函数返回EAGIN或者WSAEWOULDBLOCK,表示本地网络栈(socket send buffer)写满了,进入等待,直到网络栈重新可写,再继续发送。
代码语言:javascript
代码运行次数:0
复制
 show global varibales like 'net_buffer_length';

socket send buffer大小默认由操作系统/proc/sys/net/core/wmem_default文件中的值指定。

MySQL客户端接收的慢,就会导致MySQL服务器由于结果发不出去,从而使该事务的执行时间变长。

如果某个Session的状态一直处于Sending to client状态,表示服务器端的网络栈写满了。(客户端使用-quick参数并且处理较慢,有可能会出现这种情况)

对于正常的线上业务,如果不是必须使用mysql_use_result这个接口,都建议使用mysql_store_result将结果缓存在客户端本地

Sending data?

Sending data并不一定是指正在发送数据,而可能是处于执行器中的任意阶段,这是因为一个查询语句的状态变化如下:

  1. MySQL查询语句进入执行阶段以后,首先把状态设置为Sending data
  2. 发送执行结果的列相关信息给客户端
  3. 继续执行语句的流程
  4. 执行完成以后,把状态设置为空字符串

Sending to client表示当前线程处于等待客户端接收结果的状态;Sending data只是表示正在执行。

全表扫描对InnoDB的影响

当我们在查询数据的时候,会从磁盘上读取数据页到内存中,如果内存中的数据页是最新的,可以直接读取内存也返回,不需要从磁盘上再次读取。

内存数据页是在Buffer Pool中管理的,Buffer Pool的两个重要作用是:

  • 加速更新
  • 加速查询

InnoDB Buffer Pool的大小由innodb_buffer_pool_size决定,一般建议设置成物理内存的60%~80%。

Buffer Pool对查询的加速效果依赖于内存命中率这个指标

代码语言:javascript
代码运行次数:0
复制
show engine innodb status\G;

从上图中可以看出,当前的命中率是97.7%,命中率越高,说明我们从内存页获取数据的次数越多。

由于现在磁盘和内存的数据量完全是一个量级,因此很容易出现页淘汰的现象。

InnoDB内存管理

InnoDB内存管理使用的是优化过后的最近最少使用(LRU)算法,该算法的核心就是用来淘汰最久未使用的数据。

InnoDB为什么要对LRU进行优化?

如果按照普通的LRU算法,假设我们一个很大的查询需要淘汰掉绝大多数的内存页,这将会导致Buffer Pool的内存命中率急速下降,磁盘压力增加,SQL语句会响应变慢。

InnoDB改进后的LRU算法

InnoDB的LRU算法使用链表实现,但在实现上,它按照5:3的比例把整个LRU链表分成了young区域和old区域,也就是说链表头部的5/8区域是young区域,链表尾部的3/8区域是old区域,LRU_old指向old区域的第一个位置。

改进后的LRU算法执行流程如下:

  1. 如果访问的数据页在young区域,那么会将该数据页移动链表头部
  2. 如果访问的数据页不在链表中,那么就会将链表尾部的数据页淘汰掉,然后将新的数据页插入到old区域开始的地方(LRU_old)
  3. 处于old区域的数据页在被访问时:如果这个数据页在LRU链表中存在的时间超过了1s,就把它移动到链表头部;如果数据页存在时间少于1s,则保持位置不变,该时间由innodb_old_blocks_time参数控制,单位是ms。
代码语言:javascript
代码运行次数:0
复制
show global variables like 'innodb_old_blocks_time';
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员修炼笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档