Python中筛选百万级IP地址是否属于3个CIDR的最快方法
最快的IP筛选方案:选B,别碰A!
绝对是方案B更快,而且差距会非常明显——尤其是你只有3个CIDR的情况下,咱们来掰扯清楚为什么:
先说说方案A为啥拉胯
把CIDR转换成全量IP集合的思路,乍一看简单,但实际完全不适合你的场景:
- 内存直接爆炸:比如一个
/8的CIDR对应16,777,216个IP,光是存这个集合就要占几十MB(甚至更多,取决于语言),要是有几个大前缀的CIDR,你的内存直接扛不住。 - 预处理慢到离谱:生成全量IP集合本身就要遍历所有可能的IP,大前缀CIDR的生成时间会非常长,完全没必要做这种无用功。
- 查询效率也没优势:就算你勉强生成了集合,100万次的“是否在集合中”查询,虽然是O(1),但集合的哈希查询本身也有开销,而且前面的预处理成本已经把这条路堵死了。
方案B才是最优解
把CIDR转成起始/结束的整数IP区间,然后逐个检查IP是否在区间内,这才是高效的正确姿势:
- 预处理成本极低:每个CIDR只需要做一次计算,把IP转成32位整数,再通过子网掩码算出区间的起始和结束值。3个CIDR的话,几毫秒就能搞定所有预处理。
- 查询效率拉满:把待检查的IP转成整数后,只需要做3次简单的数值比较(判断是否在某个区间内)。100万次查询就是300万次数值比较,这对CPU来说完全是小菜一碟,线性时间O(n),几乎没有额外开销。
- 内存占用可以忽略:只需要存3对整数,完全不占空间。
给你个简单的Python实现示例
先写两个工具函数:
def ip_to_int(ip: str) -> int: """把IP字符串转成32位整数""" octets = list(map(int, ip.split('.'))) return (octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3] def cidr_to_range(cidr: str) -> tuple[int, int]: """把CIDR转成起始和结束的整数IP区间""" ip_str, prefix_str = cidr.split('/') prefix_len = int(prefix_str) ip_int = ip_to_int(ip_str) # 计算子网掩码 mask = (0xFFFFFFFF << (32 - prefix_len)) & 0xFFFFFFFF start = ip_int & mask end = start | (~mask & 0xFFFFFFFF) return (start, end)
然后进行筛选:
# 你的3个CIDR列表 target_cidrs = ["1.0.0.0/25", "xxx.xxx.xxx.xxx/xx", "yyy.yyy.yyy.yyy/yy"] # 预处理得到区间 ip_ranges = [cidr_to_range(cidr) for cidr in target_cidrs] # 假设你的100万IP存在这个列表里 raw_ips = ["1.0.0.1", "2.3.4.5", ...] filtered_ips = [] for ip in raw_ips: ip_int = ip_to_int(ip) # 遍历区间,找到匹配就停止 for start, end in ip_ranges: if start <= ip_int <= end: filtered_ips.append(ip) break
这个代码跑100万IP,秒级就能出结果,完全不用担心性能问题。
额外优化小技巧
如果你的CIDR之间有重叠或者包含关系,可以先把区间合并(比如把重叠的区间合并成一个大区间),这样能减少每次查询的判断次数——不过3个CIDR的话,这个优化的收益不大,直接遍历反而更简单。
内容的提问来源于stack exchange,提问作者Jonathan Allen Grant
相关产品推荐
相关产品推荐

