Unix Shell脚本对比顺序不同的大文件,含ETL(Informatica)方案咨询
嘿,我来帮你搞定这个大文件比对的难题——500万条记录、3.5G的文件,还碰到地址顺序乱序+服务器空间不够的问题,确实挺头疼的。下面给你梳理几个可行的方案,不管是用Unix脚本还是Informatica都能解决:
一、Unix脚本方案:绕开全量排序的空间瓶颈
直接用sort因为空间不够失败很正常,咱们换个思路:先把每条代理记录的地址标准化(不管原顺序如何,把地址字段排序后拼接成统一格式),再做比对,这样既解决了地址乱序的问题,又能减少临时文件的压力。
1. 先写个标准化脚本(awk实现)
首先用awk把每条代理的地址字段提取出来排序,生成格式统一的记录。假设每条代理的结构是姓名+出生日期+性别+若干地址字段,可以写这样的awk脚本(保存为normalize.awk):
# 把地址字段排序后拼接成标准化字符串 function normalize_addr(addr_str) { split(addr_str, addrs, " ") asort(addrs) normalized = "" for (i=1; i<=length(addrs); i++) { normalized = normalized " " addrs[i] } return substr(normalized, 2) # 去掉开头的空格 } BEGIN { FS = " " current_pos = 2 # 跳过开头的sysdate字段 } { # 遍历所有代理记录,最后一个字段是总记录数,跳过 while (current_pos <= NF-1) { # 提取代理的核心标识(姓名+出生日期+性别) name = $current_pos " " $(current_pos+1) dob = $(current_pos+2) gender = $(current_pos+3) # 提取所有地址字段,直到下一个代理的姓名(假设姓名首字母大写,可根据实际调整判断逻辑) addr_start = current_pos + 4 addr_end = addr_start while (addr_end <= NF-1 && $(addr_end) !~ /^[A-Z][a-z]+$/) { addr_end++ } addr_end-- # 拼接地址字符串并标准化 addr_raw = "" for (k=addr_start; k<=addr_end; k++) { addr_raw = addr_raw " " $k } addr_norm = normalize_addr(addr_raw) # 输出标准化后的记录,用|分隔字段方便后续处理 print name "|" dob "|" gender "|" addr_norm current_pos = addr_end + 1 # 跳到下一个代理的起始位置 } }
2. 生成标准化文件并比对
用这个脚本处理两个原始文件,生成标准化后的临时文件(如果/tmp空间不够,就指定到其他大磁盘路径):
awk -f normalize.awk file1.txt > /large-disk/tmp/file1_normalized.txt awk -f normalize.awk file2.txt > /large-disk/tmp/file2_normalized.txt
接下来用sort的临时目录参数指定大空间路径,去重后用comm比对:
# 去重并排序,指定临时目录到空间充足的位置 sort -u /large-disk/tmp/file1_normalized.txt --temporary-directory=/large-disk/tmp -o /large-disk/tmp/file1_sorted.txt sort -u /large-disk/tmp/file2_normalized.txt --temporary-directory=/large-disk/tmp -o /large-disk/tmp/file2_sorted.txt # 提取仅在file1存在的记录 comm -23 /large-disk/tmp/file1_sorted.txt /large-disk/tmp/file2_sorted.txt > only_in_file1.txt # 提取仅在file2存在的记录 comm -13 /large-disk/tmp/file1_sorted.txt /large-disk/tmp/file2_sorted.txt > only_in_file2.txt # 提取核心标识相同但地址不匹配的记录 grep -Ff <(comm -12 /large-disk/tmp/file1_sorted.txt /large-disk/tmp/file2_sorted.txt | cut -d"|" -f1-3) /large-disk/tmp/file1_normalized.txt > temp1.txt grep -Ff <(comm -12 /large-disk/tmp/file1_sorted.txt /large-disk/tmp/file2_sorted.txt | cut -d"|" -f1-3) /large-disk/tmp/file2_normalized.txt > temp2.txt diff temp1.txt temp2.txt > mismatched_records.txt
3. 超省空间的哈希比对法(可选)
如果标准化后的文件还是太大,可以给每条记录生成哈希值,对比哈希再回查原记录:
# 生成标准化记录的哈希文件 awk -f normalize.awk file1.txt | xargs -I {} echo {} | md5sum | awk '{print $2 " " $1}' > file1_hashes.txt awk -f normalize.awk file2.txt | xargs -I {} echo {} | md5sum | awk '{print $2 " " $1}' > file2_hashes.txt # 排序哈希后比对,再回查原记录 sort file1_hashes.txt -o file1_hashes_sorted.txt sort file2_hashes.txt -o file2_hashes_sorted.txt comm -23 file1_hashes_sorted.txt file2_hashes_sorted.txt | awk '{print $1}' | grep -Ff- /large-disk/tmp/file1_normalized.txt > only_in_file1.txt
二、Informatica ETL方案:利用集群资源并行处理
如果公司有Informatica环境,这个场景用ETL工具会更省心,还能利用集群的并行能力:
- 源处理:定义两个文件源,用自定义转换拆分每条代理记录(类似awk的逻辑,把连续的记录拆成单条代理数据)。
- 标准化转换:在
Expression Transformation里,把每个代理的地址字段转成数组,用SORT_ARRAY函数排序后拼接成标准化字符串,解决地址乱序问题。 - 分区优化:对两个数据集按代理核心标识(姓名+出生日期)做哈希分区,让相同代理的数据分到同一个节点处理,减少内存压力。
- 比对逻辑:
- 用
Left Outer Join连接两个数据集,筛选右表字段为空的记录,就是仅在第一个文件存在的记录。 - 用
Right Outer Join筛选左表字段为空的记录,就是仅在第二个文件存在的记录。 - 用
Inner Join连接后,对比标准化地址字段,筛选地址不同的记录,就是不匹配的记录。
- 用
- 结果输出:把三类结果分别输出到指定文件或数据库表中。
这个方案的优势是不需要担心单台服务器的空间/内存限制,Informatica会自动调度集群资源并行处理,可视化的流程也更容易维护。
三、极端空间不足的应急方案:分块处理
如果服务器空间实在紧张,可以用split把大文件拆成小份,每份单独处理后再合并结果:
# 把file1拆成每份10万行的小文件 split -l 100000 file1.txt file1_part_ # 对每个小文件单独执行标准化+比对,最后合并所有小结果
内容的提问来源于stack exchange,提问作者Tony
相关产品推荐
相关产品推荐

