【发布时间】:2020-04-28 00:55:10
【问题描述】:
场景:我想为地址编写自己的自动完成 API,就像 Google 提供的那样。 (非常基本:街道、门牌号码、城市、邮政编码、国家)。它仅供私人使用和培训目的。我想先覆盖大约 100 万个地址。
使用的技术: .Net Framework(非核心)、C#、Visual Studio、OSMSharp、Microsoft SQL-Server、Web Api 2(尽管我将来可能会切换到 ASP.Net Core .)
方法:
- 设置项目(用于演示目的的 Web Api 2 或控制台项目)
- 使用 DownloadClient() (https://download.geofabrik.de/) 从 OpenStreetMaps 下载相关文件
- 使用 OSMSharp 读入文件并过滤掉相关数据。
- 将过滤后的数据转换为 DataTable。
- 使用 DataTable 馈送 SQLBulkCopy 方法将数据导入数据库。
问题:第 4 步耗时太长。对于 osm.pbf 格式的“Regierungsbezirk Köln”之类的文件,大约 160MB(未压缩的 osm 文件大约 2.8 GB),大约需要 4-5 小时。我想优化这个。另一方面,将 DataTable 批量复制到数据库中(大约 100 万行)只需要大约 5 秒。 (哇。太棒了。)
最小复制: https://github.com/Cr3pit0/OSM2Database-Minimal-Reproduction
我尝试了什么:
在 SQL-Server 中使用存储过程。这带来了一系列完全不同的问题,我没能让它正常工作(主要是因为未压缩的 osm.pbf 文件超过 2GB,而 SQL Server 不喜欢这样)
想出一种不同的方法来过滤并将文件中的数据转换为 DataTable(或 CSV)。
使用 Overpass-API。尽管我在某处读到 Overpass-API 不适用于超过 10,000 个条目的数据集。
向 StackOverflow 上的绝地大师寻求帮助。 (目前正在处理中......:D)
代码摘录:
public static DataTable getDataTable_fromOSMFile(string FileDownloadPath)
{
Console.WriteLine("Finished Downloading. Reading File into Stream...");
using (var fileStream = new FileInfo(FileDownloadPath).OpenRead())
{
PBFOsmStreamSource source = new PBFOsmStreamSource(fileStream);
if (source.Any() == false)
{
return new DataTable();
}
Console.WriteLine("Finished Reading File into Stream. Filtering and Formatting RawData to Addresses...");
Console.WriteLine();
DataTable dataTable = convertAdressList_toDataTable(
source.Where(x => x.Type == OsmGeoType.Way && x.Tags.Count > 0 && x.Tags.ContainsKey("addr:street"))
.Select(Address.fromOSMGeo)
.Distinct(new AddressComparer())
);
return dataTable;
}
};
private static DataTable convertAdressList_toDataTable(IEnumerable<Address> addresses)
{
DataTable dataTable = new DataTable();
if (addresses.Any() == false)
{
return dataTable;
}
dataTable.Columns.Add("Id");
dataTable.Columns.Add("Street");
dataTable.Columns.Add("Housenumber");
dataTable.Columns.Add("City");
dataTable.Columns.Add("Postcode");
dataTable.Columns.Add("Country");
Int32 counter = 0;
Console.WriteLine("Finished Filtering and Formatting. Writing Addresses From Stream to a DataTable Class for the Database-SQLBulkCopy-Process ");
foreach (Address address in addresses)
{
dataTable.Rows.Add(counter + 1, address.Street, address.Housenumber, address.City, address.Postcode, address.Country);
counter++;
if (counter % 10000 == 0 && counter != 0)
{
Console.WriteLine("Wrote " + counter + " Rows From Stream to DataTable.");
}
}
return dataTable;
};
【问题讨论】:
-
您是否已经尝试使用分析器来确定您的瓶颈?
-
您需要向我们展示如何生成
DataTable的代码,因为那是您的瓶颈所在。看到这个答案:stackoverflow.com/a/564373/3181 -
所有相关代码都在github的Project中。如果我只是将数十行代码复制并粘贴到这个问题中,我不知道它是否会受到赞赏。至于瓶颈。我使用包含 DateTimes 的简单日志来确定哪个操作花费的时间最长。
-
@StuartGrassie FastMember 听起来很有趣,尽管我不确定这是否是我需要的。有时间我会去看看的。
-
正如@StuartGrassie 所说,您的
DataTable填充代码在逐行填充时效率可怕地。 FastMember 或类似产品是您最好的选择。
标签: c# .net openstreetmap large-data large-files