MySQL / MariaDB按分数排序

时间:2016-06-09 13:29:18

标签: mysql performance select mariadb

我有一个大约有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
;

这是一个实时数据,因此它必须很快。我也接受改变我的数据库的建议,我尝试了一些内存存储,但它不接受二进制值作为索引,或者他们不允许使用<=命令

2 个答案:

答案 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)

countryaddr_type可以按任意顺序排列,但ip_start必须是最后一个。)

你可以摆脱类型,因为LENGTH(INET6_ATON(...))是4或16,取决于它是分别是IPv4还是IPv6。

我不认为您的原始查询不应该花那么长时间。请执行此操作以进行进一步调试:

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