场景:我想编写自己的地址自动完成API,就像一个Google提供的那样。 (非常基本:街道,门牌号,城市,邮政编码,国家/地区)。它仅供私人使用和培训用途。我想从一开始就涵盖大约100万个地址。
使用的技术: .Net Framework(不是Core),C#,Visual Studio,OSMSharp,Microsoft SQL-Server,Web Api 2(尽管将来我可能会切换到ASP.Net Core。)
方法:
问题:步骤4花费的时间太长。对于格式为osm.pbf的“ RegierungsbezirkKö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却不喜欢这样)
请采用其他方法来过滤数据并将其从文件转换为数据表(或CSV)。
使用Overpass-API。尽管我在某处读到Overpass-API不适用于10,000个以上的数据集。
向StackOverflow上的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-Project(https://github.com/Cr3pit0/OSM2Database-Minimal-Reproduction)(尽管我没有彻底对其进行测试,因为我从Test-Project转移到了Real Deal,这是一个Web Api)
我很确定它可以进一步优化,但是我不认为我现在处于关注状态。我猜一个方法可能需要每月12分钟才能更新整个数据库,这需要12分钟。现在,我可以继续优化自动完成的查询。
所以非常感谢写“ osm2mssql”的人。