34GB超大文件去重及MySQL高效导入方案咨询
嘿,针对你34GB大文件去重后导入MySQL遇到的内存和耗时问题,我整理了几个实战性强的方案,你可以根据自己的服务器情况选:
方案1:用sort分块排序+去重(解决内存溢出问题)
你之前用sort+awk内存爆了,大概率是没用到sort的分块参数。sort本身支持把超出内存的数据写到临时磁盘文件,只要指定好临时目录和内存缓冲区大小就行:
sort --buffer-size=2G --temporary-dir=/data/large_tmp -u your_large_file.txt > deduplicated_file.txt
--buffer-size=2G:给sort分配2GB内存用来处理排序,根据你的服务器内存调整(比如4GB内存的话可以设3G)--temporary-dir:指定一个磁盘空间充足的目录(比如挂载了大容量硬盘的路径),用来存临时排序文件-u:直接在排序过程中去重,省掉后续awk的步骤,效率更高
这个命令会自动把大文件拆成小块排序,再合并去重,内存占用完全可控,不会出现溢出问题。
方案2:分块处理+合并去重(适合内存极小的服务器)
如果你的服务器内存特别紧张(比如只有1GB以内),可以先把大文件拆成小份,逐个处理后再合并:
- 拆分文件:按2GB每份拆分,拆分后的文件会以
part_aa、part_ab命名
split -b 2G your_large_file.txt /data/tmp/part_
- 逐个分块排序去重:
for file in /data/tmp/part_*; do sort -u "$file" > "$file"_dedup rm "$file" # 处理完就删原分块,省空间 done
- 合并所有已去重的分块(因为每个分块已经有序,用
-m参数快速合并,不用重新排序):
sort -m -u /data/tmp/part_*_dedup > final_dedup.txt
整个过程每个步骤只处理几十到几百MB的文件,内存压力极小。
方案3:MySQL端先无约束导入再去重(最快的导入方式)
带约束导入慢的核心原因是MySQL每插入一行都要检查主键、唯一索引,这对大文件来说开销极大。你可以换个思路:先导入无约束的临时表,再在数据库内去重后写入正式表:
- 创建临时表(复制正式表结构但去掉所有约束):
CREATE TABLE temp_import LIKE your_target_table; -- 删掉主键、唯一索引等约束 ALTER TABLE temp_import DROP PRIMARY KEY, DROP INDEX idx_unique_column; -- 替换成你的唯一索引名
- 用
LOAD DATA INFILE快速导入(这是MySQL最快的导入方式,比INSERT快几十倍):
LOAD DATA INFILE '/path/to/your_large_file.txt' INTO TABLE temp_import FIELDS TERMINATED BY ',' -- 按你的文件分隔符调整,比如\t是制表符 ENCLOSED BY '"' -- 如果字段有引号包裹就加,没有就去掉 LINES TERMINATED BY '\n';
- 去重并写入正式表:
如果是要完全去重,用DISTINCT:
INSERT INTO your_target_table SELECT DISTINCT * FROM temp_import;
如果是按某个主键/唯一列去重(保留最早/最新的行),可以用分组:
INSERT INTO your_target_table SELECT t.* FROM temp_import t JOIN ( SELECT unique_column, MIN(id) AS keep_id -- 用MIN或MAX选要保留的行 FROM temp_import GROUP BY unique_column ) AS t2 ON t.id = t2.keep_id;
- 清理临时表:
DROP TABLE temp_import;
这个方案的优势是把去重工作交给MySQL,不用在外部折腾大文件,导入速度会快很多。
方案4:用awk流式哈希分块去重
如果你还是想用awk,可以通过哈希把文件分流到多个小文件,再逐个去重,避免内存溢出:
BEGIN { split_count = 1000; # 分成1000个小文件,可根据需要调整 } { # 按行号哈希分流,也可以换成某个字段比如$1来哈希 hash_key = NR % split_count; output_file = "/data/tmp/hash_" hash_key ".txt"; print > output_file; } END { # 关闭所有打开的文件句柄 for (i=0; i<split_count; i++) { close("/data/tmp/hash_" i ".txt"); } }
运行命令:
awk -f split_by_hash.awk your_large_file.txt
然后每个哈希小文件单独用sort -u处理,最后合并即可,原理和方案2类似,但分流逻辑更灵活。
内容的提问来源于stack exchange,提问作者Türkan Arıt
相关产品推荐
相关产品推荐

