Улучшения производительности запросов - сбор user_activities в Cake PHP 3.0 - PullRequest
0 голосов
/ 18 февраля 2020

В настоящее время я работаю над проектом php 3.0 для создания индивидуальной социальной сети. Я имею дело с проблемой для моей "временной шкалы". Там у меня есть запрос, который сначала получает список всех моих друзей в этом сообществе, а затем собирает все сделанные им user_activities. (см. запрос ниже).

Итак, в конце у меня есть список всех действий моих друзей, упорядоченных созданным ... Во FrontEnd я использую foreach, чтобы l oop через него и иметь для каждого " activity.type "отдельный шаблон представления (просто текстовое сообщение, графическое сообщение или видео-публикация)

Для числа 50-100 друзей все нормально ... временная шкала требует <1se c. чтобы отображаться правильно ... но в сообществе есть пользователи с 5000 друзьями (я тестировал с ~ 400 друзьями, у которых время выполнения запроса составляло 4-5 секунд). </p>

У кого-нибудь есть идеи, как я могу улучшить запрос или я мог бы сделать что-то еще, чтобы улучшить производительность (только со стороны SQL или PHP, ... Я уже настроил некоторые индексы для определенных столбцов и подумал об ограничении по дате, исключая сбор только действий из - 1 год).

Я не очень разбираюсь в этой топи c, поэтому буду признателен за любые идеи:)

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

собрать список друзей

SELECT (CASE WHEN `Friends`.`user_id` = '6' THEN `FriendUsers`.`id` ELSE `Friends`.`user_id` END) AS `id` FROM `friends` `Friends` LEFT JOIN `users` `FriendUsers` ON `FriendUsers`.`id` = (`Friends`.`friend_id`) WHERE (`status` >= 0 AND (`Friends`.`user_id` = 6 OR `Friends`.`friend_id` = 6))

запрос на сбор действий пользователя

SELECT  `Wallposts`.`text` AS `Wallposts__text`, `Activities`.`name` AS `Activities__name`,
        `Users`.`firstname` AS `Users__firstname`, `Users`.`lastname` AS `Users__lastname`,
        `Users`.`username` AS `Users__username`, `Users`.`id` AS `Users__id`,
        `UsersActivities`.`created` AS `UsersActivities__created`,
        `UsersActivities`.`id` AS `UsersActivities__id`, `UsersActivities`.`video_id` AS `UsersActivities__video_id`,
        `UsersActivities`.`picture_id` AS `UsersActivities__picture_id`,
        `UsersActivities`.`visible_for_group` AS `UsersActivities__visible_for_group`,
        `Pictures`.`id` AS `Pictures__id`, `Pictures`.`user_id` AS `Pictures__user_id`,
        `Pictures`.`description` AS `Pictures__description`, `Pictures`.`fileName` AS `Pictures__fileName`,
        `Pictures`.`album_id` AS `Pictures__album_id`, `Pictures`.`isChosen` AS `Pictures__isChosen`,
        `Albums`.`id` AS `Albums__id`, `Albums`.`name` AS `Albums__name`,
        `Videos`.`id` AS `Videos__id`, `Videos`.`hoster` AS `Videos__hoster`,
        `Videos`.`video_key` AS `Videos__video_key`, `Videos`.`name` AS `Videos__name`,
        `Videos`.`description` AS `Videos__description`
    FROM  `users_activities` `UsersActivities`
    left JOIN  `users` `Users`  ON UsersActivities.user_id = Users.id
    left JOIN  `activities` `Activities`  ON UsersActivities.activity_id = Activities.id
    left JOIN  `wallposts` `Wallposts`  ON UsersActivities.wallpost_id = Wallposts.id
    LEFT JOIN  `videos` `Videos`  ON `Videos`.`id` = (`UsersActivities`.`video_id`)
    LEFT JOIN  `pictures` `Pictures`  ON `Pictures`.`id` = (`UsersActivities`.`picture_id`)
    LEFT JOIN  `albums` `Albums`  ON `Albums`.`id` = (`Pictures`.`album_id`)
    WHERE  (`UsersActivities`.`member_id` = 0
              AND  `Users`.`active` not in ('2')
              AND  `UsersActivities`.`user_activity_id` = 0
              AND  (
                    (`UsersActivities`.`user_id` = 390900002
                              AND  `UsersActivities`.`activity_id` not in (5,6)
                    )
                      OR  (`UsersActivities`.`user_id` in 
                                (391407850,391511765,
                                        391511432,491511714,391512398,391204138,391407984,391000522,
                                        391408687,391511708,391305910,391511812,391511681,491512107,
                                        391408047,391408494, -- and hundreds more 
                                )
                              AND  `UsersActivities`.`visible_for_group` in (0,3)
                          )
                   )
           )
    ORDER BY  `UsersActivities`.`created` desc 

enter image description here

ОБЪЯСНИТЬ JSON ФОРМАТ

{
  "query_block": {
    "select_id": 1,
    "ordering_operation": {
      "using_temporary_table": true,
      "using_filesort": true,
      "nested_loop": [
        {
          "table": {
            "table_name": "UsersActivities",
            "access_type": "range",
            "possible_keys": [
              "user_id",
              "activity_id",
              "member_id",
              "user_activity_id",
              "visible_for_group_idx"
            ],
            "key": "user_id",
            "used_key_parts": [
              "user_id"
            ],
            "key_length": "8",
            "rows": 25623,
            "filtered": 100,
            "index_condition": "((`project`.`usersactivities`.`user_id` = 390900002) or (`project`.`usersactivities`.`user_id` in ( *[HUNDREDS OF FRIEND IDS]* )))",
            "attached_condition": "((`project`.`usersactivities`.`user_activity_id` = 0) and (`project`.`usersactivities`.`member_id` = 0) and (((`project`.`usersactivities`.`user_id` = 390900002) and (`project`.`usersactivities`.`activity_id` not in (5,6))) or ((`project`.`usersactivities`.`user_id` in ( *[HUNDRED OF FRIEND IDS]* )) and (`project`.`usersactivities`.`visible_for_group` in (0,3)))))"
          }
        },
        {
          "table": {
            "table_name": "Users",
            "access_type": "eq_ref",
            "possible_keys": [
              "PRIMARY"
            ],
            "key": "PRIMARY",
            "used_key_parts": [
              "id"
            ],
            "key_length": "8",
            "ref": [
              "project.UsersActivities.user_id"
            ],
            "rows": 1,
            "filtered": 100,
            "attached_condition": "(`project`.`users`.`active` <> '2')"
          }
        },
        {
          "table": {
            "table_name": "Activities",
            "access_type": "ALL",
            "possible_keys": [
              "PRIMARY"
            ],
            "rows": 6,
            "filtered": 83.333,
            "using_join_buffer": "Block Nested Loop",
            "attached_condition": "<if>(is_not_null_compl(Activities), (`project`.`activities`.`id` = `project`.`usersactivities`.`activity_id`), true)"
          }
        },
        {
          "table": {
            "table_name": "Wallposts",
            "access_type": "eq_ref",
            "possible_keys": [
              "PRIMARY"
            ],
            "key": "PRIMARY",
            "used_key_parts": [
              "id"
            ],
            "key_length": "4",
            "ref": [
              "project.UsersActivities.wallpost_id"
            ],
            "rows": 1,
            "filtered": 100
          }
        },
        {
          "table": {
            "table_name": "Videos",
            "access_type": "eq_ref",
            "possible_keys": [
              "PRIMARY"
            ],
            "key": "PRIMARY",
            "used_key_parts": [
              "id"
            ],
            "key_length": "4",
            "ref": [
              "project.UsersActivities.video_id"
            ],
            "rows": 1,
            "filtered": 100
          }
        },
        {
          "table": {
            "table_name": "Pictures",
            "access_type": "eq_ref",
            "possible_keys": [
              "PRIMARY"
            ],
            "key": "PRIMARY",
            "used_key_parts": [
              "id"
            ],
            "key_length": "4",
            "ref": [
              "project.UsersActivities.picture_id"
            ],
            "rows": 1,
            "filtered": 100
          }
        },
        {
          "table": {
            "table_name": "Albums",
            "access_type": "eq_ref",
            "possible_keys": [
              "PRIMARY"
            ],
            "key": "PRIMARY",
            "used_key_parts": [
              "id"
            ],
            "key_length": "4",
            "ref": [
              "project.Pictures.album_id"
            ],
            "rows": 1,
            "filtered": 100
          }
        }
      ]
    }
  }
}

1 Ответ

2 голосов
/ 18 февраля 2020

В FrontEnd я использую foreach для l oop

Я перестал читать там. Вы должны быть в состоянии сделать один запрос SQL, который находит всех ваших друзей, JOIN по их действиям, и производит полный список действий, отсортированных по дате.

Возвращаясь к базам данных и обратно это дорого Сбор, переваривание, сортировка и т. Д. c. все необходимые данные обычно быстрее, если они выполняются на сервере базы данных и выполняются одним запросом.

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

Некоторые примечания к предоставленному вами запросу:

  • OR запрещает использование индексов, по крайней мере, для этой части.
  • NOT IN - то же самое.
  • Не используйте LEFT, если вы не ожидаете, что «правильный» стол будет грязным. Нам становится сложнее понять, что происходит, и выяснить, какие оптимизации / недоступны.
  • Будет ли применяться LIMIT? Кажется, что это может привести к огромному количеству данных. Будет ли нумерация страниц? Cake PHP собирает миллионы строк, а затем разделяет несколько? Плохие новости.

Рекомендуемый индекс:

UsersActivities:  (user_activity_id, member_id, created)

Если вы добавите этот индекс, укажите EXPLAIN, чтобы мы могли сравнить его с текущим EXPLAIN.

(Похоже, большинство JOINs находятся на id, что, я полагаю, является PRIMARY KEY соответствующих таблиц.)

Еще одно возможное улучшение: В настоящее время вы иметь

SELECT ...
    `Activities`.`name` AS `Activities__name`,
    ...
  left JOIN  `activities` `Activities`
         ON UsersActivities.activity_id = Activities.id
    ...

, и это, кажется, единственная ссылка на Activities. Кажется, что это отображение 1: многие, поэтому, возможно, будет улучшение как по скорости, так и по удобству:

SELECT ...
    ( SELECT GROUP_CONCAT(DISTINCT `name`) FROM activities
        WHERE id = UsersActivities.activity_id
    )  AS `Activities__name`,
    ...
  -- And leave out left JOIN  `activities` 
    ...
...