33-我查这么多数据,会不会把数据库内存打爆?
# 全表扫描对 server 层的影响
查询出来的结果集不是把全部的结果集都加载出来,而是有个过程的。
- 获取一行写入到 net_buffer 中,这块内存的大小是 net_buffer_length 定义的,默认为 16k。
- 循环,直到 net_buffer 写满然后通过网络发送出去。
- 发送成功后清空 net_buffer,继续读取。
- 如果返回了 EAGAIN 或 WSAEWOULDBLOCK 那么就进入等待,直到网络栈可用再继续。
也就是说 Mysql 是边读边发的,如果客户端接受的很慢,那么就容易阻塞服务端。通过 show processlist
命令查看如果是 Sending to client 这个状态就代表服务端的网络栈已经满了。
上一章说的 -q
就是会读一行处理一行,如果说读取业务很复杂,速度很慢的话,服务端会很久才会取读取下一行。所以正常业务的话建议都使用 mysql_store_result
而不是 mysql_use_result
。
注意
mysql_store_result
和 边读边发
并不冲突,两者不是一个概念。
- 边读边发面向的是 MySQL 的 server 端,首先会将数据先读取到 net_buffer 中,然后客户端来接受处理,如果客户端处理的速度很慢,net_buffer 就会慢然后进入等待状态。
- mysql_store_result 面向的是服务端,服务端接受到数据后是直接处理还是存到本地,就是看着两个参数的配置。
但是有时候通过查看 show processlist;
看到状态是 Sending data,而不是 Sending to client。
- Sending data 状态通常表示 MySQL 服务器正在从存储引擎读取数据并发送给客户端。这个状态通常出现在执行查询操作时,比如 SELECT 语句。这个状态的持续时间取决于查询的复杂性和数据的大小。
- Sending to client” 状态则表示 MySQL 服务器正在将查询结果发送给客户端。如果这个状态持续时间过长,可能是因为网络延迟,或者客户端处理数据的速度较慢。
也就是说 Sending data 指的是服务端正在执行的查询的处理,而 Sending to client 指的是正在发送到客户端。
# 全表扫描对 InnoDB 的影响
InnoDB 通过 Buffer Pool 来保存更新和新增的结果,这块区域是从内存中分出来的,可以加速扫描的效率。一旦请求过来查询如果在 Buffer Pool 中没有从硬盘获取,如果有就直接从内存返回了。这里面存储的更新和新增结果总是最新的。
但是 Buffer Pool 对于查询效果的加速有个依赖指标:内存命中率。
show engine innodb status
通过这个命令中的 Buffer pool hit rate 字段可以看出来,一般情况下一个稳定的服务要达到 99%,而 100% 是很难达到的。
InnoDB Buffer Pool 的大小是由参数 innodb_buffer_pool_size 确定的,这个值一般设置为物理内存的 6-8 成。
InnoDB 的 LRU (最近最少使用 Least Recently Used, LRU) 算法。
笔记
这个算法的实现就是淘汰最久未使用的数据,它通过维护一个链表,每当访问了某个数据时,就将这个数据加入到链表的头部,如果数据本身存在于链表中,那么就将数据从链表的中间移动到链表的头部,这样最终下来,在链表尾部的数据一定就是最久未被使用的数据了,因此可以将其淘汰。
但是 InnoDB 对这个算法进行了修改,分成了两个部分 young
(从链头开始) 和 old
(从链尾开始)。
- 如果访问的元素在
young
区域,就和 LRU 的默认算法一致。 - 新插入元素放在
old
区域中。 old
数据页中的数据每次被访问会有两个判断- 被访问的元素在
old
中存在时间是否超过了时间间隔,如果是就放在链表头部。 - 如果没有超过时间间隔,就保持不变。
- 时间间隔是由 innodb_old_blocks_time 参数控制的,单位为妙。
- 被访问的元素在
# 上一章答案
如果一个事务被 kill 之后,持续处于回滚状态,从恢复速度的角度看,你是应该重启等它执行结束,还是应该强行重启整个 MySQL 进程?
- 这个问题需要考虑的是如果没有影响到系统的使用的话,比如在一个低峰期,还是等他自己完成回滚为好,毕竟重启服务可能会造成数据不一致。
- 如果说影响到了其他业务的使用,需要做主备切换,让新的主库先提供服务,然后等他自己完成回滚。
总的来说强制重启是一个下下策。