标签:mysql range-types index datatypes
确定IP是否包含在CIDR块中的最快方法是什么?
目前,每当我存储一个CIDR地址时,我还会创建两列来启动和结束IP地址.开始和结束IP地址被索引.如果我想查看哪个网络包含一个地址,那么我查看start_ip和end_ip之间的ip,这看起来不太理想.
它发生在我身上我可以存储正确的移位号码,并且可以匹配类似移位的IP地址(在@cidr的情况下为660510)…
select @cidr, inet_aton(substring_index(@cidr,'/',1))>>(32-substring_index(@cidr,'/',-1));
+---------------+-----------------------------------------------------------------------------+
| @cidr | inet_aton(substring_index(@cidr,'/',1))>>(32-substring_index(@cidr,'/',-1)) |
+---------------+-----------------------------------------------------------------------------+
| 10.20.30.0/24 | 660510 |
+---------------+-----------------------------------------------------------------------------+
1 row in set (0.00 sec)
set @ip:='10.20.30.40';
Query OK, 0 rows affected (0.00 sec)
select @ip, inet_aton(@ip)>>(32-substring_index(@cidr,'/',-1));
+-------------+----------------------------------------------------+
| @ip | inet_aton(@ip)>>(32-substring_index(@cidr,'/',-1)) |
+-------------+----------------------------------------------------+
| 10.20.30.40 | 660510 |
+-------------+----------------------------------------------------+
1 row in set (0.00 sec)
为了以索引方式从中受益,我需要知道子网掩码(要移位的位数).否则,我将系统地比较位移(即,为每个可能的网络掩码(从0到24位)盲目移位).
我有其他优化来源,但优化在http://lite.ip2location.com/database/ip-asn发现的IP2Location™LITE IP-ASN数据库将是一个概念证明.
桌子…
CREATE TABLE `ip2loc_asn` (
`asn` bigint(20) DEFAULT NULL,
`cidr` varchar(50) DEFAULT NULL,
`start_ip` bigint(20) DEFAULT NULL,
`end_ip` bigint(20) DEFAULT NULL,
`name` varchar(250) DEFAULT NULL,
KEY `ip2locasn_startip_endip` (`start_ip`,`end_ip`),
KEY `asn` (`asn`),
KEY `cidr` (`cidr`)
) ENGINE=MyISAM; -- table is recreated monthly, MyISAM is the perfect engine
样本数据…
select * from ip2loc_asn limit 10;
+-------+--------------+----------+----------+-------------------------------+
| asn | cidr | start_ip | end_ip | name |
+-------+--------------+----------+----------+-------------------------------+
| 56203 | 1.0.4.0/24 | 16778240 | 16778495 | Big Red Group |
| 56203 | 1.0.5.0/24 | 16778496 | 16778751 | Big Red Group |
| 56203 | 1.0.6.0/24 | 16778752 | 16779007 | Big Red Group |
| 38803 | 1.0.7.0/24 | 16779008 | 16779263 | Goldenit Pty ltd Australia, A |
| 18144 | 1.0.64.0/18 | 16793600 | 16809983 | Energia Communications,Inc. |
| 9737 | 1.0.128.0/17 | 16809984 | 16842751 | TOT Public Company Limited |
| 9737 | 1.0.128.0/18 | 16809984 | 16826367 | TOT Public Company Limited |
| 9737 | 1.0.128.0/19 | 16809984 | 16818175 | TOT Public Company Limited |
| 23969 | 1.0.128.0/24 | 16809984 | 16810239 | TOT Public Company Limited |
| 23969 | 1.0.129.0/24 | 16810240 | 16810495 | TOT Public Company Limited |
+-------+--------------+----------+----------+-------------------------------+
10 rows in set (0.00 sec)
网络掩码范围从8到32位……
select min(substring_index(cidr,'/',-1)+0), max(substring_index(cidr,'/',-1)+0) from ip2loc_asn;
+-------------------------------------+-------------------------------------+
| min(substring_index(cidr,'/',-1)+0) | max(substring_index(cidr,'/',-1)+0) |
+-------------------------------------+-------------------------------------+
| 8 | 32 |
+-------------------------------------+-------------------------------------+
1 row in set (0.33 sec)
select * from ip2loc_asn where cidr like '%/8' limit 1;
+------+-----------+----------+----------+------------------------------+
| asn | cidr | start_ip | end_ip | name |
+------+-----------+----------+----------+------------------------------+
| 3356 | 4.0.0.0/8 | 67108864 | 83886079 | Level 3 Communications, Inc. |
+------+-----------+----------+----------+------------------------------+
1 row in set (0.00 sec)
select * from ip2loc_asn where cidr like '%/32' limit 1;
+-------+---------------+-----------+-----------+------+
| asn | cidr | start_ip | end_ip | name |
+-------+---------------+-----------+-----------+------+
| 51964 | 57.72.27.1/32 | 961026817 | 961026817 | |
+-------+---------------+-----------+-----------+------+
1 row in set (0.02 sec)
目前的执行计划……
explain select * from ip2loc_asn where inet_aton('10.20.30.40') between start_ip and end_ip;
+----+-------------+------------+-------+--------------------------+--------------------------+---------+------+-------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+--------------------------+--------------------------+---------+------+-------+-----------------------+
| 1 | SIMPLE | ip2loc_asn | range | ip2loc_asn_startip_endip | ip2loc_asn_startip_endip | 9 | NULL | 10006 | Using index condition |
+----+-------------+------------+-------+--------------------------+--------------------------+---------+------+-------+-----------------------+
1 row in set (0.00 sec)
我的笨拙尝试……
mysql to3_reference> alter table ip2loc_asn add column shifted_netmask int(10) unsigned;
Query OK, 626695 rows affected (4.06 sec)
Records: 626695 Duplicates: 0 Warnings: 0
mysql to3_reference> update ip2loc_asn set shifted_netmask = start_ip>>(32-substring_index(cidr,'/',-1));
Query OK, 626695 rows affected (5.98 sec)
Rows matched: 626695 Changed: 626695 Warnings: 0
mysql to3_reference> alter table ip2loc_asn add key ip2loc_asn_shiftednetmask (shifted_netmask);
Query OK, 626695 rows affected (5.83 sec)
Records: 626695 Duplicates: 0 Warnings: 0
旧方式:
select * from ip2loc_asn where inet_aton('8.8.8.0') between start_ip and end_ip;
+-------+------------+-----------------+--------------+-----------+-----------+------------------------------+
| asn | cidr | shifted_netmask | netmask_bits | start_ip | end_ip | name |
+-------+------------+-----------------+--------------+-----------+-----------+------------------------------+
| 3356 | 8.0.0.0/9 | 16 | 9 | 134217728 | 142606335 | Level 3 Communications, Inc. |
| 3356 | 8.0.0.0/8 | 8 | 8 | 134217728 | 150994943 | Level 3 Communications, Inc. |
| 15169 | 8.8.8.0/24 | 526344 | 24 | 134744064 | 134744319 | Google Inc. |
+-------+------------+-----------------+--------------+-----------+-----------+- -----------------------------+
3 rows in set (0.00 sec)
使用shifted_netmask的方法(不合需要 – 我正在进行全表扫描以发现网络掩码中的位数)…
select * from ip2loc_asn where shifted_netmask = inet_aton('8.8.8.0')>>32-netmask_bits;
+-------+------------+-----------------+--------------+-----------+-----------+------------------------------+
| asn | cidr | shifted_netmask | netmask_bits | start_ip | end_ip | name |
+-------+------------+-----------------+--------------+-----------+-----------+------------------------------+
| 3356 | 8.0.0.0/8 | 8 | 8 | 134217728 | 150994943 | Level 3 Communications, Inc. |
| 3356 | 8.0.0.0/9 | 16 | 9 | 134217728 | 142606335 | Level 3 Communications, Inc. |
| 15169 | 8.8.8.0/24 | 526344 | 24 | 134744064 | 134744319 | Google Inc. |
+-------+------------+-----------------+--------------+-----------+-----------+------------------------------+
3 rows in set (0.64 sec)
所需的方法类似于上一个查询减去网络掩码位的扫描.
解决方法:
主要问题是优化器不知道是否有一个匹配的起始端对或批次.因此,任何尝试优化的尝试都会遇到表扫描,或者至少是大范围扫描.
你必须从哪开始? IP地址?还是CIDR阻止?我问这是因为我们可能需要重新排列您开始使用的数据,以便有效地查找其他数据.
在this中,我将解释如何构建和维护所有2 ^ 32(或IPv6等效)IP地址的表.它仅使用start_ip列,并从下一行推断出end_ip.这意味着所有未分配的IP范围必须在表中有一行. (这不是一个很大的负担,最多是行数加倍.)这样,几乎所有的操作基本上都是O(1) – 就像WHERE ip> = start_ip ORDER BY start_ip DESC LIMIT 1得到的回答“立即”.没有表扫描,没有范围扫描;没有什么比’点查询'(有效)更糟糕了.请注意,它甚至不需要测试end_ip.警告:不处理重叠范围.某些应用程序(可能不是您的应用程序)可以调整为不需要重叠.
如何适应CIDR?一种方法是将您的CIDR表转换为我的变体.你熟悉如何做到这一点;主要区别在于缺少end_ip和增加“非拥有”范围.因此,如果您“从”CIDR开始并需要查找IP,这是一个可能的答案.
标签:mysql,range-types,index,datatypes 来源: https://codeday.me/bug/20190806/1602890.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。