加速一个MySQL的JOIN查询

huangapple go评论65阅读模式
英文:

Speeding up a mySQL JOIN query

问题

I'm trying to see up a query in mySQL. The table structure looks like:

CREATE TABLE `glinks_BuildRelations` (
  `relation_id` int(11) NOT NULL,
  `cat_id` int(11) NOT NULL,
  `link_id` int(11) NOT NULL,
  `page_num` int(11) NOT NULL,
  `distance` float DEFAULT NULL,
  `paid` int(11) NOT NULL DEFAULT 0
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb3;

ALTER TABLE `glinks_BuildRelations`
  MODIFY `relation_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2384882;
COMMIT;

CREATE TABLE `glinks_Link_Descriptions_with_URLs` (
  `description_id` mediumint(8) NOT NULL,
  `Description` text DEFAULT NULL,
  `Description_gite` text DEFAULT NULL,
  `Directions` text DEFAULT NULL,
  `Description_ES` text DEFAULT NULL,
  `Description_gite_ES` text DEFAULT NULL,
  `Directions_ES` text DEFAULT NULL,
  `Description_EN` text DEFAULT NULL,
  `Description_gite_EN` text DEFAULT NULL,
  `Directions_EN` text DEFAULT NULL,
  `Multilang_english_Description` text DEFAULT NULL,
  `Multilang_espanol_Description` text DEFAULT NULL,
  `Short_Description` text DEFAULT NULL,
  `link_id_fk` mediumint(7) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

ALTER TABLE `glinks_Link_Descriptions_with_URLs`
  ADD PRIMARY KEY (`description_id`),
  ADD KEY `link_id_fk` (`link_id_fk`);

ALTER TABLE `glinks_Link_Descriptions_with_URLs`
  MODIFY `description_id` mediumint(8) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=121050;
COMMIT;

(I tried to make a SQL fiddle with it, but it complained about the index!)

The table holds 121k rows in glinks_Link_Descriptions_with_URLs, and then 2,384,881 rows in the glinks_BuildRelations table. The query I'm using needs to

  1. Grab the records from glinks_BuildRelations , like: SELECT * FROM glinks_BuildRelations as relations WHERE cat_id = 197
  2. Then grab the values from glinks_Link_Descriptions_with_URLs where descs.link_id_fk = relations.link_id match)

This one is fast:

> SELECT * FROM glinks_BuildRelations as relations WHERE cat_id = 197;
> 0.012 seconds

The other one with JOIN is much slower, it seems:

SELECT * FROM glinks_BuildRelations as relations
			JOIN glinks_Link_Descriptions_with_URLs AS descs ON descs.link_id_fk = relations.link_id
			WHERE cat_id = 197
    5720 rows in set (6.928 sec)

Is there a way I can speed this up?

英文:

I'm trying to see up a query in mySQL. The table structure looks like:

CREATE TABLE `glinks_BuildRelations` (
  `relation_id` int(11) NOT NULL,
  `cat_id` int(11) NOT NULL,
  `link_id` int(11) NOT NULL,
  `page_num` int(11) NOT NULL,
  `distance` float DEFAULT NULL,
  `paid` int(11) NOT NULL DEFAULT 0
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb3;

ALTER TABLE `glinks_BuildRelations`
  MODIFY `relation_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2384882;
COMMIT;

CREATE TABLE `glinks_Link_Descriptions_with_URLs` (
  `description_id` mediumint(8) NOT NULL,
  `Description` text DEFAULT NULL,
  `Description_gite` text DEFAULT NULL,
  `Directions` text DEFAULT NULL,
  `Description_ES` text DEFAULT NULL,
  `Description_gite_ES` text DEFAULT NULL,
  `Directions_ES` text DEFAULT NULL,
  `Description_EN` text DEFAULT NULL,
  `Description_gite_EN` text DEFAULT NULL,
  `Directions_EN` text DEFAULT NULL,
  `Multilang_english_Description` text DEFAULT NULL,
  `Multilang_espanol_Description` text DEFAULT NULL,
  `Short_Description` text DEFAULT NULL,
  `link_id_fk` mediumint(7) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

ALTER TABLE `glinks_Link_Descriptions_with_URLs`
  ADD PRIMARY KEY (`description_id`),
  ADD KEY `link_id_fk` (`link_id_fk`);

ALTER TABLE `glinks_Link_Descriptions_with_URLs`
  MODIFY `description_id` mediumint(8) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=121050;
COMMIT;

(I tried to make a SQL fiddle with it, but it complained about the index!)

The table holds 121k rows in glinks_Link_Descriptions_with_URLs, and then 2,384,881 rows in the glinks_BuildRelations table. The query I'm using needs to

  1. Grab the records from glinks_BuildRelations , like: SELECT * FROM glinks_BuildRelations as relations WHERE cat_id = 197
  2. Then grab the values from glinks_Link_Descriptions_with_URLs where descs.link_id_fk = relations.link_id match)

This one is fast:

> SELECT * FROM glinks_BuildRelations as relations WHERE cat_id = 197;
> 0.012 seconds

The other one with JOIN is much slower, it seems:

SELECT * FROM glinks_BuildRelations as relations
			JOIN glinks_Link_Descriptions_with_URLs AS descs ON descs.link_id_fk = relations.link_id
			WHERE cat_id = 197
    5720 rows in set (6.928 sec)

Is there a way I can speed this up?

答案1

得分: 2

以下是更好性能的建议:

  • 对于所有表,请使用InnoDB而不是MyISAM。对于几乎所有工作负载,InnoDB性能更好。

  • 为每个表定义一个主键。自增列必须是主键。

  • 不要使用mediumint作为主键。MySQL内部始终使用四个字节,因此除非您希望在8388607之后停止插入新行,否则不需要使用mediumint,只需使用intbigint

  • 不要使用int(11),只需使用int。看起来像长度说明符的东西没有意义,并且在MYSQL 8.0中已弃用。

  • 使外键列与其他表中引用的主键具有相同的数据类型。即INT不同于INT UNSIGNED。它们必须是相同的类型。

  • 使用utf8mb4而不是utf8mb3。

  • 为您要搜索的列(cat_id)和要加入的列(link_id_fk)创建索引。如果不存在索引,创建外键将隐式创建一个索引。

以下是我得到的定义:

CREATE TABLE `glinks_BuildRelations` (
  `relation_id` INT NOT NULL AUTO_INCREMENT,
  `cat_id` INT NOT NULL,
  `link_id` INT NOT NULL,
  `page_num` INT NOT NULL,
  `distance` FLOAT DEFAULT NULL,
  `paid` INT NOT NULL DEFAULT 0,
   PRIMARY KEY (relation_id),
   KEY (cat_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=2384882;

CREATE TABLE `glinks_Link_Descriptions_with_URLs` (
  `description_id` INT NOT NULL AUTO_INCREMENT,
  `Description` text DEFAULT NULL,
  `Description_gite` text DEFAULT NULL,
  `Directions` text DEFAULT NULL,
  `Description_ES` text DEFAULT NULL,
  `Description_gite_ES` text DEFAULT NULL,
  `Directions_ES` text DEFAULT NULL,
  `Description_EN` text DEFAULT NULL,
  `Description_gite_EN` text DEFAULT NULL,
  `Directions_EN` text DEFAULT NULL,
  `Multilang_english_Description` text DEFAULT NULL,
  `Multilang_espanol_Description` text DEFAULT NULL,
  `Short_Description` text DEFAULT NULL,
  `link_id_fk` INT NOT NULL,
   PRIMARY KEY (description_id),
   FOREIGN KEY (link_id_fk) REFERENCES glinks_BuildRelations(relation_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=121050;

现在验证查询是否使用了索引:

mysql> explain SELECT * FROM glinks_BuildRelations as relations
         JOIN glinks_Link_Descriptions_with_URLs AS descs
         ON descs.link_id_fk = relations.link_id
         WHERE cat_id = 197;
+----+-------------+-----------+------------+------+---------------+------------+---------+------------------------+------+----------+-------+
| id | select_type | table     | partitions | type | possible_keys | key        | key_len | ref                    | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------------+---------+------------------------+------+----------+-------+
|  1 | SIMPLE      | relations | NULL       | ref  | cat_id        | cat_id     | 4       | const                  |    1 |   100.00 | NULL  |
|  1 | SIMPLE      | descs     | NULL       | ref  | link_id_fk    | link_id_fk | 4       | test.relations.link_id |    1 |   100.00 | NULL  |
+----+-------------+-----------+------------+------+---------------+------------+---------+------------------------+------+----------+-------+

EXPLAIN报告中的type: ref显示它正在使用每个表的索引。

英文:

Here are recommendations for better performance:

  • Use InnoDB for all tables, not MyISAM. InnoDB has better performance for almost every workload.

  • Define a primary key for each table. The auto-increment column must be a key.

  • Don't bother with mediumint for the primary key. MySQL internally uses four bytes anyway, so there's no advantage to using a mediumint unless you want it to stop taking new rows after 8388607. Just use int or bigint.

  • Don't bother with int(11), just use int. What looks like a length specifier doesn't mean anything, and it is deprecated in MYSQL 8.0.

  • Make the foreign key column the same data type as the primary key it references in the other table. I.e. INT is not the same as INT UNSIGNED. They must be the same type.

  • Use utf8mb4 instead of utf8mb3.

  • Create an index for the column you search on (cat_id), and the column you join (link_id_fk). Creating a foreign key implicitly creates an index if one does not exist.

Here's what I ended up with for definitions:

CREATE TABLE `glinks_BuildRelations` (
  `relation_id` INT NOT NULL AUTO_INCREMENT,
  `cat_id` INT NOT NULL,
  `link_id` INT NOT NULL,
  `page_num` INT NOT NULL,
  `distance` FLOAT DEFAULT NULL,
  `paid` INT NOT NULL DEFAULT 0,
   PRIMARY KEY (relation_id),
   KEY (cat_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=2384882;

CREATE TABLE `glinks_Link_Descriptions_with_URLs` (
  `description_id` INT NOT NULL AUTO_INCREMENT,
  `Description` text DEFAULT NULL,
  `Description_gite` text DEFAULT NULL,
  `Directions` text DEFAULT NULL,
  `Description_ES` text DEFAULT NULL,
  `Description_gite_ES` text DEFAULT NULL,
  `Directions_ES` text DEFAULT NULL,
  `Description_EN` text DEFAULT NULL,
  `Description_gite_EN` text DEFAULT NULL,
  `Directions_EN` text DEFAULT NULL,
  `Multilang_english_Description` text DEFAULT NULL,
  `Multilang_espanol_Description` text DEFAULT NULL,
  `Short_Description` text DEFAULT NULL,
  `link_id_fk` INT NOT NULL,
   PRIMARY KEY (description_id),
   FOREIGN KEY (link_id_fk) REFERENCES glinks_BuildRelations(relation_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=121050;

Now verify the query makes use of the indexes:

mysql> explain SELECT * FROM glinks_BuildRelations as relations
         JOIN glinks_Link_Descriptions_with_URLs AS descs
         ON descs.link_id_fk = relations.link_id
         WHERE cat_id = 197;
+----+-------------+-----------+------------+------+---------------+------------+---------+------------------------+------+----------+-------+
| id | select_type | table     | partitions | type | possible_keys | key        | key_len | ref                    | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------------+---------+------------------------+------+----------+-------+
|  1 | SIMPLE      | relations | NULL       | ref  | cat_id        | cat_id     | 4       | const                  |    1 |   100.00 | NULL  |
|  1 | SIMPLE      | descs     | NULL       | ref  | link_id_fk    | link_id_fk | 4       | test.relations.link_id |    1 |   100.00 | NULL  |
+----+-------------+-----------+------------+------+---------------+------------+---------+------------------------+------+----------+-------+

The type: ref in the EXPLAIN report shows it is using an index for each table.

huangapple
  • 本文由 发表于 2023年3月21日 00:16:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/75792775.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定