news 2026/4/16 21:28:06

40亿个qq号,如何去重,只有1g内存可用.

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
40亿个qq号,如何去重,只有1g内存可用.

这个问题是典型的海量数据处理场景(类似“大数据去重”问题),需要巧妙利用有限内存。下面是详细解决方案和优化策略:

一、问题分析

已知条件:

  • 40亿个QQ号(假设每个QQ号8字节,原始数据约 32GB)

  • 只有1GB内存可用

  • 需要去重,保留唯一QQ号

关键限制:

  • 内存放不下全部数据

  • 硬盘空间足够(可以存储中间文件)

  • 时间效率需要合理

二、解决方案(多层次)

方案1:哈希分片法(最可靠)

# 伪代码描述流程 def deduplicate_huge_data(): # 步骤1:哈希分片到多个小文件 for qq in read_all_qq(): hash_value = hash(qq) % 1024 # 分成1024个文件 write_to_file(f"part_{hash_value}.txt", qq) # 步骤2:逐个文件去重(内存中处理) unique_qq = [] for i in range(1024): qq_list = read_file(f"part_{i}.txt") # 1GB内存可容纳约1.3亿个QQ号(每个8字节) # 每个文件约4000万个QQ号,内存足够 unique_set = set(qq_list) # 去重 write_to_file(f"unique_{i}.txt", unique_set) # 步骤3:合并结果 merge_all_unique_files()

分片数量计算:

40亿 ÷ 1.3亿 ≈ 31 但需要预留内存给其他操作,建议分1024个文件: 每个文件约 4000万QQ号,占用内存约320MB

方案2:位图法(BitMap)优化版

适用于QQ号范围相对集中的情况:

// 两层位图方案 public class QQDeduplication { // 假设QQ号范围在10亿以内(实际情况QQ号目前最大11位) private static final long MAX_QQ = 10_0000_0000L; // 100亿 public void deduplicate() { // 第一层:布隆过滤器(Bloom Filter)快速过滤 BloomFilter bloomFilter = createBloomFilter(); // 第二层:分段位图 // 将100亿范围分成1000个段,每段1亿 int segmentSize = 100_000_000; // 1亿 int segments = (int)(MAX_QQ / segmentSize) + 1; for (int seg = 0; seg < segments; seg++) { // 当前段的位图:1亿位 ≈ 12.5MB BitSet bitSet = new BitSet(segmentSize); // 扫描原始数据,处理属于当前段的QQ号 for (long qq : readAllQQNumbers()) { if (qq / segmentSize == seg) { long offset = qq % segmentSize; if (!bitSet.get((int)offset)) { bitSet.set((int)offset); writeUniqueQQ(qq); } } } } } }

内存计算:

  • 每个段1亿QQ号,位图大小:1亿位 ÷ 8 = 12.5MB

  • 加上程序开销,远小于1GB

方案3:外部排序+去重

# Linux系统命令组合方案 # 步骤1:分割大文件(假设原始文件为qq.txt) split -l 10000000 qq.txt qq_part_ # 步骤2:对每个小文件排序去重 for file in qq_part_*; do sort -u $file > ${file}_sorted done # 步骤3:多路归并去重 sort -m -u qq_part_*_sorted > qq_unique.txt

Java实现外部排序:

public class ExternalSortDeduplicate { public void externalSort(String inputFile, String outputFile) throws IOException { int maxLines = 50000000; // 每批处理5000万行 List<String> chunkFiles = new ArrayList<>(); // 1. 分批读取、排序、去重、写入临时文件 try (BufferedReader br = new BufferedReader(new FileReader(inputFile))) { List<Long> buffer = new ArrayList<>(maxLines); String line; int chunkNum = 0; while ((line = br.readLine()) != null) { buffer.add(Long.parseLong(line)); if (buffer.size() >= maxLines) { // 排序去重 Collections.sort(buffer); String chunkFile = writeUniqueChunk(buffer, chunkNum++); chunkFiles.add(chunkFile); buffer.clear(); } } // 处理最后一批 if (!buffer.isEmpty()) { Collections.sort(buffer); String chunkFile = writeUniqueChunk(buffer, chunkNum); chunkFiles.add(chunkFile); } } // 2. 多路归并 mergeSortedChunks(chunkFiles, outputFile); // 3. 清理临时文件 for (String chunkFile : chunkFiles) { new File(chunkFile).delete(); } } // 多路归并实现 private void mergeSortedChunks(List<String> chunkFiles, String output) throws IOException { PriorityQueue<QueueElement> pq = new PriorityQueue<>(); // 打开所有文件,读取第一行 List<BufferedReader> readers = new ArrayList<>(); for (String chunkFile : chunkFiles) { BufferedReader reader = new BufferedReader(new FileReader(chunkFile)); readers.add(reader); String line = reader.readLine(); if (line != null) { pq.offer(new QueueElement(Long.parseLong(line), readers.size() - 1)); } } try (BufferedWriter bw = new BufferedWriter(new FileWriter(output))) { Long lastOutput = null; while (!pq.isEmpty()) { QueueElement current = pq.poll(); // 去重:只输出与前一个不同的数 if (lastOutput == null || !current.value.equals(lastOutput)) { bw.write(current.value.toString()); bw.newLine(); lastOutput = current.value; } // 从同一文件读取下一行 BufferedReader reader = readers.get(current.fileIndex); String line = reader.readLine(); if (line != null) { pq.offer(new QueueElement(Long.parseLong(line), current.fileIndex)); } } } // 关闭所有reader for (BufferedReader reader : readers) { reader.close(); } } }

三、实际生产环境中的优化方案

方案4:MapReduce分布式处理(如果允许)

// Hadoop MapReduce示例 public class QQDeduplicationMR { public static class DedupMapper extends Mapper<Object, Text, Text, NullWritable> { private Text qq = new Text(); public void map(Object key, Text value, Context context) throws IOException, InterruptedException { // QQ号作为key,MapReduce会自动去重 qq.set(value.toString().trim()); context.write(qq, NullWritable.get()); } } public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "QQ Deduplication"); job.setJarByClass(QQDeduplicationMR.class); job.setMapperClass(DedupMapper.class); job.setReducerClass(Reducer.class); // 使用默认Reducer去重 job.setOutputKeyClass(Text.class); job.setOutputValueClass(NullWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1); } }

方案5:数据库辅助方案

-- 使用数据库临时表(如果有数据库可用) -- 步骤1:创建临时表(带索引) CREATE TABLE temp_qq ( qq_number BIGINT PRIMARY KEY ) ENGINE = MEMORY; -- 使用内存表 -- 步骤2:分批插入(每次插入1000万条) -- 由于主键约束,重复的会被忽略 INSERT IGNORE INTO temp_qq VALUES (...); -- 步骤3:导出唯一结果 SELECT qq_number FROM temp_qq INTO OUTFILE 'unique_qq.txt';

方案6:布隆过滤器 + 磁盘存储

// 使用Guava的布隆过滤器 public class BloomFilterSolution { public void deduplicate(String inputFile, String outputFile) throws IOException { // 布隆过滤器参数:预期40亿元素,错误率0.1% BloomFilter<Long> bloomFilter = BloomFilter.create(Funnels.longFunnel(), 4_000_000_000L, 0.001); Set<Long> memoryCache = new HashSet<>(10_000_000); // 内存缓存1千万 try (BufferedReader br = new BufferedReader(new FileReader(inputFile)); BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile))) { String line; while ((line = br.readLine()) != null) { long qq = Long.parseLong(line); if (!bloomFilter.mightContain(qq)) { // 肯定不重复 bloomFilter.put(qq); memoryCache.add(qq); bw.write(line); bw.newLine(); } else if (!memoryCache.contains(qq)) { // 可能重复,需要进一步检查 // 这里可以查询磁盘上的已存在记录 if (!checkInDiskStorage(qq)) { memoryCache.add(qq); bw.write(line); bw.newLine(); } } // 定期清理内存缓存 if (memoryCache.size() > 10_000_000) { memoryCache.clear(); } } } } }

四、性能对比与选择建议

方案内存占用磁盘IO时间复杂度适用场景
哈希分片中等O(n)通用,最稳定
位图分段O(n*k)QQ号范围集中
外部排序中等O(n log n)需要有序输出
MapReduceO(n)分布式环境
数据库O(n)有数据库可用
布隆过滤器极低O(n)允许极小误差

五、生产环境最佳实践

推荐方案:哈希分片 + 内存去重

public class ProductionSolution { public static void main(String[] args) throws Exception { // 参数配置 int numShards = 1024; // 分片数 int batchSize = 10_000_000; // 每批处理1000万 // 阶段1:哈希分片 List<File> shardFiles = hashSharding("qq_input.txt", numShards); // 阶段2:并行处理每个分片 ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); List<Future<File>> futures = new ArrayList<>(); for (File shardFile : shardFiles) { futures.add(executor.submit(() -> processShard(shardFile))); } // 阶段3:合并结果 List<File> resultFiles = new ArrayList<>(); for (Future<File> future : futures) { resultFiles.add(future.get()); } mergeResults(resultFiles, "qq_unique.txt"); executor.shutdown(); } private static File processShard(File shardFile) throws IOException { // 使用HashSet去重(适合分片后数据量) Set<Long> uniqueSet = new HashSet<>(); try (BufferedReader br = new BufferedReader(new FileReader(shardFile))) { String line; while ((line = br.readLine()) != null) { uniqueSet.add(Long.parseLong(line)); } } // 写入临时结果文件 File resultFile = new File(shardFile.getPath() + ".unique"); try (BufferedWriter bw = new BufferedWriter(new FileWriter(resultFile))) { for (Long qq : uniqueSet) { bw.write(qq.toString()); bw.newLine(); } } return resultFile; } }

内存优化技巧:

  1. 使用基本类型集合

// 使用Trove库的LongHashSet,比Java HashSet节省内存 import gnu.trove.set.hash.TLongHashSet; TLongHashSet longSet = new TLongHashSet(10_000_000); // 内存占用:约8字节/元素 vs HashSet的~40字节/元素
  1. 压缩存储

// 使用变长编码存储QQ号 // 较短的QQ号占用更少字节
  1. 分批处理

// 控制每批数据量,防止OOM while (hasMoreData()) { List<Long> batch = readNextBatch(5_000_000); // 每批500万 processBatch(batch); System.gc(); // 显式触发GC }

六、扩展思考

如果QQ号长度不一致(4-11位),可以考虑:

  1. 分长度处理:相同长度的QQ号一起处理

  2. 字典序排序:按字符串排序,然后去重

  3. 前缀树:对字符串形式的QQ号构建Trie树去重

对于超大规模数据(万亿级别),需要:

  1. 分布式计算(Spark/Flink)

  2. 列式存储 + 压缩

  3. 使用RoaringBitmap等高级数据结构

这个问题的核心是时间与空间的权衡,在内存限制下,通过分批、分片、外部排序等技术,实现海量数据去重。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 11:10:51

从入门到精通:PHP对接区块链账户API的8个核心技巧(含实战代码)

第一章&#xff1a;PHP 区块链账户管理概述区块链技术的核心之一是账户系统&#xff0c;它负责标识用户身份、管理资产以及验证交易。在 PHP 环境中实现区块链账户管理&#xff0c;虽然不像 Go 或 Rust 那样常见&#xff0c;但依然可以通过扩展库和加密工具完成安全可靠的账户创…

作者头像 李华
网站建设 2026/4/16 15:53:54

PHP 8.7上线倒计时:你的应用经得起这8项兼容性压力测试吗?

第一章&#xff1a;PHP 8.7上线倒计时&#xff1a;兼容性挑战全景透视随着 PHP 社区对性能与安全性的持续追求&#xff0c;PHP 8.7 的发布进入倒计时阶段。这一版本在继承 JIT 编译优化的基础上&#xff0c;进一步强化了类型系统&#xff0c;并引入多项语言级变更&#xff0c;但…

作者头像 李华
网站建设 2026/4/15 22:28:55

语音合成支持语音签名认证?身份识别安全机制探讨

语音合成支持语音签名认证&#xff1f;身份识别安全机制探讨 在虚拟主播直播带货、AI客服全天候应答、有声书自动生成的今天&#xff0c;我们越来越难分辨一段声音是否来自真人。更令人不安的是&#xff1a;只需几秒钟录音&#xff0c;攻击者就能用AI克隆出你的声音&#xff0c…

作者头像 李华
网站建设 2026/4/16 15:31:25

[精品]基于微信小程序的高新学院学生学业管理系统 UniApp

文章目录 项目效果图开发核心技术介绍&#xff1a;SpringBoot和Vue 介绍系统测试详细视频演示源码获取 项目效果图 项目编号&#xff1a;054 开发核心技术介绍&#xff1a; 本系统的开发环境如下&#xff1a; 操作系统&#xff1a;微软win10以上版本 开发平台&#…

作者头像 李华
网站建设 2026/4/16 19:09:51

语音合成可用于心理治疗?情感陪伴机器人应用前景

语音合成可用于心理治疗&#xff1f;情感陪伴机器人应用前景 在老龄化社会加速到来、心理健康问题日益突出的今天&#xff0c;一个现实难题摆在面前&#xff1a;专业心理咨询师数量有限&#xff0c;服务成本高&#xff0c;而孤独感、焦虑和抑郁却在人群中悄然蔓延。尤其对于独居…

作者头像 李华
网站建设 2026/4/16 13:51:51

PHP动态内容如何高效缓存?边缘计算场景下的2种突破性方案

第一章&#xff1a;PHP动态内容缓存的挑战与边缘计算机遇在现代Web应用中&#xff0c;PHP作为广泛使用的服务器端脚本语言&#xff0c;常用于生成动态内容。然而&#xff0c;传统PHP缓存机制如OPcode缓存和页面级缓存&#xff0c;在面对高并发、低延迟需求时逐渐显现出局限性。…

作者头像 李华