FIND_IN_SET или использовать JOIN? - PullRequest
0 голосов
/ 13 мая 2019

У меня есть таблица статей, она может быть связана со многими категориями.

Вот решение 1 с FIND_IN_SET:

 CREATE TABLE `article` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `cat` varchar(512) DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5090104 DEFAULT CHARSET=utf8mb4

А вот решение 2 с объединением таблиц:

 CREATE TABLE `article_2` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5511464 DEFAULT CHARSET=utf8mb4 


 CREATE TABLE `article_cat` (
  `cat` int(10) unsigned NOT NULL,
  `article_id` int(10) unsigned NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`cat`,`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 

Затем приведем сравнение запросов:

mysql> select count(1) from article;
+----------+
| count(1) |
+----------+
|  5207355 |
+----------+
1 row in set (1.12 sec)

mysql> select count(1) from article_2;
+----------+
| count(1) |
+----------+
|  5207313 |
+----------+
1 row in set (0.74 sec)

mysql> select count(1) from article_cat;
+----------+
| count(1) |
+----------+
| 20589375 |
+----------+
1 row in set (2.85 sec)


mysql> select count(1) from `article` where FIND_IN_SET('2',`article`.`cat`);
+----------+
| count(1) |
+----------+
|  2157053 |
+----------+
1 row in set (1.22 sec)

mysql> 
mysql> select count(1) from `article_2` u INNER JOIN article_cat c ON c.article_id = u.id WHERE c.cat =2;
+----------+
| count(1) |
+----------+
|  2156622 |
+----------+
1 row in set (13.72 sec)


mysql> select * from `article` where FIND_IN_SET('2',`article`.`cat`) limit 2000000,10;
+---------+-----------------+---------------------+---------------------+
| id      | cat             | created_at          | updated_at          |
+---------+-----------------+---------------------+---------------------+
| 5132154 | 2,5,7           | 2019-05-13 15:24:58 | 2019-05-13 15:24:58 |
| 5132155 | 1,2,3,5,6,8,9   | 2019-05-13 15:24:58 | 2019-05-13 15:24:58 |
| 5132156 | 1,2,6,7         | 2019-05-13 15:24:58 | 2019-05-13 15:24:58 |
| 5132157 | 1,2,3,5,9       | 2019-05-13 15:24:58 | 2019-05-13 15:24:58 |
| 5132158 | 1,2,3,4,6,7,9   | 2019-05-13 15:24:58 | 2019-05-13 15:24:58 |
| 5132159 | 1,2,5,6,7       | 2019-05-13 15:24:58 | 2019-05-13 15:24:58 |
| 5132160 | 2,3,4,5,7,9     | 2019-05-13 15:24:58 | 2019-05-13 15:24:58 |
| 5132161 | 1,2,5,9         | 2019-05-13 15:24:58 | 2019-05-13 15:24:58 |
| 5132164 | 1,2,3,4,6,7,8,9 | 2019-05-13 15:24:58 | 2019-05-13 15:24:58 |
| 5132166 | 1,2,3,4,5,8     | 2019-05-13 15:24:58 | 2019-05-13 15:24:58 |
+---------+-----------------+---------------------+---------------------+
10 rows in set (1.28 sec)

mysql> select * from `article_2` u INNER JOIN article_cat c ON c.article_id = u.id WHERE c.cat =2 limit 2000000,10 ;
+---------+---------------------+---------------------+-----+------------+---------------------+---------------------+
| id      | created_at          | updated_at          | cat | article_id | created_at          | updated_at          |
+---------+---------------------+---------------------+-----+------------+---------------------+---------------------+
| 5133109 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |   2 |    5133109 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |
| 5133110 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |   2 |    5133110 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |
| 5133113 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |   2 |    5133113 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |
| 5133116 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |   2 |    5133116 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |
| 5133117 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |   2 |    5133117 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |
| 5133120 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |   2 |    5133120 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |
| 5133124 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |   2 |    5133124 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |
| 5133133 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |   2 |    5133133 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |
| 5133137 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |   2 |    5133137 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |
| 5133138 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |   2 |    5133138 | 2019-05-13 15:25:01 | 2019-05-13 15:25:01 |
+---------+---------------------+---------------------+-----+------------+---------------------+---------------------+
10 rows in set (14.01 sec)

Из того, что я знаю, FIND_IN_SET выполняет полное сканирование столбцов, индекс не получает выгоды, но здесь я вижу, что он имеет лучшую производительность, чем решение для таблицы соединений, это нормально?

Было бы неплохо использовать FIND_IN_SET, где размер статьи таблицы увеличился до 10000000+? если нет, то когда было бы лучше использовать FIND_IN_SET?

UPDATE:

FIND_IN_SET в некоторых случаях работает лучше, единственная проблема в том, что это не стиль проектирования реляционных баз данных.

1 Ответ

0 голосов
/ 13 мая 2019

Производительность объединения низкая, потому что вы просматриваете много строк в индексе.и пересмотреть их, чтобы получить метки времени и другие поля (которых нет в индексе.

Рекомендации для article_cat.

  1. идентификатор не требуется
  2. cat, article_id - хороший первичный ключ, особенно для этого запроса. Если ищутся article_ids больше, поменяйте местами порядок и добавьте cat, article_id как UNIQUE KEY

удалить created_at, updated_at, учитывая, что они находятся в таблице статей, это будет дубликат, дубликат, который замедляет запросы JOIN.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...