go中实现原子去重写文件需用map缓存已处理key(如id+timestamp拼接或sha256哈希),写入前判重;必须用os.o_append追加写入,避免覆盖;并发时用sync.map或rwmutex保护map;重启后需从文件加载历史key初始化map,并规范computekey逻辑统一去重标准。

用 map 记录已写入的 key,避免重复写入
Go 本身不提供“原子去重写文件”的内置机制,最常用且可控的方式是:在写入前用 map 缓存已处理过的记录标识(比如某字段值或哈希),后续遇到相同标识就跳过。适合日志追加、ETL 导出等场景。
关键点不是“文件去重”,而是“写入前判重”——因为文件系统不维护语义索引,靠读全量文件判断既慢又易错(尤其并发时)。
- 用
map[string]struct{}最省内存,struct{}零字节 - key 建议用业务唯一字段拼接(如
id + timestamp),或用sha256.Sum256算哈希,别直接用原始行字符串(空格、换行差异易导致误判) - 如果数据量超百MB,别把整个文件内容 load 进内存做 map;改用布隆过滤器(
bloomfilter包)或外部临时 SQLite 表
追加写入时用 os.O_APPEND | os.O_CREATE 安全打开文件
很多人以为 os.OpenFile(..., os.O_WRONLY|os.O_CREATE) 就够了,但漏掉 os.O_APPEND 会导致后续写入覆盖开头——看似“没重复”,实则是旧记录被抹掉,新记录反复覆盖同一位置。
正确打开方式:
f, err := os.OpenFile("output.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
log.Fatal(err)
}
defer f.Close()
-
os.O_APPEND保证每次f.Write()都追加到文件末尾,不受其他 goroutine 干扰 - 不要用
os.Create()替代——它等价于O_TRUNC,每次都会清空文件 - 如果需按天分文件(如
log_20240501.txt),先检查文件是否存在再决定是否新建,而非无脑Create
并发写入时用 sync.Map 或加锁保护判重 map
多个 goroutine 同时往一个文件写,且共用同一个去重 map,不加同步会 panic:“concurrent map read and map write”。sync.Map 是常见解法,但要注意它的 key 类型必须是可比较的(不能是 slice、map、func)。
- 简单场景用
sync.RWMutex包裹普通map更直观,读多写少时性能不输sync.Map - 别在循环里反复
mu.Lock()/Unlock(),把整批记录判重逻辑包在锁内,减少竞争 - 如果写入目标是不同文件(如按用户 ID 分片),可为每个文件配独立 map + mutex,彻底规避锁争用
从已有文件加载历史记录做初始化去重
程序重启后,内存 map 清空,若不加载上次写入的内容,仍会写入重复记录。这时得从文件末尾往前读(或全量读)提取已存在 key。
注意:别用 bufio.Scanner 默认 64KB 缓冲——超长日志行会被截断,导致哈希不一致。改用 bufio.NewReader + ReadString('\n') 手动控制:
r := bufio.NewReader(f)
for {
line, err := r.ReadString('\n')
if err == io.EOF { break }
if err != nil { /* handle */ }
key := computeKey(line) // 你的提取逻辑
seen[key] = struct{}{}
}
- 只读最后一千行就够了?用
tail -n 1000 file | ...调外部命令太重,推荐用github.com/hpcloud/tail或自己实现反向读(seek 到末尾,倒着找换行符) - 如果文件是 JSON Lines 格式,每行一个对象,解析时用
json.Decoder流式解码,别json.Unmarshal([]byte(line))全量转切片 - 加载历史后记得对 map 做一次
runtime.GC()提示回收(小概率有用,但大文件解析后内存残留明显)
真正麻烦的从来不是“怎么去重”,而是“怎么定义重复”——时间戳毫秒级差异、JSON 字段顺序不同、空格缩进不一致,都可能让两个逻辑相同的记录被判为不同。建议在 computeKey 里统一 trim、排序键名、忽略无关字段,比在文件层补救更可靠。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











