mysql 一次性 select 一千万条会导致oom吗?

假设表里有 1千万条数据,使用 gorm 去获取全部数据,执行如下代码

rows, err := gormDb.Raw("select * from xxx_table").Rows()
if err != nil {
    panic(err)
}
for rows.Next() {

}

问题一:
执行 gormDb.Raw("select * from xxx_table").Rows(),会把数据库所有数据加载到内存吗?经过跟踪源码好像是执行 rows.Next 时才会从 tcp 链接中读取一条数据,如果是这样的 go 服务应该不会有 oom 的风险,但是我不太理解这个读取结果是存到了什么地方,是 go 服务 net.Read 的时候 mysql 才会向 tcp 发结果数据吗?(流式处理)

问题二:
一次性查那么多条数据,mysql 服务是怎么处理的,是一次性把一千万条数据加载到 mysql 服务器的内存里吗?这样 mysql 服务器会有 oom 的风险吧?

还请各位大大们解答。

最佳答案

有几点可以明确: 1:select语句不一定会让mysql加载数据到内存(前提是命中索引),而是在发送数据的时候一条一条的读取数据。 2:查询如果是有条件的,发送时候怎么知道读取哪些数据呢?可以理解为,mysql查询期间保存了要返回的数据地址列表,发送期间,读取地址对应的数据,然后发送。(1000万的数据地址,确实也不大) 3:客户端可以选择一次性读取,也可以一条一条读(Rows方式),一条一条读取也不会oom。但是如果客户端处理慢的话(比如处理1000万条需要1天时间),会让mysql端发送数据处理阻塞状态(sending data) 这篇文章可以参考:smartkeyerror.com/MySQL-Sending-Da...

3年前 评论
讨论数量: 7

看你MySQL版本以及配置,默认是fetchAll,也就是加载到内存资源里面,rows.Next只是在资源里面获取下一条,stream是采用游标的方式,流式读取!

3年前 评论

@Imuyu 也就说第一个问题,数据是在 mysql 内存里, rows.Next 是每次从 tcp 链接里边读取到了一条数据而已,不会导致 golang oom

第二个问题,如果 mysql 内存不够,会导致 mysql oom。

3年前 评论
canaan_wang 3年前

@chaofei 数据在硬盘里面,你select all会加载到内存里面返回到客户端资源句柄,rows.Next是操作的资源句柄,你可以测试一下,监控内存!

3年前 评论

@Imuyu 我测试了,使用 gorm 去 select all, 使用 top -p 查看 mysql 内存变化,发现并没有什么大的变化,很疑惑

3年前 评论

当然没有明显变化,千万条数据,加载内存里面也不过几十M或者几百M,问题在于并发,成千上百的请求,成千上百的select all带来的内存飙升,你使用协程多跑几个!

3年前 评论
canaan_wang 3年前
canaan_wang 3年前

有几点可以明确: 1:select语句不一定会让mysql加载数据到内存(前提是命中索引),而是在发送数据的时候一条一条的读取数据。 2:查询如果是有条件的,发送时候怎么知道读取哪些数据呢?可以理解为,mysql查询期间保存了要返回的数据地址列表,发送期间,读取地址对应的数据,然后发送。(1000万的数据地址,确实也不大) 3:客户端可以选择一次性读取,也可以一条一条读(Rows方式),一条一条读取也不会oom。但是如果客户端处理慢的话(比如处理1000万条需要1天时间),会让mysql端发送数据处理阻塞状态(sending data) 这篇文章可以参考:smartkeyerror.com/MySQL-Sending-Da...

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!