我在一个表中有geoip数据,network_start_ip
和network_end_ip
是varbinary(16)
列,结果为INET6_ATON(ip_start/end)
作为值。另外2列是经度和纬度。
CREATE TABLE `ipblocks` (
`network_start_ip` varbinary(16) NOT NULL,
`network_last_ip` varbinary(16) NOT NULL,
`latitude` double NOT NULL,
`longitude` double NOT NULL,
KEY `network_start_ip` (`network_start_ip`),
KEY `network_last_ip` (`network_last_ip`),
KEY `idx_range` (`network_start_ip`,`network_last_ip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
如您所见,我已经创建了3个用于测试的索引。为什么我的查询(非常简单)
SELECT
latitude, longitude
FROM
ipblocks b
WHERE
INET6_ATON('82.207.219.33') BETWEEN b.network_start_ip AND b.network_last_ip
不使用这些索引吗?
该查询大约需要3秒钟的时间,无法在生产环境中使用它。
答案 0 :(得分:2)
这是行不通的,因为引用了两列-确实很难优化。假设没有重叠的IP范围,则可以将查询重构为:
var imageArray = [];
$(document).on("click", ".showPrv", function () {
$("#dropzone").each(function () {
$(".dz-image-preview").each(function () {
$(".dz-image").each(function () {
const src = $(this).find("img").attr("src")
if(imageArray.indexOf(src) < 0)
imageArray.push(src)
});
});
});
});
内部查询应在SELECT b.*
FROM (SELECT b.*
FROM ipblocks b
WHERE b.network_start_ip <= INET6_ATON('82.207.219.33')
ORDER BY b.network_start_ip DESC
LIMIT 1
) b
WHERE INET6_ATON('82.207.219.33') <= network_last_ip;
上使用索引。外部查询仅比较一行,因此不需要任何索引。
或作为:
ipblocks(network_start_ip)
这将使用SELECT b.*
FROM (SELECT b.*
FROM ipblocks b
WHERE b.network_last_ip >= INET6_ATON('82.207.219.33')
ORDER BY b.network_end_ip ASC
LIMIT 1
) b
WHERE network_last_ip <= INET6_ATON('82.207.219.33');
上的索引。 MySQL(我认为MariaDB)在升序排序方面比在降序排序方面做得更好。
答案 1 :(得分:1)
感谢Gordon Linoff,我为自己的问题找到了最佳查询。
SELECT b.* FROM
(SELECT b.* FROM ipblocks b WHERE b.network_start_ip <= INET6_ATON('82.207.219.33')
ORDER BY b.network_start_ip DESC LIMIT 1 )
b WHERE INET6_ATON('82.207.219.33') <= network_last_ip
现在,我们在内部查询中选择比INET6_ATON(82.207.219.33)
小 的块,但我们对它们进行降序排序 ,这使我们能够再次使用LIMIT 1
。
查询响应时间现在为0.002至.004秒。很棒!
答案 2 :(得分:0)
此查询是否给您正确的结果?在搜索整数表示形式时,您的起始IP /结束IP似乎存储为二进制字符串。 我首先要确保network_start_ip和network_last_ip是IP地址的整数表示形式的无符号INT字段。假设您仅使用IPv4:
CREATE TABLE ipblocks_int AS
SELECT
INET_ATON(network_start_ip) as network_start_ip,
INET_ATON(network_last_ip) as network_last_ip,
latitude,
longitude
FROM ipblocks
然后使用(network_start_ip,network_last_ip)作为主键。
答案 3 :(得分:0)
这是一个棘手的问题。没有简单的解决方案。
之所以艰难,是因为它有效
start <= 123 AND
last >= 123
无论可用什么索引,Optimizer都将与其中一个或另一个一起使用。使用INDEX(start, ...)
,它将选择start <= 123
,它将扫描索引的第一部分。其他条款也是如此。其中一个扫描的索引大于索引的一半,另一个扫描的索引较少,但不足以值得使用索引。将其移至PRIMARY KEY
会在某些情况下有所帮助,但几乎不值得付出任何努力。
最重要的是,不管您使用INDEX
还是PRIMARY KEY
的方式做什么,大多数 IP常数将导致查询超过1.5秒。 >
您的起始/最后IP范围是否重叠?如果是这样,那就增加了复杂性。特别是,重叠可能会使Gordon的LIMIT 1
失效。
我的解决方案涉及要求不重叠的区域。 IP上的任何空白都需要IP的“无主”范围。这是因为只有一个start_ip。 last_ip小于表中下一项的开始。请参阅http://mysql.rjweb.org/doc.php/ipranges(其中包括用于IPv4和IPv6的代码。)
同时,经度/经度的DOUBLE
太高了:http://mysql.rjweb.org/doc.php/latlng#representation_choices