【问题标题】:MySQL Long First Query TimeMySQL 首次查询时间长
【发布时间】:2015-03-10 16:57:51
【问题描述】:

我有一个网站,其中有两个对数据库的主要查询,它们在第一次运行时非常慢,经过一些测试,这似乎是 MySQL 的问题。如果我在第一次运行查询时直接在 Sequal Pro 中运行查询,它最多可能需要 4 秒才能运行,但再次运行相同的查询大约需要 60 毫秒,本地查询时间与我们服务器上的查询时间大致相同,让我觉得这不是服务器问题。

不完全确定增加缓冲池大小是否会有太大帮助,因为潜在查询组合的数量可能在 800K 左右。

数据库中的表是 innodb,两个查询都访问同一个表,该表有 52K 条记录,我需要的大部分信息都被分组到一个被索引的“searchfield”字段中。

只有在查询中使用的字段或者是主键/外键的字段才会被索引。

我尝试在主查询的“where”中将内部连接更改为 select 语句,但这并没有使查询变得更快。

查询是

查询 1

SELECT 
  `item_attribute`.`attribute_id` AS `attribute_id`,
  `attribute_value_id` AS `attribute_value_id`,
  `collection_attribute`.`title` AS `ca_title`,
  `collection_attribute`.`type` AS `ca_type`,
  `collection_attribute`.`is_collapsible` AS `ca_is_collapsible`,
  `collection_attribute`.`orderindex` AS `ca_orderindex`,
  `collection_attribute`.`multi_select` AS `ca_multi_select`,
  `item_attribute`.`item_id` AS `item_id`,
  `product`.`id` AS `product_id` 
  FROM `item_attribute`
  INNER JOIN `item` ON item.id = item_attribute.item_id
  INNER JOIN `product` ON product.id = item.product_id
  INNER JOIN `collection_attribute` ON item_attribute.attribute_id = collection_attribute.attribute_id
  INNER JOIN `attribute_value` ON attribute_value.id = item_attribute.attribute_value_id 
  WHERE ((`product`.`searchfilter` LIKE '%c:35∆%') AND (`collection_attribute`.`collection_id`='35')) AND (`attribute_value`.`active`=1)
  GROUP BY `attribute_value_id`

查询 2

SELECT DISTINCT `item_attribute`.`attribute_id` AS `attribute_id`,
  GROUP_CONCAT(item_attribute.attribute_value_id SEPARATOR \"-\") AS `attribute_value`,
  GROUP_CONCAT(attribute_value.title SEPARATOR \" - \") AS `title`
  FROM `item_attribute`
  LEFT JOIN `item` ON item.id = item_attribute.item_id 
  LEFT JOIN `attribute` ON attribute.id = item_attribute.attribute_id 
  LEFT JOIN `attribute_value` ON attribute_value.id = attribute_value_id 
  WHERE (`item`.`product_id`='894') AND (`attribute`.`is_option`=1) 
  GROUP BY `attribute_id`, `item_id`

表结构

CREATE TABLE `product` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `slug` varchar(255) NOT NULL,
  `sku` varchar(20) NOT NULL,
  `active` tinyint(1) DEFAULT '0',
  `orderindex` int(2) DEFAULT '-1',
  `search` varchar(255) DEFAULT NULL,
  `searchfilter` varchar(255) DEFAULT NULL,
  `created_at` int(11) DEFAULT NULL,
  `updated_at` int(11) DEFAULT NULL,
  `protected` tinyint(1) DEFAULT '0',
  `description` varchar(512) DEFAULT NULL,
  `type_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `product_ibfk_1` (`type_id`),
  KEY `searchfilter` (`searchfilter`),
  CONSTRAINT `product_ibfk_1` FOREIGN KEY (`type_id`) REFERENCES `attribute_value` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1882 DEFAULT CHARSET=utf8;

--

CREATE TABLE `collection_attribute` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `collection_id` int(11) DEFAULT NULL,
  `attribute_id` int(11) DEFAULT NULL,
  `title` varchar(512) NOT NULL,
  `slug` varchar(512) NOT NULL,
  `created_at` int(11) DEFAULT NULL,
  `updated_at` int(11) DEFAULT NULL,
  `active` tinyint(1) DEFAULT '0',
  `orderindex` int(2) DEFAULT '-1',
  `search` varchar(255) DEFAULT NULL,
  `searchfilter` varchar(255) DEFAULT NULL,
  `protected` tinyint(1) DEFAULT '0',
  `is_collapsible` tinyint(1) DEFAULT '0',
  `type` enum('icon','checkbox','checkboxIcon','image') DEFAULT NULL,
  `multi_select` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `collection_attribute_ibfk_1` (`collection_id`),
  KEY `collection_attribute_ibfk_2` (`attribute_id`),
  CONSTRAINT `collection_attribute_ibfk_1` FOREIGN KEY (`collection_id`) REFERENCES `collection` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `collection_attribute_ibfk_2` FOREIGN KEY (`attribute_id`) REFERENCES `attribute` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=140 DEFAULT CHARSET=utf8;

--

    CREATE TABLE `item` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `slug` varchar(255) NOT NULL,
  `title` varchar(255) NOT NULL,
  `pattern_code` varchar(32) NOT NULL,
  `tom_code` varchar(32) NOT NULL,
  `navision_code` varchar(32) NOT NULL,
  `description` varchar(255) DEFAULT NULL,
  `active` tinyint(1) DEFAULT '0',
  `orderindex` int(2) DEFAULT '-1',
  `created_at` int(11) DEFAULT NULL,
  `updated_at` int(11) DEFAULT NULL,
  `search` varchar(1024) DEFAULT NULL,
  `searchfilter` varchar(255) DEFAULT NULL,
  `product_id` int(11) DEFAULT NULL,
  `protected` tinyint(1) DEFAULT '0',
  `pattern_series` varchar(255) DEFAULT NULL,
  `pattern_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `item_ibfk_1` (`product_id`),
  KEY `searchfilter` (`searchfilter`),
  KEY `product_id` (`product_id`),
  KEY `pattern_id` (`pattern_id`),
  CONSTRAINT `item_ibfk_1` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `item_ibfk_2` FOREIGN KEY (`pattern_id`) REFERENCES `pattern_series` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=50060 DEFAULT CHARSET=utf8;

--

CREATE TABLE `item_attribute` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `item_id` int(11) NOT NULL,
  `attribute_id` int(11) NOT NULL,
  `attribute_value_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `attribute_id` (`attribute_id`),
  KEY `attribute_value_id` (`attribute_value_id`),
  KEY `item_id` (`item_id`),
  CONSTRAINT `item_attribute_ibfk_1` FOREIGN KEY (`attribute_id`) REFERENCES `attribute` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `item_attribute_ibfk_2` FOREIGN KEY (`attribute_value_id`) REFERENCES `attribute_value` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `item_attribute_ibfk_3` FOREIGN KEY (`item_id`) REFERENCES `item` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=857111 DEFAULT CHARSET=utf8;

--

CREATE TABLE `attribute_value` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `slug` varchar(255) NOT NULL,
  `code` varchar(255) DEFAULT NULL,
  `title` varchar(255) NOT NULL,
  `active` tinyint(1) DEFAULT '0',
  `orderindex` int(2) DEFAULT '-1',
  `created_at` int(11) DEFAULT NULL,
  `updated_at` int(11) DEFAULT NULL,
  `search` varchar(255) DEFAULT NULL,
  `searchfilter` varchar(255) DEFAULT NULL,
  `attribute_id` int(11) DEFAULT NULL,
  `protected` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3471 DEFAULT CHARSET=utf8;

--

CREATE TABLE `attribute` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `slug` varchar(255) NOT NULL,
  `code` varchar(255) DEFAULT NULL,
  `is_option` tinyint(1) DEFAULT '0',
  `searches` tinyint(1) DEFAULT '0',
  `option_type` enum('dropdown','switch','fingersizes') DEFAULT NULL,
  `option_label` varchar(32) DEFAULT NULL,
  `active` tinyint(1) DEFAULT '0',
  `orderindex` int(2) DEFAULT '-1',
  `created_at` int(11) DEFAULT NULL,
  `updated_at` int(11) DEFAULT NULL,
  `search` varchar(255) DEFAULT NULL,
  `searchfilter` varchar(255) DEFAULT NULL,
  `protected` tinyint(1) DEFAULT '0',
  `option_requires` int(11) DEFAULT NULL,
  `option_depends` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `is_option` (`is_option`)
) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8;

任何关于如何改善初始查询时间的建议都会很棒

提前致谢:)

-- 编辑--

解释选择查询 1

解释选择查询 2

【问题讨论】:

    标签: mysql database performance innodb


    【解决方案1】:

    在启动服务器后第一次执行查询时,没有缓存,因此查询需要从磁盘获取内容。由于缓存,访问相同表的相同部分的所有后续查询将快得多。这是“正常的”。

    如果您启用了“查询缓存”(默认情况下可能已启用),那么当您第二次运行完全相同的查询时,它会立即找到查询缓存的结果。我所说的“确切地”是指空格没有改变。几乎所有“生产”服务器都最好关闭查询缓存。

    innodb_buffer_pool_size 应该是可用 RAM 的 70% 左右。更改该值不会影响针对冷缓存的 SELECT,但可能有助于/伤害后续运行。这似乎与您的情况无关,因为第二次运行非常快。

    请提供EXPLAIN SELECT ...,以便我们了解优化器如何决定执行它们。

    LIKE '%c:35∆%' -- 由于前导通配符,无法使用索引。

    item_ids 是什么?

    item_attribute 是一种 EAV 模式模式。糟透了。这两个查询都很丑陋,而且可扩展性很差。它可能有助于 一些 摆脱id 并从其他字段的适当组合中制作复合主键。希望是使用与数据聚集在一起的 PRIMARY KEY,而不必从辅助键中反弹。 More discussion of EAV.

    假设它的基数很低,索引可能永远不会被使用:
    密钥is_option (is_option)

    【讨论】:

    • 感谢您的信息。我将尝试将通配符更改为另一个联接,看看是否有帮助。并感谢有关 EAV 的信息,看看我是否可以让它与 Yii 一起工作 :) 与我们服务器的技术支持交谈,他建议将池大小更改为 40%,即使我在另一篇文章中看到这里它可以达到 70%,但这似乎只包含查询 1 的 3 个。我们有 1 GB 的 RAM,因此池大小目前约为 400MB。
    • 哎哟。 1GB。即使是 400MB 也是危险的高。交换对 MySQL 性能非常不利,比现在的 buffer_pool 更糟糕。检查 my.cnf 的其余部分——可能需要降低其他几项内容。
    • 嗯,但你说'innodb_buffer_pool_size 应该是可用 RAM 的 70% 左右'它只设置为 40%,现在你说那太高了。我有点困惑现在应该设置什么,它最初只有 48MB
    • 可用 RAM 的 70%,而不是 RAM 的 70%。当您的 RAM 少于 4GB 时,其中大部分会被操作系统、mysql 和其他东西占用。
    • 我认为 70% 是一个简单、安全的陈述。那是在机器变成 4GB 或更大之后。 (对于 128GB,70% 可以提高到 80-85,但好处在于“收益递减”。)然后是“虚拟”和“云”,它们的 RAM 分配“很小”。这搞砸了我的 70%,我必须强调“可用”,并注意 2G 和 1G 机器。如果你从 1G 中取出 700M,你可能会“交换”——这比降低缓冲池要糟糕得多
    猜你喜欢
    • 1970-01-01
    • 2012-03-11
    • 2015-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-18
    • 1970-01-01
    相关资源
    最近更新 更多