【发布时间】:2016-04-17 13:15:47
【问题描述】:
我有一个包含 1500 万条记录的表,其中包含姓名、电子邮件地址和 IP。我需要使用 IP 地址使用国家/地区代码更新同一张表中的另一列。我下载了一个小型数据库(ip2location lite - https://lite.ip2location.com/),其中包含所有 IP 范围和相关国家/地区。 ip2location 表的结构如下;
CREATE TABLE `ip2location_db1` (
`ip_from` int(10) unsigned DEFAULT NULL,
`ip_to` int(10) unsigned DEFAULT NULL,
`country_code` char(2) COLLATE utf8_bin DEFAULT NULL,
`country_name` varchar(64) COLLATE utf8_bin DEFAULT NULL,
KEY `idx_ip_from` (`ip_from`),
KEY `idx_ip_to` (`ip_to`),
KEY `idx_ip_from_to` (`ip_from`,`ip_to`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin
我正在使用以下函数从 ip 地址中检索国家代码;
CREATE DEFINER=`root`@`localhost` FUNCTION `get_country_code`(
ipAddress varchar(30)
) RETURNS VARCHAR(2)
DETERMINISTIC
BEGIN
DECLARE ipNumber INT UNSIGNED;
DECLARE countryCode varchar(2);
SET ipNumber = SUBSTRING_INDEX(ipAddress, '.', 1) * 16777216;
SET ipNumber = ipNumber + (SUBSTRING_INDEX(SUBSTRING_INDEX(ipAddress, '.', 2 ),'.',-1) * 65536);
SET ipNumber = ipNumber + (SUBSTRING_INDEX(SUBSTRING_INDEX(ipAddress, '.', -2 ),'.',1) * 256);
SET ipNumber = ipNumber + SUBSTRING_INDEX(ipAddress, '.', -1 );
SET countryCode =
(SELECT country_code
FROM ip2location.ip2location_db1
USE INDEX (idx_ip_from_to)
WHERE ipNumber >= ip2location.ip2location_db1.ip_from AND ipNumber <= ip2location.ip2location_db1.ip_to
LIMIT 1);
RETURN countryCode;
END$$
DELIMITER ;
我运行了一个 EXPLAIN 语句,这是输出;
'1', 'SIMPLE', 'ip2location_db1', NULL, 'range', 'idx_ip_from_to', 'idx_ip_from_to', '5', NULL, '1', '33.33', 'Using index condition'
我的问题是对 1000 条记录的查询需要大约 15 秒才能执行,这意味着在所有数据库上运行相同的查询需要 2 天以上才能完成。有没有办法改进这个查询。
PS - 如果我删除 USE INDEX (idx_ip_from_to) 查询需要两倍的时间。你能解释一下原因吗?
另外,我不是数据库专家,所以请耐心等待:)
【问题讨论】:
-
表格是否有重叠范围?如果是这样,你就无法优化它(即使有 Gordon 的建议)。
-
不要将
utf8用于country_code——当你只需要2个字节时它需要6个字节;使用ascii。 -
归一化
country_name;它把桌子弄得乱七八糟。 -
(我向 ip2location.com 发送了关于
country_code的评论。)
标签: mysql optimization