优化 MySQL 查询以进行整数范围搜索

Posted

技术标签:

【中文标题】优化 MySQL 查询以进行整数范围搜索【英文标题】:Optimizing MySQL query for integer range search 【发布时间】:2013-10-18 18:24:14 【问题描述】:

我在一个表中有超过 170 万条记录,其中包含 ip 地址范围(开始和结束)以及主键和相应的详细信息。

表结构是

mysql> desc csv;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| begin   | bigint(20)  | NO   | PRI | 0       |       |
| end     | bigint(20)  | NO   | PRI | 0       |       |
| code    | char(2)     | YES  |     | NULL    |       |
| country | varchar(50) | YES  |     | NULL    |       |
| city    | varchar(50) | YES  |     | NULL    |       |
| area    | varchar(50) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+

由于主键中的索引,当要进行精确匹配时,搜索速度很快

mysql> SELECT * FROM csv WHERE begin=3338456576;
+------------+------------+------+---------------+----------+---------------+
| begin      | end        | code | country       | city     | area          |
+------------+------------+------+---------------+----------+---------------+
| 3338456576 | 3338456831 | US   | UNITED STATES | NEW YORK | NEW YORK CITY |
+------------+------------+------+---------------+----------+---------------+
1 row in set (0.03 sec)

但是当我尝试在一个范围内搜索时,它需要更长的时间。

mysql> SELECT * FROM csv WHERE begin<3338456592 AND end>3338456592;
+------------+------------+------+---------------+----------+---------------+
| begin      | end        | code | country       | city     | area          |
+------------+------------+------+---------------+----------+---------------+
| 3338456576 | 3338456831 | US   | UNITED STATES | NEW YORK | NEW YORK CITY |
+------------+------------+------+---------------+----------+---------------+
1 row in set (1.59 sec)

有什么方法可以优化我的查询以在一个范围内搜索 IP 地址?

编辑

建表语句

CREATE TABLE `csv` (
  `begin` bigint(20) NOT NULL DEFAULT '0',
  `end` bigint(20) NOT NULL DEFAULT '0',
  `code` char(2) DEFAULT NULL,
  `country` varchar(50) DEFAULT NULL,
  `city` varchar(50) DEFAULT NULL,
  `area` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`begin`,`end`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

【问题讨论】:

能否也贴出create table语句的输出?还有一些解释语句输出? 查询返回多少行?您能否发布“SELECT count(*) FROM csv WHERE begin3338456592;”的结果? ? 真的只有一排吗?这意味着所有剩余的行都满足相反的条件begin &gt; 3338456592 AND end &lt; 3338456592。假设begin &lt;= end(我的假设是真的吗?),那么170万条记录肯定有begin = end = 3338456592,只有一行有begin &lt;&gt; 3338456592 AND end &lt;&gt; 3338456592,对吗? @kordirko 看看this link showing 20 rows from my table. 看这个:***.com/questions/7955382/… 【参考方案1】:

如果 IP 范围不重叠,因此查询永远不会返回超过 1 行,您可以使用此:

SELECT q.*
FROM 
  ( SELECT csv.* 
    FROM csv
    WHERE csv.begin < 3338456592 
    ORDER BY csv.begin DESC
    LIMIT 1
  ) AS q
WHERE 3338456592 < q.end ;

不需要添加索引。将使用主索引。

【讨论】:

太棒了!!在 0.06 秒内完成。谢谢 删除最后一行也可以。结果是0.0 seconds 保留最后一行。否则你可能会得到误报。【参考方案2】:

如果范围重叠,您应该:

将 ip 范围定义为 LineString 列 在该列上定义空间索引 使用几何“包含”查询

在Efficient data model for range queries中查看更多信息

【讨论】:

【参考方案3】:

SELECT begin, end, code, country, city, area FROM csv WHERE begin &lt;&gt; 3338456592 HAVING begin NOT BETWEEN MIN(begin) AND MAX(end) 的得分是多少?

UPD: 这是我的表结构版本。

CREATE TABLE `csv` (
  `begin` INT(10) NOT NULL DEFAULT '0',
  `end` INT(10) NOT NULL DEFAULT '0',
  `code` char(2) DEFAULT NULL,
  `country` varchar(50) DEFAULT NULL,
  `city` varchar(45) DEFAULT NULL,
  `area` varchar(40) DEFAULT NULL,
  KEY `combined` (`begin`,`end`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

我认为使用国家和代码作为 ENUM 会更快。

【讨论】:

对我来说这似乎是一个不可能的 SQL...begin = 3338456592 MIN(begin) = 0 MAX(begin) = 4294967295 和 begin 3338456592 不在这些值之间? @GauravSharma 实际上我们无法尝试优化您的表结构。什么返回SELECT * FROM csv PROCEDURE ANALYSE(); @GauravSharma 你能把表引擎改成myisam吗?它比innodb@GauravSharma emm UNSIGNED INT(10) 怎么样? UNSIGNED INT 确实可以保持最大值关闭 4294967295 IP 255.255.255.255 你可以删除 (10) 这仅适用于 zerofill...我有时会看到这个 INT(11) without zerofill 不知道也许程序员认为 int 可以通过这种方式保存更大的值......

以上是关于优化 MySQL 查询以进行整数范围搜索的主要内容,如果未能解决你的问题,请参考以下文章

优化 mysql 查询以减少搜索的行数

MySql 模糊查询范围查询

优化 MySQL 全文搜索查询?

优化数据库搜索查询

如何获得mysql随机整数范围?

MYSQL列中的数据以逗号隔开,如何查询?