有没有办法直接从 SELECT 查询中将 IP 与 IP+CIDR 进行匹配?
就像是
SELECT COUNT(*) AS c FROM BANS WHERE typeid=6 AND (SELECT ipaddr,cidr FROM BANS) MATCH AGAINST 'this_ip';
因此,您不必先从数据库中获取所有记录,然后再匹配它们一一。
如果c> 0 然后匹配。
BANS表:
id int auto incr PK
typeid TINYINT (1=hostname, 4=ipv4, 6=ipv6)
ipaddr BINARY(128)
cidr INT
host VARCHAR(255)
DB:
查询时已知MySQL 5 IP和IPv类型(4或6)。
例如,二进制格式的 IP 为 ::1
禁止 IP 为例如 ::1/64
Something like
SELECT COUNT(*) AS c FROM BANS WHERE typeid=6 AND (SELECT ipaddr,cidr FROM BANS) MATCH AGAINST 'this_ip';
So you don't first fetch all records from DB and then match them one-by one.
If c > 0 then were matched.
BANS table:
id int auto incr PK
typeid TINYINT (1=hostname, 4=ipv4, 6=ipv6)
ipaddr BINARY(128)
cidr INT
host VARCHAR(255)
DB: MySQL 5
IP and IPv type (4 or 6) is known when querying.
IP is for example ::1 in binary format
BANNED IP is for example ::1/64
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
请记住,IP 不是文本地址,而是数字 ID。 我有类似的情况(我们正在进行地理IP查找),如果您将所有IP地址存储为整数(例如,我的IP地址是192.115.22.33,因此它存储为3228767777),那么您可以查找IP通过使用右移运算符可以轻松实现。
所有这些类型的查找的缺点是您无法从索引中受益,并且每次进行查找时都必须进行全表扫描。 上述方案可以通过存储 CIDR 网络的网络 IP 地址(范围的开头)和广播地址(范围的结尾)来改进,因此例如要存储 192.168.1.0/24,您可以存储两个列:
然后您可以简单地进行匹配。
这将允许您将 CIDR 网络存储在数据库中,并利用快速数字索引快速有效地将它们与 IP 地址进行匹配。
下面讨论的注释:
MySQL 5.0 包含一个名为“索引合并相交" 可以加速此类查询(并避免全表扫描),只要:
(network,broadcast)
。COUNT(*)
是这样,但对于SELECT * ... LIMIT 1
则不然。MySQL 5.6 包含一个称为 MRR 的优化,它也可以加速全行检索,但这超出了本答案的范围。
Remember that IPs are not a textual address, but a numeric ID. I have a similar situation (we're doing geo-ip lookups), and if you store all your IP addresses as integers (for example, my IP address is 192.115.22.33 so it is stored as 3228767777), then you can lookup IPs easily by using right shift operators.
The downside of all these types of lookups is that you can't benefit from indexes and you have to do a full table scan whenever you do a lookup. The above scheme can be improved by storing both the network IP address of the CIDR network (the beginning of the range) and the broadcast address (the end of the range), so for example to store 192.168.1.0/24 you can store two columns:
And then you can to match it you simply do
This would let you store CIDR networks in the database and match them against IP addresses quickly and efficiently by taking advantage of quick numeric indexes.
Note from discussion below:
MySQL 5.0 includes a ranged query optimization called "index merge intersect" which allows to speed up such queries (and avoid full table scans), as long as:
(network, broadcast)
.COUNT(*)
, but is not true forSELECT * ... LIMIT 1
.MySQL 5.6 includes an optimization called MRR which would also speed up full row retrieval, but that is out of scope of this answer.
IPv4 地址、网络地址和网络掩码都是 UINT32 数字,并以人类可读的形式呈现为“点分四组”。 在检查地址是否位于给定网络空间(网络/网络掩码)中时,内核中的路由表代码执行非常快速的按位 AND 比较。 这里的技巧是将点分四组的 IP 地址、网络地址和网络掩码存储在表中作为 UINT32,然后执行相同的 32 位按位 AND 进行匹配。 例如
IPv4 addresses, network addresses and netmasks are all UINT32 numbers and are presented in human-readable form as "dotted-quads". The routing table code in the kernel performs a very fast bit-wise AND comparison when checking if an address is in a given network space (network/netmask). The trick here is to store the dotted-quad IP addresses, network addresses and netmasks in your tables as UINT32, and then perform the same 32-bit bit-wise AND for your matching. eg
对于
IPv4
,您可以使用:For
IPv4
, you can use:生成整数 IP 地址范围
以下示例使用 PostgreSQL:
Generating IP Address Ranges as Integers
The following example is using PostgreSQL:
嗯。 您可以构建一个 cidr 掩码表,加入它,然后将 ip 和(MySQL 中的
&
)与带有禁止块 ip 地址的掩码进行比较。 那会达到你想要的效果吗?如果您不想构建掩码表,可以将掩码计算为
-1 << (x-cidr)
,具体取决于x = 64
或32
。Hmmm. You could build a table of the cidr masks, join it, and then compare the ip anded (
&
in MySQL) with the mask with the ban block ipaddress. Would that do what you want?If you don't want to build a mask table, you could compute the mask as
-1 << (x-cidr)
withx = 64
or32
depending.