我有一个大约有1300万行的表,而我的主键是十六进制值VARBINARY(16)
。
我使用以下查询来获取结果:
SELECT *
FROM dbip
WHERE ip_start <= INET6_ATON('XXX.XX.XX.XX')
AND addr_type = 4
ORDER BY ip_start DESC
LIMIT 1;
但是这个查询需要大约0.1秒,它应该是大约0.02秒或更短。
99%的带宽在巴西,只有131.270行,我的数据库中有country
列。那么在桌面的其余部分之后,我该怎样做才能首先在巴西搜索此IP?你觉得这样我会获得几毫秒吗?
我的表:
CREATE TABLE `dbip`
(
`addr_type` TINYINT(1) NOT NULL,
`ip_start` VARBINARY(16) NOT NULL,
`ip_end` VARBINARY(16) NOT NULL,
`country` CHAR(2) NOT NULL,
`stateprov` VARCHAR(80) NOT NULL,
`city` VARCHAR(80) NOT NULL,
`latitude` FLOAT NOT NULL,
`longitude` FLOAT NOT NULL,
`timezone_offset` FLOAT NOT NULL,
`timezone_name` VARCHAR(64) NOT NULL,
`isp_name` VARCHAR(128) NOT NULL,
`connection_type` VARCHAR(8) NULL DEFAULT NULL,
`organization_name` VARCHAR(128) NOT NULL,
PRIMARY KEY (`ip_start`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
这是一个实时数据,因此它必须很快。我也接受改变我的数据库的建议,我尝试了一些内存存储,但它不接受二进制值作为索引,或者他们不允许使用<=
命令
答案 0 :(得分:0)
这是非常倾向的架构,需要更多优化。
首先,您必须指出所有可以立即参考和分组的东西。索引国家代码本身将减少选择时间,即
SELECT * FROM dbip
WHERE
country="BR"
AND addr_type = 4
AND ip_start <= INET6_ATON('XXX.XX.XX.')
ORDER BY ip_start DESC;
将选择范围缩小到131270行。 add_type索引将进一步削减搜索。
其次,将这些重复值标准化,否则,您将最终难以维护数据完整性(想象一些用拼写错误存储城市名称)。此外,将大量数据加载到DBMS中需要大量资源。是的,它不方便&#34;要离开加入这些数据,从长远来看,它将为您节省时间。如果没有规范化,每行将占用至少530个字节,如果结果返回50k行,则530bytes x 50k =在聚合之前容易25MB(在排序之前排序)
如果没有LIMIT 1
,这个查询肯定会花费几分钟,因为它试图遍历整个1300万行,将大量数据聚合到内存中。
答案 1 :(得分:0)
由于几乎所有条目都是针对巴西的,我们可能会忽略country
。
WHERE addr_type = 4
AND ip_start <= INET6_ATON('XXX.XX.XX.')
ORDER BY ip_start DESC;
需要
INDEX(addr_type, ip_start)
特别以“=常数”开头,然后转到“范围”。 ORDER BY
应该在同一优化中使用。
相反,如果您添加“AND country ='BR',则需要:
INDEX(country, addr_type, ip_start)
(country
和addr_type
可以按任意顺序排列,但ip_start
必须是最后一个。)
你可以摆脱类型,因为LENGTH(INET6_ATON(...))
是4或16,取决于它是分别是IPv4还是IPv6。
我不认为您的原始查询不应该花那么长时间。请执行此操作以进行进一步调试:
FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';