查看 IP 地址是否在子网列表中的有效方法
Efficient way to see if an IP address is in a list of subnets
我基本上有一个 2 列 table,IP 子网作为 key/index,描述作为值。例如:
10.20.30.0/30 "Subnet 1"
我需要编写一个 REST 服务,它将 return 包含给定 IP 地址的子网的描述,或者如果 IP 地址匹配多个子网,则为子网列表。
起初,我以为我会简单地使用后端数据库 (Postgres) 并扩展所有子网,因为我不需要处理大量数据。所以上面的例子将扩展为:
10.20.30.0 "Subnet 1"
10.20.30.1 "Subnet 1"
10.20.30.2 "Subnet 1"
10.20.30.3 "Subnet 1"
这在存储方面效率低下,尤其是当子网变大时。然而,它做起来又快又容易,而且现有的数据库有非常有效的方法来查找 IP 地址作为索引,所以这是我的第一个想法。我最关心的是查找效率,因为我的数据库中将有大约 100 万个条目。
但是,我发现我对IPv6也有要求,涉及的子网很大。这意味着我不再有扩展所有子网的选项。
在我开始编写自定义 REST 之前 API 我想知道使用子网作为索引检查 IP 地址的 POSTGRES 查询是否有效。检查不是微不足道的,我不知道如何在内部完成以保持效率。
有谁知道 POSTGRES 如何检查按子网索引的 table 中的 IP 地址?
编辑:这是我正在谈论的在 POSTGRES 中查找 IP 地址的示例(使用在线 https://extendsclass.com/postgresql-online.html)
drop table ipdesc;
create table ipdesc (addr inet, category varchar(20));
insert into ipdesc (addr, category) values ('10.10.10.0/24', 'tens');
insert into ipdesc (addr, category) values ('20.20.20.0/24', 'twenties');
insert into ipdesc (addr, category) values ('50.50.50.0/24', 'fifties');
insert into ipdesc (addr, category) values ('50.50.50.0/30', 'sub-fifty');
select * from ipdesc where inet '50.50.50.1' << addr;
结果:
addr category
---- --------
50.50.50.0/24 fifties
50.50.50.0/30 sub-fifty
谢谢!
由于 CIDR 是连续的,您可以添加两列:范围内的最小和最大 IP 地址,并在这些列上创建索引。我写了一个 gist here 如果你想看到整个东西并玩它。
基本上:
create table mod_ipdesc
as select addr, category,
inet(host(network(addr))) as amin,
inet(host(broadcast(addr))) as amax
from ipdesc;
create index mod_ipdesc_addr on mod_ipdesc(amin, amax);
然后:
select *
from test a
left outer join mod_ipdesc b
on (a.ip between b.amin and b.amax);
根据您的 table 定义,加上带有几个 ip
值(如 inet
)的测试 table,我们得到:
ip
addr
category
amin
amax
168.192.1.10
null
null
null
null
10.10.10.20
10.10.10.0/24
tens
10.10.10.0
10.10.10.255
50.50.50.1
50.50.50.0/24
fifties
50.50.50.0
50.50.50.255
50.50.50.1
50.50.50.0/30
sub-fifty
50.50.50.0
50.50.50.3
50.50.50.10
50.50.50.0/24
fifties
50.50.50.0
50.50.50.255
更新:查询计划比较
一开始我无法用这么小的 tables 判断是否使用了索引(在小的 tables 上,没有使用索引,但是它可能只是 Postgresql 确定扫描速度更快的情况)。 [是](对于更大的 tables)。
第二个问题是:Postgres 是否有某种魔力,可以使用 inet
列上的索引来进行 <<
类型的查询? [否](至少据我所知,使用的是 Postgres 9.6)。
我创建了 another gist,每个 table 有 1000 个条目。
可以看到使用了amin
、amax
索引(下面第二个方案),但是直接在addr
上的索引没有(下面第一个方案:全table扫描):
explain
select * from test a
left outer join ipdesc b
on (b.addr >> a.ip);
| QUERY PLAN |
| :---------------------------------------------------------------------- |
| Nested Loop Left Join (cost=0.00..20442.10 rows=6800 width=68) |
| Join Filter: (b.addr >> a.ip) |
| -> Seq Scan on test a (cost=0.00..23.60 rows=1360 width=32) |
| -> Materialize (cost=0.00..21.00 rows=1000 width=36) |
| -> Seq Scan on ipdesc b (cost=0.00..16.00 rows=1000 width=36) |
explain
select * from test a
left outer join mod_ipdesc b
on (a.ip between b.amin and b.amax);
| QUERY PLAN |
| :------------------------------------------------------------------------------------------- |
| Nested Loop Left Join (cost=0.28..8001.60 rows=151111 width=132) |
| -> Seq Scan on test a (cost=0.00..23.60 rows=1360 width=32) |
| -> Index Scan using mod_ipdesc_addr on mod_ipdesc b (cost=0.28..4.76 rows=111 width=100) |
| Index Cond: ((a.ip >= amin) AND (a.ip <= amax)) |
我基本上有一个 2 列 table,IP 子网作为 key/index,描述作为值。例如:
10.20.30.0/30 "Subnet 1"
我需要编写一个 REST 服务,它将 return 包含给定 IP 地址的子网的描述,或者如果 IP 地址匹配多个子网,则为子网列表。
起初,我以为我会简单地使用后端数据库 (Postgres) 并扩展所有子网,因为我不需要处理大量数据。所以上面的例子将扩展为:
10.20.30.0 "Subnet 1"
10.20.30.1 "Subnet 1"
10.20.30.2 "Subnet 1"
10.20.30.3 "Subnet 1"
这在存储方面效率低下,尤其是当子网变大时。然而,它做起来又快又容易,而且现有的数据库有非常有效的方法来查找 IP 地址作为索引,所以这是我的第一个想法。我最关心的是查找效率,因为我的数据库中将有大约 100 万个条目。
但是,我发现我对IPv6也有要求,涉及的子网很大。这意味着我不再有扩展所有子网的选项。
在我开始编写自定义 REST 之前 API 我想知道使用子网作为索引检查 IP 地址的 POSTGRES 查询是否有效。检查不是微不足道的,我不知道如何在内部完成以保持效率。
有谁知道 POSTGRES 如何检查按子网索引的 table 中的 IP 地址?
编辑:这是我正在谈论的在 POSTGRES 中查找 IP 地址的示例(使用在线 https://extendsclass.com/postgresql-online.html)
drop table ipdesc;
create table ipdesc (addr inet, category varchar(20));
insert into ipdesc (addr, category) values ('10.10.10.0/24', 'tens');
insert into ipdesc (addr, category) values ('20.20.20.0/24', 'twenties');
insert into ipdesc (addr, category) values ('50.50.50.0/24', 'fifties');
insert into ipdesc (addr, category) values ('50.50.50.0/30', 'sub-fifty');
select * from ipdesc where inet '50.50.50.1' << addr;
结果:
addr category
---- --------
50.50.50.0/24 fifties
50.50.50.0/30 sub-fifty
谢谢!
由于 CIDR 是连续的,您可以添加两列:范围内的最小和最大 IP 地址,并在这些列上创建索引。我写了一个 gist here 如果你想看到整个东西并玩它。
基本上:
create table mod_ipdesc
as select addr, category,
inet(host(network(addr))) as amin,
inet(host(broadcast(addr))) as amax
from ipdesc;
create index mod_ipdesc_addr on mod_ipdesc(amin, amax);
然后:
select *
from test a
left outer join mod_ipdesc b
on (a.ip between b.amin and b.amax);
根据您的 table 定义,加上带有几个 ip
值(如 inet
)的测试 table,我们得到:
ip | addr | category | amin | amax |
---|---|---|---|---|
168.192.1.10 | null | null | null | null |
10.10.10.20 | 10.10.10.0/24 | tens | 10.10.10.0 | 10.10.10.255 |
50.50.50.1 | 50.50.50.0/24 | fifties | 50.50.50.0 | 50.50.50.255 |
50.50.50.1 | 50.50.50.0/30 | sub-fifty | 50.50.50.0 | 50.50.50.3 |
50.50.50.10 | 50.50.50.0/24 | fifties | 50.50.50.0 | 50.50.50.255 |
更新:查询计划比较
一开始我无法用这么小的 tables 判断是否使用了索引(在小的 tables 上,没有使用索引,但是它可能只是 Postgresql 确定扫描速度更快的情况)。 [是](对于更大的 tables)。
第二个问题是:Postgres 是否有某种魔力,可以使用 inet
列上的索引来进行 <<
类型的查询? [否](至少据我所知,使用的是 Postgres 9.6)。
我创建了 another gist,每个 table 有 1000 个条目。
可以看到使用了amin
、amax
索引(下面第二个方案),但是直接在addr
上的索引没有(下面第一个方案:全table扫描):
explain select * from test a left outer join ipdesc b on (b.addr >> a.ip);
| QUERY PLAN | | :---------------------------------------------------------------------- | | Nested Loop Left Join (cost=0.00..20442.10 rows=6800 width=68) | | Join Filter: (b.addr >> a.ip) | | -> Seq Scan on test a (cost=0.00..23.60 rows=1360 width=32) | | -> Materialize (cost=0.00..21.00 rows=1000 width=36) | | -> Seq Scan on ipdesc b (cost=0.00..16.00 rows=1000 width=36) |
explain select * from test a left outer join mod_ipdesc b on (a.ip between b.amin and b.amax);
| QUERY PLAN | | :------------------------------------------------------------------------------------------- | | Nested Loop Left Join (cost=0.28..8001.60 rows=151111 width=132) | | -> Seq Scan on test a (cost=0.00..23.60 rows=1360 width=32) | | -> Index Scan using mod_ipdesc_addr on mod_ipdesc b (cost=0.28..4.76 rows=111 width=100) | | Index Cond: ((a.ip >= amin) AND (a.ip <= amax)) |