Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL查询数据

MySQL查询2000w超大数据的解决方法

作者:Hui  Baby

这篇文章主要为大家详细介绍了MySQL查询2000w超大数据的解决方法,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以了解下

一、核心前提:MySQL 原生是否支持「流式查询」?

MySQL 本身无服务器端流式查询能力,仅客户端可通过配置实现「流式读取」,二者的核心区别直接决定了 2000 万条数据查询的异常表现:

表格

维度服务器端客户端(如 mysql 命令行 / JDBC)
流式能力无原生流式查询,必须先生成完整结果集(加载到内存 / 临时磁盘),再向客户端推送可通过显式配置开启「流式读取」(逐行接收 / 处理),默认是「缓冲读取」(一次性接收全量数据)
数据处理逻辑无论客户端如何配置,服务器都会先完成所有计算(排序 / 聚合 / 全表扫描),生成完整结果集流式读取:逐行接收、处理后释放内存;缓冲读取:一次性接收全量结果集并缓存

二、2000 万条数据查询的异常表现(是否撑爆 / 触发 OOM?)

1. 服务器端:极少直接 OOM,核心问题是「IO 假死 / 超时 / 磁盘飙满」

2. 客户端:未开流式读取必触发 OOM,开流式则无风险

三、分维度解决方案(针对性解决服务器 / 客户端问题)

1. 服务器端:解决 IO 假死 / 超时(唯一根本方案:拆分查询)

核心逻辑:将 2000 万条数据拆分为小批次(如每批 1 万条),让服务器每次仅处理少量数据,避免全量数据触发大量磁盘 IO。

# Shell脚本分批次查询(按自增主键拆分,通用最优方案)
#!/bin/bash
# 配置MySQL连接信息
MYSQL_USER="root"
MYSQL_PWD="你的密码"
MYSQL_DB="目标数据库"
TABLE_NAME="目标表"
RESULT_FILE="query_result.txt"

# 配置分批参数:每批1万条,总数据2000万条
start_id=1
batch_size=10000
total_data=20000000

# 循环分批次查询
while [ $start_id -le $total_data ]; do
  end_id=$((start_id + batch_size - 1))
  # 小批次查询:服务器仅加载1万条数据,无大量临时磁盘IO
  mysql -u${MYSQL_USER} -p${MYSQL_PWD} -D${MYSQL_DB} -N -s -e \
    "SELECT * FROM ${TABLE_NAME} WHERE id BETWEEN ${start_id} AND ${end_id};" >> ${RESULT_FILE}
  
  # 打印进度(可选)
  echo "已查询:id ${start_id} ~ ${end_id},进度:$((start_id*100/total_data))%"
  
  # 更新起始ID,休眠0.2秒降低服务器压力
  start_id=$((end_id + 1))
  sleep 0.2
done

echo "2000万条数据查询完成,结果已写入${RESULT_FILE}"

适配无连续主键场景:若无自增 ID,可按时间字段拆分(如按创建时间分天 / 小时查询):

-- 示例:按create_time分批次查询(每批1小时数据)
SELECT * FROM big_table WHERE create_time BETWEEN '2026-01-01 00:00:00' AND '2026-01-01 01:00:00';

2. 客户端:解决 OOM(强制开启流式读取)

不同客户端的流式读取配置方式:

表格

客户端类型流式读取配置方法
mysql 命令行默认开启无缓冲查询,无需额外配置(天然不会 OOM)
JDBC(Java)禁用缓冲,设置流式读取参数:java<br>Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);<br>stmt.setFetchSize(Integer.MIN_VALUE); // 核心参数<br>ResultSet rs = stmt.executeQuery("SELECT * FROM big_table");<br>while (rs.next()) { 逐行处理数据 }<br>
Navicat/DBeaver开启「分批读取」:Navicat→工具→选项→高级→设置「每次获取行数 = 1000」;DBeaver→连接配置→结果集→「分批获取行数」

3. 服务器端兜底优化(缓解 IO 压力,非根本方案)

调整 MySQL 配置参数,减少临时磁盘 IO 开销,避免服务器假死加剧:

# my.cnf / my.ini 配置
[mysqld]
# 临时文件目录指向高速磁盘(如SSD),降低IO耗时
tmpdir = /data/mysql_tmp 
# 内存临时表上限,超出则写入磁盘(不宜过大,避免占用过多内存)
tmp_table_size = 64M     
max_heap_table_size = 64M
# 排序/读取缓冲区大小,避免单查询占用过多内存
sort_buffer_size = 1M    
read_buffer_size = 1M    
# 关闭不必要的预读,减少磁盘IO
innodb_read_ahead_threshold = 0
# 限制单查询的并行度,避免抢占资源
innodb_thread_concurrency = 8

四、关键注意事项(避坑点)

五、最终核心结论

异常本质

核心解决方案

关键原则:MySQL 不适合一次性处理超大结果集,必须通过「分批次查询 + 客户端流式读取」组合方案,才能避免服务器假死和客户端 OOM。

到此这篇关于MySQL查询2000w超大数据的解决方法的文章就介绍到这了,更多相关MySQL查询数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文