Повышение производительности на большом наборе данных с помощью связей сводной таблицы (с использованием Laravel) - PullRequest
0 голосов
/ 22 апреля 2020

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

Схема настроена как Много-ко-многим Полиморф c отношения. У меня есть таблица Videos, которая содержит видеоинформацию, таблица Category, которая содержит все категории, и таблица Categorizable, которая содержит сводную информацию.

Соотношение между Videos и Categorizable около 1: 4. (Т.е. для каждого видео есть как минимум 4+ категории).

Результаты при доступе к сводным данным с ограничением в 40 строк и БЕЗ смещения: ~ 1.2s +, Добавление смещения увеличило бы это еще больше, если смещение> 50 000 строк.

Хотя 1,2 секунды кажутся маленькими, это лишь малая часть всего набора данных, который в конечном итоге содержит около 30 миллионов видеозаписей (таким образом, имея ~ 12 + миллион классифицируемых записей). Боюсь, что 1,2 будет умножаться на каждый миллион записей.

Схема базы данных

Таблица видео :

------------------------------------------------------------------------
id     | title                      | author    | views | duration | etc.
------------------------------------------------------------------------
1      | What's the biggest word?   | Dictonary | 3432  | 600      | ...
2      | Yearly Videos Roundup 2020 | YouTube   | 165   | 945      | ...
3      | Google SEO Help            | Google    | 1401  | 287      | ...
↓      
101234 | How to cook pasta          | YouTube   | 9401  | 87       | ...

Индексы:

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Table  | Non_unique | Key_name         | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
videos | 0          | PRIMARY          | 1            | id          | A         | 253057      | NULL     | NULL   |      | BTREE      |         |               | YES     | NULL
videos | 1          | idx_videos_views | 1            | views       | A         | 102188      | NULL     | NULL   |YES   | BTREE      |         |               | YES     | NULL

Таблица категорий :

-------------------------------------------------------------
id      | category_id | cateogrizable_id | categorizable_type
-------------------------------------------------------------
1       | 5           |  1               | 'Video'
2       | 100         |  2               | 'Video'
3       | 31          |  3               | 'Video'
↓
299052  | 65          |  101234          | 'Video'

Индексы:

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Table          | Non_unique | Key_name             | Seq_in_index | Column_name      | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
categorizables | 0          | PRIMARY              | 1            | id               | A         | 296745      | NULL     | NULL   |      | BTREE      |         |               | YES     | NULL
categorizables | 1          | idx_category_id      | 1            | category_id      | A         | 82          | NULL     | NULL   |      | BTREE      |         |               | YES     | NULL
categorizables | 1          | idx_categorizable_id | 1            | categorizable_id | A         | 104705      | NULL     | NULL   |      | BTREE      |         |               | YES     | NULL

Таблица категорий :

--------------------
id  | name 
--------------------
1   | Education
2   | Health
3   | Entertainment
↓
100 | News

Индексы:

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Table       | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
categories  |  0         | PRIMARY  |  1           |  id         |  A        |  100        | NULL     | NULL   |      |  BTREE     |         |               |  YES    | NULL

MySQL

Тип: InnoDB

Laravel Запрос :

Category::where('id', $cat)
  ->with(['videos' => function($query){ 
    return $query->take(40)->orderby('views'); 
   }])
   ->get();

Превращается в MySQL запрос :

SELECT `videos`.`id`, `views` 
FROM `videos` inner join `categorizables` 
ON `videos`.`id` = `categorizables`.`categorizable_id`
WHERE `categorizables`.`category_id` = 1 
ORDER BY `views` desc 
LIMIT 40 offset 0

Результаты производительности

Ниже приведены выходные данные производительности от MySQL

---------------------------------------------------------
Stage                                          | Duration
---------------------------------------------------------
stage/sql/starting                             | 0.000068
stage/sql/Executing hook on transaction begin. | 0.000000
stage/sql/starting                             | 0.000003
stage/sql/checking permissions                 | 0.000001
stage/sql/checking permissions                 | 0.000001
stage/sql/Opening tables                       | 0.000038
stage/sql/init                                 | 0.000003
stage/sql/System lock                          | 0.000005
stage/sql/optimizing                           | 0.000007
stage/sql/statistics                           | 0.005628
stage/sql/preparing                            | 0.000008
stage/sql/Creating tmp table                   | 0.000033
stage/sql/executing                            | 1.273442
stage/sql/end                                  | 0.000001
stage/sql/query end                            | 0.000001
stage/sql/waiting for handler commit           | 0.000008
stage/sql/removing tmp table                   | 0.000003
stage/sql/closing tables                       | 0.000006
stage/sql/freeing items                        | 0.000080
stage/sql/cleaning up                          | 0.000000

Специально :

stage/sql/executing | 1.273442

Стоимость запроса :

----------------------------------
Variable_name     | Value
----------------------------------
Last_query_cost   | 107258.575124

РЕДАКТИРОВАТЬ:

Объяснить запрос

С сортировкой:

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
id | select_type | table          | partitions | type   | possible_keys                        | key             | key_len | ref                                     | rows  | filtered | Extra
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1  | SIMPLE      | categorizables | NULL       | ref    | idx_category_id,idx_categorizable_id | idx_category_id | 4       | const                                   | 51210 | 100.00   | Using temporary; Using filesort
1  | SIMPLE      | videos         | NULL       | eq_ref | PRIMARY                              | PRIMARY         | 4       | dev_db.categorizables.categorizable_id  | 1     | 100.00   | Using index

Без сортировки:

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
id | select_type | table          | partitions | type   | possible_keys                        | key             | key_len | ref   | rows  | filtered | Extra
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1  | SIMPLE      | videos         | NULL       | index  | NULL                                 | PRIMARY         | 4       | NULL  | 40    | 100.00   | Backward index scan; Using index

Ответы [ 2 ]

1 голос
/ 27 апреля 2020

Позвольте мне рассказать вам о наихудшем случае:

SELECT  v.`id`, v.`views`
    FROM  `videos` AS v
    inner join  `categorizables` AS c  ON v.`id` = c.`categorizable_id`
    WHERE  c.`category_id` = 1
    ORDER BY  v.`views` desc
    LIMIT  40 offset 50000 

Поток выглядит примерно так:

  1. Найдите в categorizables все строки с category_id = 1. Это может использовать или не использовать индекс: INDEX(category_id, categorizable_id) может помочь.
  2. Для каждой из этих строк введите videos, чтобы получить views и id. Предполагая, что id является PRIMARY KEY, у меня нет добавленной рекомендации.
  3. Соберите все эти вещи во временную таблицу. (Предположительно, больше 50К строк?)
  4. Сортировать эту таблицу.
  5. Прочитать отсортированную таблицу, пропустив более 50000 строк.
  6. Доставить 40 строк и выйти.

Надеюсь, очевидно, что удаление сортировки или удаление смещения или (et c) приведет к упрощенному плану выполнения и, следовательно, будет быстрее.

Вы говорите, что есть отношения многие ко многим? Это categorizables? Следовал ли он советам по производительности здесь: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table?

0 голосов
/ 22 апреля 2020

Вы можете посмотреть на это Laravel Панель отладки , чтобы увидеть, сколько повторяющихся запросов вы делаете.

Далее вы можете загружать отношения при получении данных, например:

Контроллер индекса

$videos = Videos::with('categories')->get();
return $videos;

Вы также можете использовать кэширование для Laravel, например:

$videos = \Cache::rememberForever('key', function() {
  return Video::with('categories')->get();
});

return $videos;
...