我在一个表中有geoip数据, network_start_ip
以及 network_end_ip
是 varbinary(16)
结果为的列 INET6_ATON(ip_start/end)
作为价值观。另外两列是纬度和经度。
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秒钟,这对于在生产中使用它来说太长了。
4条答案
按热度按时间uubf1zoe1#
它不起作用,因为有两个列被引用——这确实很难优化。假设没有重叠的ip范围,可以将查询重组为:
内部查询应在上使用索引
ipblocks(network_start_ip)
. 外部查询只比较一行,因此不需要任何索引。或作为:
这将在上使用索引
(network_last_ip)
. mysql(我认为mariadb)使用升序排序比降序排序做得更好。bnl4lu3b2#
多亏了gordon linoff,我找到了问题的最佳答案。
现在我们选择小于
INET6_ATON(82.207.219.33)
在内部查询中,但我们将它们按降序排列,这使我们能够使用LIMIT 1
再一次。查询响应时间现在是.002到.004秒。太好了!
ftf50wuq3#
这个查询给你的结果正确吗?在搜索整数表示时,开始/结束IP似乎存储为二进制字符串。我首先要确保network\u start\u ip和network\u last\u ip是无符号int字段,用整数表示ip地址。这是假设您仅使用ipv4:
然后使用(network\u start\u ip,network\u last\u ip)作为主键。
s8vozzvw4#
这是个棘手的问题。没有简单的解决办法。
它之所以艰难,是因为它是有效的
不管有什么索引可用,优化器都将使用其中一个索引。与
INDEX(start, ...)
,它会选择start <= 123
它将扫描索引的第一部分。另一条也一样。其中一个扫描了超过一半的索引,另一个扫描的更少——但还不足以让索引值得使用。把它移到PRIMARY KEY
在某些情况下会有所帮助,但这几乎不值得努力。底线,不管你做了什么
INDEX
或者PRIMARY KEY
,大多数ip常量将导致查询超过1.5秒。开始/结束ip范围重叠吗?如果是这样,那就增加了复杂性。特别是,重叠可能会使戈登的理论失效
LIMIT 1
.我的解决方案需要不重叠的区域。IP中的任何缺口都需要“无主”IP范围。这是因为只有一个起始ip;最后一个\u ip小于表中下一项的开头。看到了吗http://mysql.rjweb.org/doc.php/ipranges (它包括ipv4和ipv6的代码。)
与此同时,
DOUBLE
对于lat/lng是过度杀戮:http://mysql.rjweb.org/doc.php/latlng#representation_choices