osm-File(osm.pbf) 的处理和过滤在 C# 中花费的时间太长
Processing and Filtering of osm-File(osm.pbf) is taking way too long in C#
场景: 我想为地址编写自己的自动完成-API,就像 One Google 提供的那样。 (非常基本:街道、门牌号、城市、邮政编码、国家/地区)。它仅供私人使用和培训目的。我想一开始就涵盖大约 100 万个地址。
使用的技术: .Net Framework(非核心)、C#、Visual Studio、OSMSharp、Microsoft SQL-Server、Web Api 2(虽然我将来可能会切换到 ASP.Net 核心。)
方法:
- 设置项目(Web Api 2 或用于演示目的的控制台项目)
- 使用 DownloadClient() 从 OpenStreetMaps 下载相关文件 (https://download.geofabrik.de/)
- 使用 OSMSharp 读入文件并过滤掉相关数据。
- 将过滤后的数据转换为数据表。
- 使用 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 服务器不喜欢这样)
想出一种不同的方法来过滤数据并将文件中的数据转换为数据表(或 CSV)。
使用立交桥-API。尽管我在某处读到 Overpass-API 不适用于超过 10,000 个条目的数据集。
向 Whosebug 上的 Jedi-Grandmasters 寻求帮助。 (目前正在处理中...: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;
};
好的,我想我明白了。对于大约 600mb 的文件大小和过滤后大约 310 万行的数据,我减少到大约 12 分钟。
我尝试的第一件事是用 FastMember 替换填充我的 DataTable 的逻辑。哪个有效,但没有带来我希望的性能提升(我在 3 小时后取消了流程......)。经过更多研究后,我偶然发现了一个名为 "osm2mssql" (https://archive.codeplex.com/?p=osm2mssql) 的旧项目。我使用了代码的一小部分直接从 osm.pbf 文件中读取数据并将其修改为我的用例(→ 从 Ways 中提取地址数据)。我确实使用 FastMember 将 IEnumerable<Address>
写入数据表,但我不再需要 OSM-Sharp 以及它们拥有的任何额外依赖项。所以非常感谢FastMember的建议。我一定会在未来的项目中牢记这个图书馆。
对于那些感兴趣的人,我相应地更新了我的 Github-项目 (https://github.com/Cr3pit0/OSM2Database-Minimal-Reproduction)(虽然我没有彻底测试它,因为我从测试项目转到了真正的交易, 这是一个 Web Api)
我很确定它可以进一步优化,但我认为我现在不关心。 12 分钟的方法可能会每月调用一次来更新整个数据库,我想这很好。现在我可以继续为自动完成优化我的查询。
所以非常感谢写 "osm2mssql" 的人。
场景: 我想为地址编写自己的自动完成-API,就像 One Google 提供的那样。 (非常基本:街道、门牌号、城市、邮政编码、国家/地区)。它仅供私人使用和培训目的。我想一开始就涵盖大约 100 万个地址。
使用的技术: .Net Framework(非核心)、C#、Visual Studio、OSMSharp、Microsoft SQL-Server、Web Api 2(虽然我将来可能会切换到 ASP.Net 核心。)
方法:
- 设置项目(Web Api 2 或用于演示目的的控制台项目)
- 使用 DownloadClient() 从 OpenStreetMaps 下载相关文件 (https://download.geofabrik.de/)
- 使用 OSMSharp 读入文件并过滤掉相关数据。
- 将过滤后的数据转换为数据表。
- 使用 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 服务器不喜欢这样)
想出一种不同的方法来过滤数据并将文件中的数据转换为数据表(或 CSV)。
使用立交桥-API。尽管我在某处读到 Overpass-API 不适用于超过 10,000 个条目的数据集。
向 Whosebug 上的 Jedi-Grandmasters 寻求帮助。 (目前正在处理中...: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;
};
好的,我想我明白了。对于大约 600mb 的文件大小和过滤后大约 310 万行的数据,我减少到大约 12 分钟。
我尝试的第一件事是用 FastMember 替换填充我的 DataTable 的逻辑。哪个有效,但没有带来我希望的性能提升(我在 3 小时后取消了流程......)。经过更多研究后,我偶然发现了一个名为 "osm2mssql" (https://archive.codeplex.com/?p=osm2mssql) 的旧项目。我使用了代码的一小部分直接从 osm.pbf 文件中读取数据并将其修改为我的用例(→ 从 Ways 中提取地址数据)。我确实使用 FastMember 将 IEnumerable<Address>
写入数据表,但我不再需要 OSM-Sharp 以及它们拥有的任何额外依赖项。所以非常感谢FastMember的建议。我一定会在未来的项目中牢记这个图书馆。
对于那些感兴趣的人,我相应地更新了我的 Github-项目 (https://github.com/Cr3pit0/OSM2Database-Minimal-Reproduction)(虽然我没有彻底测试它,因为我从测试项目转到了真正的交易, 这是一个 Web Api)
我很确定它可以进一步优化,但我认为我现在不关心。 12 分钟的方法可能会每月调用一次来更新整个数据库,我想这很好。现在我可以继续为自动完成优化我的查询。
所以非常感谢写 "osm2mssql" 的人。