我正在做一个项目,我需要查找与发出 HTTP 请求的记录的 IP 地址关联的主机名。目前,查找是日常 ETL 工作的一部分。当前的方法是使用标量 CLR 函数(与此类似的代码在网络上的许多地方发布,下面发布了我的修改;我不确定原作者是谁):
using System.Data.SqlTypes;
using System.Net;
using System.Security;
using System.Text.RegularExpressions;
using Microsoft.SqlServer.Server;
public partial class udfn_GetHostName
{
[Microsoft.SqlServer.Server.SqlFunction]
public static string udfn_GetHostname(string IPAddr)
{
try
{
/*
Using deprecated method intentionally.
GetHostEntry() is now recommended.
But it does some irritating things like returning an error if a PTR
record points to a name that doesn't have an A record.
*/
IPHostEntry IpEntry = Dns.GetHostByAddress(IPAddr);
// Test whether the record returned has at least one alphabetic character
// If it does, then it's a name
// Otherwise the DNS server might have returned the IP address
Match match = Regex.Match(IpEntry.HostName.ToString(), @"[a-zA-Z]+");
if (match.Success)
{
return IpEntry.HostName.ToString();
}
else
{
return "None";
}
}
catch(Exception ex)
{
return "Failed";
//return ex.Message.ToString();
}
}
}
我不是 C# 开发人员,因此 CLR 代码的质量可能不是很好。
然后我在将新行加载到维度后调用这样的函数:
-- Update only rows that we just inserted
UPDATE DIM.Network_Addresses
SET reverse_dns = dbo.[udfn_GetHostname](client_ip)
WHERE reverse_dns IS NULL
AND is_current = 1
AND created_date = (SELECT MAX(created_date) FROM DIM.API_Network_Address);
这种方法有效但速度很慢,至少有几个原因。
1) 使用标量函数使 SQL Server 使用新的 SQL 上下文对需要更新的每一行调用一次 CLR 函数。
2) 由于 GetHostname() 和其他 CLR 名称解析函数的工作方式,函数调用本身非常慢:长时间超时,有时多次往返网络,如果 DNS 服务器不响应或没有 PTR,则所有超时记录等
可以推荐一种设计模式来提高查找反向 DNS 记录和更新表的性能吗?
我正在考虑一些不同的事情:
1) 将这项工作移到数据库之外,并使用诸如 dig 之类的工具并行进行查找。
2)尝试找到一些方法来并行调用函数或将其转换为内联函数(在这方面没有取得太大进展!)
但是,欢迎任何想法。
不要在 SQL 中执行此操作。从可以归类为“愚蠢地使用 SQLCLR”的少数事情来看,进行昂贵的冗长网络调用排名第一。至少,确保 CLR 代码
Thread.BeginThreadAffinity()
在等待 intertubez 响应之前调用(包括 DNS 查找和反向查找)。处理这个问题的正确方法是使用外部进程,将要解析的 IP 放入队列中,批量出列并使用异步 I/O 并行解析多个(数十个)IP,例如。非过时的
Dns.BeginGetHostEntry()
。