2016-06-09 76 views
0

我有一张拥有大约1300万行的表,并且我的主键是十六进制值VARBINARY(16)MySQL/MariaDB按分数排序排列

我用的是后续查询来获取我的结果:

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

什么是您的表格架构? – Alex

+0

你是怎么确定你的查询时间应该是“0,02秒或更少”? –

+0

@BobJarvis - 系统最低要求。我需要这样做才能有更好的表现 – Guhh

回答

0

这是相当倒模式,需要更多的优化。

首先,您必须将所有认为可行的所有内容直接引用并分组。索引国家代码本身将减少选择时间,即

SELECT * FROM dbip 
    WHERE 
    country="BR" 
    AND addr_type = 4 
    AND ip_start <= INET6_ATON('XXX.XX.XX.') 
    ORDER BY ip_start DESC; 

将缩小select到131270的行。 add_type索引将进一步削减搜索。

其次,规范化那些重复性的价值,否则,你最终会无法保持数据的完整性(想象一些存储城市名称的错字)。另外,将大量数据加载到DBMS中会占用大量资源。是的,留下加入这些数据是“不方便的”,从长远来看它会为您节省。如果没有标准化,每行将占用至少530字节,如果结果返回50k行,那么530bytes x 50k =聚集前容易25MB(按排序顺序排列前)

如果没有LIMIT 1,这个查询肯定会占用更多分钟,因为它试图遍历整个1300万行,将大量数据聚合到内存。

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

相反,如果你加上`和国家= 'BR',那么你需要:

INDEX(country, addr_type, ip_start) 

countryaddr_type可以以任何顺序,但ip_start必须是最后一个。)

由于LENGTH(INET6_ATON(...))的值为4或16,因此可以删除类型,具体取决于它们是IPv4还是IPv6。

我不认为你原来的查询不应该花那么长时间。请进行进一步调试:

FLUSH STATUS; 
SELECT ...; 
SHOW SESSION STATUS LIKE 'Handler%';