有一个IP地址库,假设有几十万条ip,如何判断某个ip地址是否在这个库中?

70 阅读3分钟

🌟 问题分析

数据规模:几十万条 IP 地址(如 100 万条)。

查询需求:高效判断某个 IP 是否在 IP 库中。

存储方式:考虑 内存占用查询速度

IP 地址格式

• IPv4:x.x.x.x,范围 0.0.0.0 ~ 255.255.255.255(32-bit 整数)。

• IPv6:较复杂,主要讨论 IPv4

🚀 解决方案

✅ 方案 1:哈希表(HashSet / Bloom Filter)

🔹 方法 1.1:使用 HashSet(字典)

思路

  1. 先将 IP 地址转换为整数 存入 HashSet(Python set)。

  2. 查询 IP 时,直接 O(1) 时间复杂度 查找。

IP 转换方式

from ipaddress import ip_address

def ip_to_int(ip):
    return int(ip_address(ip))

ip_set = set()
ip_set.add(ip_to_int("192.168.1.1"))  # 添加 IP

# 查询某个 IP 是否在库中
def is_ip_in_db(ip):
    return ip_to_int(ip) in ip_set

print(is_ip_in_db("192.168.1.1"))  # True
print(is_ip_in_db("8.8.8.8"))      # False

优点

• 查询 O(1) 时间复杂度,非常快。

简单易实现,适用于 数据量不大的情况(如 <100 万条)。

缺点

占用内存较大,100 万个 IPv4 地址大约 20~30MB

🔹 方法 1.2:使用布隆过滤器(Bloom Filter)

适用场景

节省内存,但允许 极少量误判

• 查询速度 O(1) ,适用于 超大规模数据

Python 示例(使用 pybloom 库):

from pybloom_live import BloomFilter
from ipaddress import ip_address

bf = BloomFilter(capacity=1000000, error_rate=0.001)

def ip_to_int(ip):
    return int(ip_address(ip))

# 添加 IP
bf.add(ip_to_int("192.168.1.1"))

# 查询 IP 是否存在
print(ip_to_int("192.168.1.1") in bf)  # True
print(ip_to_int("8.8.8.8") in bf)      # False (极小概率误判)

优点

大幅节省内存(100 万个 IP 仅需几 MB)。

查询速度 O(1)

缺点

允许误判(0.001 的误判率意味着 1000 个查询可能有 1 个误判)。

不能删除元素(标准 Bloom Filter 只能插入,不支持删除)。

✅ 方案 2:前缀树(Trie / Radix Tree)

适用场景

存储 IP 段(如 192.168.0.0/16) ,或者当 IP 数据很多 时节省空间。

适用于黑名单匹配、防火墙规则匹配

思路

  1. 构建前缀树(Trie) ,每个 IP 存储成二进制形式

  2. 查询时,从根节点开始匹配 O(log n) 时间复杂度

示例代码

class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False

class IPTrie:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, ip):
        node = self.root
        for bit in f"{ip:032b}":  # 转换为 32-bit 二进制字符串
            if bit not in node.children:
                node.children[bit] = TrieNode()
            node = node.children[bit]
        node.is_end = True

    def search(self, ip):
        node = self.root
        for bit in f"{ip:032b}":
            if bit not in node.children:
                return False
            node = node.children[bit]
        return node.is_end

trie = IPTrie()
trie.insert(ip_to_int("192.168.1.1"))
print(trie.search(ip_to_int("192.168.1.1")))  # True
print(trie.search(ip_to_int("8.8.8.8")))      # False

优点

查询速度 O(log n)

节省存储空间(合并前缀减少冗余)。

缺点

实现较复杂

构建 Trie 需要时间

✅ 方案 3:位图(BitSet)

适用场景

IP 数据量巨大(千万级别) ,但范围有限(0 ~ 4.2 亿)。

适用于 IP 黑名单、IP 访问限制

思路

  1. 使用 位数组(BitSet) ,每个 IP 占 1 bit

  2. 查询时 O(1) 读取 bit,内存占用极小(4.2 亿 IP 仅需 50MB)。

示例代码

import numpy as np

N = 2**32  # IPv4 地址范围 (0 ~ 4.2亿)
bitset = np.zeros(N // 8, dtype=np.uint8)  # 约 50MB 内存

def add_ip(ip):
    index = ip_to_int(ip)
    bitset[index // 8] |= (1 << (index % 8))

def check_ip(ip):
    index = ip_to_int(ip)
    return (bitset[index // 8] & (1 << (index % 8))) != 0

add_ip("192.168.1.1")
print(check_ip("192.168.1.1"))  # True
print(check_ip("8.8.8.8"))      # False

优点

超高效查询(O(1))

超低内存占用(4.2 亿 IP 仅需 50MB)

缺点

• 只能存 IPv4,IPv6 需要更大空间。

不能存储 IP 段(适合单个 IP 存储)。

🎯 方案对比

💡 结论

百万级 IPHashSet(查询快)

亿级 IP布隆过滤器(省内存)

IP 段匹配Trie(存储前缀)

大规模单个 IPBitSet(存 4.2 亿 IP 仅 50MB)

🔥 结论:一般选择 Bloom Filter 或 BitSet,大规模查询更高效! 🚀