Mysql 包含350万个条目的数据库表-我们如何提高性能?

Mysql 包含350万个条目的数据库表-我们如何提高性能?,mysql,Mysql,我们有一个MySQL表,有大约350万个IP条目 结构: CREATE TABLE IF NOT EXISTS `geoip_blocks` ( `uid` int(11) NOT NULL auto_increment, `pid` int(11) NOT NULL, `startipnum` int(12) unsigned NOT NULL, `endipnum` int(12) unsigned NOT NULL, `locid` int(11) NOT NULL,

我们有一个MySQL表,有大约350万个IP条目

结构:

CREATE TABLE IF NOT EXISTS `geoip_blocks` (
  `uid` int(11) NOT NULL auto_increment,
  `pid` int(11) NOT NULL,
  `startipnum` int(12) unsigned NOT NULL,
  `endipnum` int(12) unsigned NOT NULL,
  `locid` int(11) NOT NULL,
  PRIMARY KEY  (`uid`),
  KEY `startipnum` (`startipnum`),
  KEY `endipnum` (`endipnum`)
) TYPE=MyISAM  AUTO_INCREMENT=3538967 ;
问题是:一个查询需要3秒钟以上的时间

SELECT uid FROM `geoip_blocks` WHERE 1406658569 BETWEEN geoip_blocks.startipnum AND geoip_blocks.endipnum LIMIT 1
-大约3秒

SELECT uid FROM `geoip_blocks` WHERE startipnum < 1406658569 and endipnum > 1406658569 limit 1
从'geoip_blocks'中选择uid,其中startipnum<1406658569和endipnum>1406658569限制1
-没有增益,大约3秒

SELECT uid FROM `geoip_blocks` WHERE startipnum < 1406658569 and endipnum > 1406658569 limit 1

如何改进这一点?

您的startip和endip应该是一个组合索引。Mysql不能在一个查询中使用同一个表上的多个索引

我不确定语法,但类似于


键(startipnum,endipnum)

您的startip和endip应该是一个组合索引。Mysql不能在一个查询中使用同一个表上的多个索引

我不确定语法,但类似于


键(startipnum,endipnum)

看起来您正在尝试查找IP地址所属的范围。问题是MySQL不能在中间操作中充分利用索引。使用=操作时,索引工作得更好

向查询中添加=操作的一种方法是向表中添加。例如:

numeric 1406658569
ip address 83.215.232.9
class A with 8 bit network part
network part = 83
使用
(networkpart、startipnum、endipnum、uid)上的索引
这样的查询将变得非常快速:

SELECT  uid 
FROM    `geoip_blocks` 
WHERE   networkpart = 83
        AND 1406658569 BETWEEN startipnum AND endipnum

如果一个geoip块跨越多个网络类,则必须将其拆分为每个网络类的一行。

看起来您正在尝试查找IP地址所属的范围。问题是MySQL不能在中间操作中充分利用索引。使用=操作时,索引工作得更好

向查询中添加=操作的一种方法是向表中添加。例如:

numeric 1406658569
ip address 83.215.232.9
class A with 8 bit network part
network part = 83
使用
(networkpart、startipnum、endipnum、uid)上的索引
这样的查询将变得非常快速:

SELECT  uid 
FROM    `geoip_blocks` 
WHERE   networkpart = 83
        AND 1406658569 BETWEEN startipnum AND endipnum

如果一个geoip块跨越多个网络类,您必须将其拆分为每个网络类的一行。

根据您问题中的信息,我假设您正在执行MaxMind®的geoip®产品。我下载了GeoIP®数据的免费版本,将其加载到MySQL数据库中,并做了一些快速实验

使用startipnum上的索引,查询执行时间从0.15秒到0.25秒不等。在startipnum和endipnum上创建复合索引不会改变查询性能。这让我相信性能问题是由于硬件不足、MySQL调优不当或两者兼而有之。我运行测试的服务器有8G的RAM,这远远超过了获得相同性能所需的内存,因为索引文件只有28M

我的建议是以下两种选择之一

  • 花些时间调整MySQL服务器。MySQL在线文档将是一个合理的起点。如果MySQL文档不够的话,互联网搜索会发现大量的书籍、论坛、文章等
  • 如果我的假设是正确的,即您正在使用GeoIP®产品,那么第二种选择是使用MaxMind®提供的二进制文件格式。自定义文件格式已针对速度、内存使用和数据库大小进行了优化。为多种语言提供了访问数据的API
    另外,您提出的两个查询并不相同。中间运算符是包含的。第二个查询需要使用=运算符,以等效于使用中间运算符的查询。

    根据您问题中的信息,我假设您所做的是来自MaxMind®的GeoIP®产品的实现。我下载了GeoIP®数据的免费版本,将其加载到MySQL数据库中,并做了一些快速实验

    使用startipnum上的索引,查询执行时间从0.15秒到0.25秒不等。在startipnum和endipnum上创建复合索引不会改变查询性能。这让我相信性能问题是由于硬件不足、MySQL调优不当或两者兼而有之。我运行测试的服务器有8G的RAM,这远远超过了获得相同性能所需的内存,因为索引文件只有28M

    我的建议是以下两种选择之一

  • 花些时间调整MySQL服务器。MySQL在线文档将是一个合理的起点。如果MySQL文档不够的话,互联网搜索会发现大量的书籍、论坛、文章等
  • 如果我的假设是正确的,即您正在使用GeoIP®产品,那么第二种选择是使用MaxMind®提供的二进制文件格式。自定义文件格式已针对速度、内存使用和数据库大小进行了优化。为多种语言提供了访问数据的API
    另外,您提出的两个查询并不相同。中间运算符是包含的。第二个查询需要使用=运算符,以便与使用between运算符的查询等效。

    也许您想看看表的分区。此功能从MySQL 5.1开始就可用-因此,您不需要指定使用的版本,如果您使用的是旧版本,则此功能可能不适用

    由于IP地址的可能值范围是已知的(至少对于IPv4而言),您可以将表分解为大小相同的多个分区(如果数据分布不均匀,则可能甚至不相等)。有了它,MySQL可以很容易地跳过表的大部分,如果仍然需要的话,可以加快扫描速度


    有关可用的选项和语法,请参阅。

    也许您想看看表的分区。此功能从MySQL 5.1开始就可用-因此,您不需要指定使用的版本,如果您使用的是旧版本,则此功能可能不适用

    作为可能的值范围f