【问题标题】:Very Slow simple MySql query with index非常慢的带索引的简单 MySql 查询
【发布时间】:2016-06-29 04:31:03
【问题描述】:

我有这张桌子:

CREATE TABLE `messenger_contacts` (
  `number` varchar(15) NOT NULL,
  `has_telegram` tinyint(1) NOT NULL DEFAULT '0',
  `geo_state` int(11) NOT NULL DEFAULT '0',
  `geo_city` int(11) NOT NULL DEFAULT '0',
  `geo_postal` int(11) NOT NULL DEFAULT '0',
  `operator` tinyint(1) NOT NULL DEFAULT '0',
  `type` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `messenger_contacts`
  ADD PRIMARY KEY (`number`),
  ADD KEY `geo_city` (`geo_city`),
  ADD KEY `geo_postal` (`geo_postal`),
  ADD KEY `type` (`type`),
  ADD KEY `type1` (`operator`),
  ADD KEY `has_telegram` (`has_telegram`),
  ADD KEY `geo_state` (`geo_state`);

大约有 1100 万条记录。

在这张表上进行简单的计数选择大约需要 30 到 60 秒才能完成女巫似乎非常高。

select count(number) from messenger_contacts where geo_state=1

我不是数据库专家,所以除了设置索引之外,我不知道我还能做些什么来加快查询速度?

更新:

好的,我对列类型和大小进行了一些更改:

CREATE TABLE IF NOT EXISTS `messenger_contacts` (
  `number` bigint(13) unsigned NOT NULL,
  `has_telegram` tinyint(1) NOT NULL DEFAULT '0' ,
  `geo_state` int(2) NOT NULL DEFAULT '0',
  `geo_city` int(4) NOT NULL DEFAULT '0',
  `geo_postal` int(10) NOT NULL DEFAULT '0',
  `operator` tinyint(1) NOT NULL DEFAULT '0' ,
  `type` tinyint(1) NOT NULL DEFAULT '0' ,
  PRIMARY KEY (`number`),
  KEY `has_telegram` (`has_telegram`,`geo_state`),
  KEY `geo_city` (`geo_city`),
  KEY `geo_postal` (`geo_postal`),
  KEY `type` (`type`),
  KEY `type1` (`operator`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

现在*number 的查询只需 4 到 5 秒

感谢每一个人的帮助,即使是给我-1的那个人。考虑到我的服务器是低端硬件并且我将缓存select count 结果,这已经足够了。

【问题讨论】:

  • geo_state 有几种状态?在各种状态中,状态 =1 的百分比是多少?
  • explain extended select count(number) from messenger_contacts where geo_state=1 的输出是什么?我会将其添加到您问题的底部,因为这是帮助调试的一件有用的事情。
  • 你的 mysql 中的错误:`type` tinyint(1) NOT NULL DEFAULT '0''。行首有双单引号。
  • @maxhb 拼写错误,已修复
  • COUNT(*) 有什么不同吗?

标签: mysql sql performance indexing


【解决方案1】:

也许

select count(geo_state) from messenger_contacts where geo_state=1

因为它会给出相同的结果,但不会使用聚集索引中的数字列?

如果这没有帮助,我会尝试将数字列更改为 INT 类型,这应该会减小索引大小,或者尝试增加 MySQL 可用于缓存索引的内存量。

【讨论】:

    【解决方案2】:

    您没有更改数据类型。 INT(11) == INT(2) == INT(100) -- 每个都是一个 4 字节的有符号整数。您可能需要 1 字节无符号 TINYINT UNSIGNED 或 2 字节 SMALLINT UNSIGNED

    索引“标志”是一种浪费,我认为 typehas_telegram 是。优化器永远不会使用它们,因为它比简单地进行表扫描效率低。

    标准编码模式是:

    select count(*)
        from messenger_contacts
        where geo_state=1
    

    除非你不需要计算NULLs,这就是COUNT(geo_state) 所暗示的。

    一旦您在geo_state 上拥有索引(或以geo_state 开头的索引开始),查询将扫描索引(这是一个单独的BTree 结构)从第一次出现geo_state=1 直到最后一个,边走边算。也就是说,它将触及 110 万个索引条目。所以,几秒钟是可以预料的。计算“稀有”geo_state 会运行得更快。

    30-60 秒而不是 4-5 秒的原因很可能是缓存。前者必须从磁盘读取内容;后者没有。运行查询两次。

    对于那个查询,使用geo_state索引会比使用PRIMARY KEY更快除非存在缓存差异。

    INDEX(number,geo_state) 对于提到的任何SELECTs 几乎都是无用的——geo_state 应该是第一个。这是select count(number)... 案例的“覆盖”索引示例。

    More on building indexes.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-02
      • 2012-04-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多