【问题标题】:Optimize two simple MySQL queries - column indexes优化两个简单的 MySQL 查询——列索引
【发布时间】:2016-01-28 12:01:43
【问题描述】:

我是 MySQL 查询优化的新手,需要关于如何针对两个查询优化数据库的建议 - 我应该设置哪些索引以及在哪里设置。下面是数据库结构和查询。

CREATE TABLE `data_node` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `type` enum('node','place') DEFAULT NULL,
  `name` varchar(255) DEFAULT '',
  `source_id` bigint(20) unsigned DEFAULT NULL,
  `data_id` bigint(20) unsigned NOT NULL,
  `data_lat` decimal(8,6) NOT NULL,
  `data_lon` decimal(9,6) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `data_node_tag` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `node_id` bigint(20) unsigned NOT NULL,
  `data_key` varchar(255) NOT NULL DEFAULT '',
  `data_value` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

第一个查询:

SELECT *
FROM data_node n
LEFT JOIN data_node_tag nt ON nt.node_id = n.id
WHERE n.type = "place"
  AND nt.data_value LIKE "%place%"
GROUP BY n.data_id LIMIT 100

第二次查询:

SELECT *
FROM data_node n
LEFT JOIN data_node_tag nt ON nt.node_id = n.id
WHERE n.source_id = 123
  AND n.type = "node"
  AND nt.data_value = "cafe"
  AND (n.data_lat BETWEEN 1.000000 AND 2.000000)
  AND (n.data_lon BETWEEN 3.000000 AND 4.000000)
GROUP BY n.data_id LIMIT 1000

我将不胜感激。

【问题讨论】:

  • 请注意,您的两个查询都会检查 WHERE 子句中 LEFT OUTER JOINed 表中的值,从而使它们都有效地成为 INNER JOIN
  • 他们使用聚合器,实际上没有聚合任何东西

标签: mysql query-optimization key-value entity-attribute-value


【解决方案1】:

对于第一个查询,您需要在 data_node(type, id, data_id)data_node_tag(node_id, data_value) 上建立索引。

对于第二个查询,您需要在 data_node(source_id, type, data_lat, data_long, id)data_node_tag(id, data_value) 上建立索引(这与第一个查询相同)。

【讨论】:

  • 所有列上的一个索引或每列的单独索引?
  • 索引是按照它们应该定义的顺序与列一起指定的。每个建议的索引都是一个索引。
【解决方案2】:

以下是查询1的建议:

  1. 在 data_node_tag 表的 node_id 字段上创建索引。
  2. 将 data_node 表中的 type 字段从 enum 更改为 varchar(10) 并在其上创建索引。
  3. 如果可能的话,尽量避免使用'%place%',至少左边部分不应该变量来使用索引。因此,如果您可以避免它,请根据您的要求在其上创建一个部分索引,例如前 20 到 20 个字符。

注意:最重要的索引在 node_id 字段上,因为您将基于它加入两个表。因此,您可以仅通过此索引获得主要的性能差异。

为了进一步优化查询 1 和 2,这将取决于不同条件过滤的数据量。

【讨论】:

  • MyISAM 引擎中有部分索引吗?
  • 是的,myisam 没有限制创建部分索引......首先只需在 data_node_tag 的 node_id 字段上创建一个索引并检查性能......在检查有多少数据后进一步通过不同的条件过滤,我们可以最终确定应该在哪个字段索引上创建...
【解决方案3】:

id 字段的 data_node_tag 表使用外键。 由于您有重复的 id 字段。您可以使用外键来克服数据的重复。

CREATE TABLE data_node_tag ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, node_id bigint(20) unsigned NOT NULL, data_key varchar(255) NOT NULL DEFAULT '', data_value varchar(255) NOT NULL DEFAULT '', PRIMARY KEY (id), FOREIGN KEY (id) REFERENCES data_node(id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;

【讨论】:

  • 我没有 InnoDB,因为 MyISAM 更适合我的目的(经常阅读,很少修改)。在 MyISAM 引擎中没有外键。但是感谢您的尝试! :)
  • 不知道...:)
  • 您的查询有效(不会显示错误),但外键将被忽略。相反,它将仅为该列创建索引。
  • myisam 不支持外键...即使 innodb db 中的外键也只是提供性能,就好像您在没有外键的 node_id 字段上创建索引一样。
【解决方案4】:

索引:

data_node_tag.node_id

data_node.source_id

data_node.type

data_node_tag.data_value

data_node.lat

data_node.lon。

引擎将根据基数选择要使用的那个。你可以做的更少,但这取决于你的数据是什么样的。

其次,您可能应该索引 where 子句中使用的任何字段。任何唯一的字段都应该有一个唯一的索引。您还可以在多个字段上创建复合索引,但从每个字段的索引开始。

数据库引擎每次查询只会使用每个表的 1 个索引(实际上是每个别名,但不要担心),因此如果您有 2 个要查询的字段,您可以在这两个字段上创建一个复合索引composite index

【讨论】:

  • data_id 不是AUTOINCREMENT。不幸的是,除了id 之外,没有一个字段不是唯一的。但感谢您提供剩余信息。我认为它们很有用。
  • 复合索引通常更好。
【解决方案5】:

(这些 cmets 更像是一个警告而不是一个答案。)

对于data_node_tag,去掉id;更改为PRIMARY KEY(node_id, data_key)。即便如此,您最终还是会发现 EAV 架构糟糕的原因。

您还会发现为什么 lat/lng 难以优化。

使用 InnoDB,而不是 MyISAM。

LIKE "%place%" 无法优化(尽管LIKE "place%" 可以使用索引)。考虑FULLTEXT

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-24
    • 2010-11-03
    相关资源
    最近更新 更多