我正在从国家公路交通安全管理局 API 中提取大约 25,000,000 个 VIN 号码的 VIN 规范。这是大量数据,并且由于我没有以任何方式转换数据,因此curl
似乎是比 Python 更高效、更轻量级的完成任务的方法(因为 Python 的 GIL 使并行处理变得有点痛苦)。
在下面的代码中,vins.csv
是一个包含 25M VIN 的大样本的文件,被分成 100 个 VIN 的块。这些将被传递给使用 4 个内核的 GNU Parallel。一切都nhtsa_vin_data.csv
在最后倾倒。
$ cat vins.csv | parallel -j10% curl -s --data "format=csv" \
--data "data={1}" https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVINValuesBatch/ \
>> /nas/BIGDATA/kemri/nhtsa_vin_data.csv
这个过程一开始大约每分钟写入 3,000 个 VIN,并且随着时间的推移逐渐变慢(目前约为 1,200 个/分钟)。
我的问题
- 我的命令中是否有任何东西会随着
nhtsa_vin_data.csv
规模的增长而增加开销? - 这与Linux处理
>>
操作的方式有关吗?
更新 #1 - 解决方案
每个@slm的第一个解决方案 - 使用并行的tmp文件选项将每个curl输出写入其自己的.par文件,最后合并:
$ cat vins.csv | parallel \
--tmpdir /home/kemri/vin_scraper/temp_files \
--files \
-j10% curl -s \
--data "format=csv" \
--data "data={1}" https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVINValuesBatch/ > /dev/null
cat <(head -1 $(ls *.par|head -1)) <(tail -q -n +2 *.par) > all_data.csv
@oletange 的第二个解决方案 - 使用 --line-buffer 将输出缓冲到内存而不是磁盘:
$ cat test_new_mthd_vins.csv | parallel \
--line-buffer \
-j10% curl -s \
--data "format=csv" \
--data "data={1}" https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVINValuesBatch/ \
>> /home/kemri/vin_scraper/temp_files/nhtsa_vin_data.csv
性能考虑
我发现这里建议的两个解决方案都非常有用和有趣,并且将来肯定会更多地使用这两个版本(用于比较性能和额外的 API 工作)。希望我能够运行一些测试,看看哪一个对于我的用例表现更好。
此外,运行某种吞吐量测试(如 @oletange 和 @slm 建议)是明智的,因为 NHTSA 成为此处瓶颈的可能性是不可忽视的。
答案1
我怀疑这会导致您在分叉收集 API 数据的命令之间>>
争用文件。nhtsa_vin_data.csv
curl
parallel
我会这样调整你的应用程序:
$ cat p.bash
#!/bin/bash
cat vins.csv | parallel --will-cite -j10% --progress --tmpdir . --files \
curl -s --data "format=csv" \
--data "data={1}" https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVINValuesBatch/
这将为您的curl
命令提供自己的独立文件来写入数据。
例子
我获取了您提供给我的这 3 个 VIN 1HGCR3F95FA017875;1HGCR3F83HA034135;3FA6P0T93GR335818;
,并将它们放入名为 的文件中vins.csv
。然后我将它们复制了很多次,以便该文件最终具有以下特征:
$ tail -1 vins.csv | grep -o ';' | wc -l
26
行数
$ wc -l vins.csv
15 vins.csv
然后我使用这些数据运行我的脚本:
$ ./p.bash
Computers / CPU cores / Max jobs to run
1:local / 1 / 1
Computer:jobs running/jobs completed/%of started jobs/Average seconds to complete
local:1/0/100%/0.0s ./pard9QD3.par
local:1/1/100%/10.0s ./paruwK9L.par
local:1/2/100%/8.5s ./parT6rCS.par
local:1/3/100%/7.3s ./pardzT2g.par
local:1/4/100%/6.8s ./parDAsaO.par
local:1/5/100%/6.8s ./par9X2Na.par
local:1/6/100%/6.7s ./par6aRla.par
local:1/7/100%/6.7s ./parNR_r4.par
local:1/8/100%/6.4s ./parVoa9k.par
local:1/9/100%/6.1s ./parXJQTc.par
local:1/10/100%/6.0s ./parDZZrp.par
local:1/11/100%/6.0s ./part0tlA.par
local:1/12/100%/5.9s ./parydQlI.par
local:1/13/100%/5.8s ./par4hkSL.par
local:1/14/100%/5.8s ./parbGwA2.par
local:0/15/100%/5.4s
把事情放在一起
当上述运行完成后,您可以将cat
所有文件放在一起以获得单个.csv
文件ala:
$ cat *.par > all_data.csv
执行此操作时请务必小心,因为每个文件对于其中包含的 CSV 数据都有自己的标题行。要处理从结果文件中取出标头:
$ cat <(head -1 $(ls *.par|head -1)) <(tail -q -n +2 *.par) > all_data.csv
你的表现变慢
在我的测试中,DOT 网站确实在查询继续访问其 API 时对其进行了限制。我在实验中看到的上述时间虽然很小,但随着每个查询发送到 API 网站而减少。
我在笔记本电脑上的表现如下:
$ seq 5 | parallel --will-cite --line-buffer 'yes {} | head -c 1G' | pv >> /dev/null
5GiB 0:00:51 [99.4MiB/s] [ <=> ]
笔记:以上是借用Ole Tange的回答并修改的。它通过管道写入 5GB 数据parallel
并将其传输到pv >> /dev/null
.pv
使用它,我们可以监控通过管道的吞吐量并获得 MB/s 类型的测量结果。
我的笔记本电脑能够达到约 100MB/s 的吞吐量。
NHTSA API 常见问题解答
应用程序编程接口
对于“批量解码 VIN(平面格式)”,是否有一个通过 URL 进行此查询的示例,类似于其他操作?
对于这个特定的 API,您只需将一组 VIN 放入框中,并用“;”分隔。您还可以在“;”之前注明型号年份用“,”分隔。您可以通过此服务输入的 VIN 数量有上限。
盒内示例为样本:5UXWX7C5*BA,2011; 5YJSA3DS*EF
上面提到使用API有一个上限:
您可以通过此服务输入的 VIN 数量有上限。
参考
答案2
性能通常受到以下之一的限制:
- 网络带宽。您可以使用它
sudo iftop
来查看您的网络连接是否已 100% 使用。 - 网络延迟。如果另一端的服务器需要很长时间才能回复,那么您将不会看到 100% 的带宽利用率。
- 磁盘 I/O。您可以使用
iostat -dkx 1
它来查看任何磁盘的 I/O 是否已 100% 使用。 - 中央处理器。
top
如果您的 CPU 利用率为 100%,则可以使用。按1
可查看各个 CPU 线程。如果其中之一为 100%,那么您就有一个单线程程序,受此限制。
GNU Parallel 擅长并行运行作业以利用更多带宽、磁盘 I/O 和 CPU。
但它也有其局限性。
GNU Parallel 通常将输出缓存在/tmp
.这意味着您的磁盘 I/O/tmp
可能是瓶颈。
幸运的是,在处理 CSV 时,您很少关心行的顺序:如果行混合也没关系,只要它是完整的行即可。
如果您使用--line-buffer
版本 >20170822 则 GNU Parallel 会不是磁盘上的缓冲区输出 - 它仅在内存中缓冲一整行。这需要更多的 CPU 能力才能完成,因此请检查是否parallel
使用 100% CPU。如果它使用较少,那么您还没有达到该瓶颈。
$ cat vins.csv | parallel --line-buffer curl -s --data "format=csv" \
--data "data={1}" https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVINValuesBatch/ \
>> /nas/BIGDATA/kemri/nhtsa_vin_data.csv
您可以通过以下方式查看是否存在本地瓶颈:
$ seq 1000 | parallel --line-buffer 'yes {} | head -c 1G' | pv >> /nas/BIGDATA/test
在我那台糟糕的笔记本电脑上,我的速度约为 100 MB/s。所以我那台糟糕的笔记本电脑将能够处理来自 dot.gov 的 1 Gbit/s。