如何快速向文件中写入 1 亿个 ip?

2022-04-09 15:49:53 +08:00
 lsk569937453

背景:需要向一个文件中写入 1 亿个 ip ,最快的方法是什么?机器配置是 mac book,双核 cpu,8GB 内存。 我用 java 实现了一个多线程的写入,发现速度慢的要死(写入时间 5 分钟以上)。有没有人推荐一下速度写入大文件的方法,或者其他语言快速写入大文件的方案。

7812 次点击
所在节点    程序员
66 条回复
lsk569937453
2022-04-09 17:13:15 +08:00
@xiri 因为我刚才写字符串,就把内存干爆了。
xuanbg
2022-04-09 17:21:37 +08:00
IPV4 其实就是个 32 位无符号整型,1 亿也就只需要 100M ,写文件几秒钟就好了呀。
documentzhangx66
2022-04-09 17:39:03 +08:00
最佳情况,1.5GB 总文件大小,假设是机械硬盘每秒 100MB 的顺序写入速度,最快大概 15 秒能解决问题。

最差情况,如果你是需要随机写,按机械硬盘每秒 100 次 iops 来算,全部乱序最久需要 11 天。

现在的问题是,你需要把需求中的随机,最大程度改为顺序。

因此,先在内存中,随机把数据都整理后,最后一次或几次顺序写入文件,这才是上策。如果内存一次性放不下 1.5G ,可以把这 1.5G 拆成几块,每次整理,最后用外排序,速度也慢不到哪里去。
Features
2022-04-09 17:41:35 +08:00
哈哈哈,PHP 有官方的 ip2long 和 long2ip
根据这个思路,先转成 long ,再拼接储存应该会快很多
Inn0Vat10n
2022-04-09 17:42:27 +08:00
IO 操作多线程性能没提升已经是历史了, 现在部分介质并发写性能会更高的
oldshensheep
2022-04-09 17:52:40 +08:00
一秒不到
<script src="https://gist.github.com/oldshensheep/9af3218c5b365d9cc33cba59b01265e3.js"></script>

写到内存时间 ms:313
从内存写到硬盘时间 ms:572
总时间 ms:885
oldshensheep
2022-04-09 18:03:36 +08:00
代码有点毛病,应该是一次写 4 字节一个 IP ,但是结果不会有太大区别。
LeegoYih
2022-04-09 19:10:01 +08:00
用 bitmap ,ip 转 int 当索引,存在设置为 1 ,不存在就是 0 ,然后序列化就行。
0o0O0o0O0o
2022-04-09 19:19:53 +08:00
创建一个 int 数组,然后 shuffle ,目的是去重,毕竟 ipv4 总共才 42 亿,直接随机容易重复。然后把 int 转成 ipv4 字符串再写出,在我的服务器上大概 6 秒。

init array 117.512617ms
shuffle array 3.546346202s
write to buffer 5.151557182s
write to file 5.613269245s
clf
2022-04-09 19:22:58 +08:00
有没有可能是你写的代码有问题,而不是多线程能比单线程有提升。或者多线程没提升在写入的过程中而是提升在了奇奇怪怪的代码逻辑上。
colatin
2022-04-09 19:49:57 +08:00
println 然后重定向到文件?
yuguorui96
2022-04-09 20:35:10 +08:00
写 1 亿没意思,连磁盘缓存都没打满……写个 10 亿吧
即使是随机数据,使用 zlib 这样的库也能极大的压缩数据

1e8, zlib vs raw, 810ms vs 310 ms
1e9, zlib vs raw, 11s vs 3.73s

https://gist.github.com/yuguorui/9700aa236dcb6e7072de282310d3b866
yuguorui96
2022-04-09 20:37:47 +08:00
@yuguorui96 额,后面的时间写反了,zlib 是快的那个哈。
ZE3kr
2022-04-09 21:12:35 +08:00
你们的真实场景中有将 ip 保存为 32 位整数的吗?

IP 本来就是 32 位整数…所以不转换的原始格式就是整数…只不过很多情况我们会把它转换成字符串表示,那是为了方便人去阅读,如 1.1.1.1 这样…一些日志也是为了方便人阅读,做了一个转换。楼主还是没转换过来思维
iyaozhen
2022-04-09 21:16:49 +08:00
我想问的是,你们的真实场景中有将 ip 保存为 32 位整数的吗?
有,因为很多场景要做 ip 段判断

你工作中存储 ip 的地方有用整数代替 ip 的吗?
如上所述数据库会存为 int
日志打印的时候还是方便人看的字符串
darknoll
2022-04-09 21:21:54 +08:00
@lsk569937453 先把 ip 转成 unsigned int 不就完事了?
def ipToInt(ip):
ipList = ip.split(".")
seg0 = int(ipList[0]) << 24
seg1 = int(ipList[1]) << 16
seg2 = int(ipList[2]) << 8
seg3 = int(ipList[3])
return seg0 | seg1 | seg2 | seg3
Features
2022-04-09 21:36:25 +08:00
最近在学习世界最好的语言 PHP ,用 PHP 试了一下

1.把 ip 转换成 int 遍历到数组中 花费 2800 ms
2.把数组 implode ,写入到文件中 花费 1800 ms
lsk569937453
2022-04-09 22:03:42 +08:00
@oldshensheep 随机 ip ,你这是顺序 ip 吧
lsk569937453
2022-04-09 22:08:36 +08:00
@0o0O0o0O0o 好快的速度,老哥,能看一下代码吗?
0o0O0o0O0o
2022-04-09 22:14:46 +08:00
@lsk569937453 这个数据量就直接往内存怼就可以了,高深的我不会...

package main

import (
"fmt"
"io/ioutil"
"math/rand"
"time"
)

func ubtoa(dst []byte, start int, v byte) int {
if v < 10 {
dst[start] = v + '0'
return 1
} else if v < 100 {
dst[start+1] = v%10 + '0'
dst[start] = v/10 + '0'
return 2
}

dst[start+2] = v%10 + '0'
dst[start+1] = (v/10)%10 + '0'
dst[start] = v/100 + '0'
return 3
}

func main() {
rand.Seed(time.Now().Unix())

t := time.Now()

arr := make([]int, 100000000)
for i := range arr {
arr[i] = i
}
fmt.Println("init array", time.Since(t))

rand.Shuffle(len(arr), func(i, j int) { arr[i], arr[j] = arr[j], arr[i] })
fmt.Println("shuffle array", time.Since(t))

b := make([]byte, 16*100000000)
pos := 0
for i := range arr {
pos += ubtoa(b, pos, byte(arr[i]>>24))
b[pos] = '.'
pos++

pos += ubtoa(b, pos, byte(arr[i]>>16))
b[pos] = '.'
pos++

pos += ubtoa(b, pos, byte(arr[i]>>8))
b[pos] = '.'
pos++

pos += ubtoa(b, pos, byte(arr[i]))
b[pos] = '\n'
pos++
}
fmt.Println("write to buffer", time.Since(t))

ioutil.WriteFile("ip.txt", b[:pos], 0600)
fmt.Println("write to file", time.Since(t))
}

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/845892

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX