Запрос медленный при соединении таблицы из ссылок БД - PullRequest
0 голосов
/ 11 мая 2018

Если честно, я не уверен, что это связано с объединением таблиц с ссылками на БД.

Вот мой запрос:

SELECT
DISTINCT
AA.NAME,
DD.TOURNAMENT_TIME, 
BB.CLUB_TYPE 
FROM
ROOT.test_adviser@123test.com CC
INNER JOIN 
ROOT.test_club@123test.com BB
ON BB.adviser_id = CC.adviser_id
INNER JOIN ROOT.test_student@123test.com AA
ON AA.student_id = BB.student_id
INNER JOIN test_tournament DD
ON DD.tournament_id = AA.tournament_id AND DD.slot_number = BB.slot_number

Запрос выше выполняется в течение 20 минут.Если это из-за неэффективного использования JOIN , есть ли какие-либо идеи, как я могу очистить запрос?Любая помощь приветствуется.

Другая информация

Таблица DD - имеет 7 строк (Это число, которое я надеюсь вернуть со столбцами из других таблицобъединено)

Таблица AA, CC, BB - содержит более 60 тыс. строк.

Ответы [ 2 ]

0 голосов
/ 12 мая 2018

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

Сначала вам нужно ...

Понять запрос

Лучший способ - нарисовать таблицы и пути соединения, как показано ниже.

enter image description here

Вы видите, что маленькая таблица слева с 7 строками (локальнаяsite) объединяется с двумя удаленными таблицами, которые все объединены.Как Oracle может выполнить это объединение?Есть две возможности.

Самый простой способ - Oracle обрабатывает семь строк локальная таблица в объединении вложенного цикла и получает необходимые записи с помощью удаленного запроса.

Второй способ - передать 7 строк на удаленный сайт и выполнить там весь запрос.

Чтобы увидеть, какой из вариантов лучше, сначала нужно ...

Понимание плана выполнения

Используя EXPLAIN PLAN, вы можете проверить, что на самом деле делает Oracle.Я показываю план для сгенерированных данных на основе вашего описания.

EXPLAIN PLAN  SET STATEMENT_ID = 'jara1' into   plan_table  FOR
SELECT /*+  driving_site(CC) USE_HASH(AA BB CC)  */
DISTINCT
AA.NAME,
DD.TOURNAMENT_TIME, 
BB.CLUB_TYPE 
FROM
test_adviser@MY_LINK CC
INNER JOIN 
test_club@MY_LINK BB
ON BB.adviser_id = CC.adviser_id
INNER JOIN test_student@MY_LINK AA
ON AA.student_id = BB.student_id
INNER JOIN test_tournament DD
ON DD.tournament_id = AA.tournament_id AND DD.slot_number = BB.slot_number;


SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL')); 


---------------------------------------------------------------------------------------------------------
| Id  | Operation             | Name            | Rows  | Bytes | Cost (%CPU)| Time     | Inst   |IN-OUT|
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |                 |     1 |    88 |    33   (4)| 00:00:01 |        |      |
|   1 |  HASH UNIQUE          |                 |     1 |    88 |    33   (4)| 00:00:01 |        |      |
|   2 |   NESTED LOOPS        |                 |     1 |    88 |    32   (0)| 00:00:01 |        |      |
|   3 |    NESTED LOOPS       |                 |     1 |    75 |    31   (0)| 00:00:01 |        |      |
|   4 |     NESTED LOOPS      |                 |     7 |   217 |    17   (0)| 00:00:01 |        |      |
|   5 |      TABLE ACCESS FULL| TEST_TOURNAMENT |     7 |    98 |     3   (0)| 00:00:01 |        |      |
|   6 |      REMOTE           | TEST_STUDENT    |     1 |    17 |     2   (0)| 00:00:01 | MY_LI~ | R->S |
|   7 |     REMOTE            | TEST_CLUB       |     1 |    44 |     2   (0)| 00:00:01 | MY_LI~ | R->S |
|   8 |    REMOTE             | TEST_ADVISER    |     1 |    13 |     1   (0)| 00:00:01 | MY_LI~ | R->S |
---------------------------------------------------------------------------------------------------------

Remote SQL Information (identified by operation id):
----------------------------------------------------

   6 - SELECT "STUDENT_ID","TOURNAMENT_ID","NAME" FROM "TEST_STUDENT" "AA" WHERE 
       :1="TOURNAMENT_ID" (accessing 'MY_LINK' )

   7 - SELECT "STUDENT_ID","SLOT_NUMBER","ADVISER_ID","CLUB_TYPE" FROM "TEST_CLUB" "BB" WHERE 
       :1="SLOT_NUMBER" AND :2="STUDENT_ID" (accessing 'MY_LINK' )

   8 - SELECT "ADVISER_ID" FROM "TEST_ADVISER" "CC" WHERE :1="ADVISER_ID" (accessing 'MY_LINK' )

Итак, вы видите, что это первый вариант , описанный выше.Локальные NESTED LOOP с, а затем удаленный доступ для каждого ID.

Определение индекса

Обратите внимание, что эти индексы необходимы для этого запроса

create index test_student_idx1 on test_student(tournament_id);
create index test_club_idx1 on test_club(slot_number);
create index test_club_idx2 on test_club(student_id);
create index test_adviser_IDX1 on test_adviser(adviser_id);

Это хороший выбор?Ну, это может быть - в случае, если удаленные запросы возвращают очень небольшое количество строк.

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

Вы можете быстро проверить его с помощью следующего запроса:

select AA.tournament_id, count(*) from test_student@MY_LINK AA
INNER JOIN test_tournament DD
ON DD.tournament_id = AA.tournament_id
group by aa.tournament_id;

Если запрос (и аналогичный для таблицы CLUB) возвращает большое количество строк (что подозрительно), этот доступ не оптимально .Например, если вы видите 1000 записей на ключ в обоих запросах, вы можете ожидать один миллион (1000 * 1000) вызовов удаленного доступа.

В этом случае вам необходимо переключиться на второй вариант - завершить удаленный запрос.Подсказка DRIVING_SITE - это ваш друг.Подсказка указывает, что весь запрос должен выполняться на удаленном сайте.Таким образом, таблица test_tournament будет перемещена на удаленный сайт и там будет выполнен запрос.

Единственное отличие состоит в том, что Oracle выполняет не такую ​​сложную оптимизацию плана выполнения, как в случае локального запроса.

Возможно, даже это будетпривести к неоптимальному плану (хотя лучше, чем предыдущий) с эффектом , умирающим во вложенном цикле .Чем вы просто принудительно используете HASH_JOIN, используя подсказку.

Поэтому добавьте к вашему запросу один из следующих советов и наблюдайте за его поведением.

 SELECT /*+  driving_site(CC) */

 SELECT /*+  driving_site(CC) USE_HASH(AA BB CC) */

Тест, который лучше для ваших данных.

Это план выполнения с использованием соединения HASH.Обратите внимание, что план выполняется полностью на удаленном сайте.Планы с HASH JOIN не возвращаются мгновенно, но большое преимущество заключается в том, что они не заканчиваются чрезвычайно длинными истекшими временами, как в случае соединений с большим объемом NESTED LOOP s.

------------------------------------------------------------------------------------------------------------
| Id  | Operation              | Name              | Rows  | Bytes | Cost (%CPU)| Time     | Inst   |IN-OUT|
------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT REMOTE|                   |     1 |    55 |  1328   (1)| 00:00:01 |        |      |
|   1 |  HASH UNIQUE           |                   |     1 |    55 |  1328   (1)| 00:00:01 |        |      |
|*  2 |   HASH JOIN SEMI       |                   |     1 |    55 |  1327   (1)| 00:00:01 |        |      |
|*  3 |    HASH JOIN           |                   |     1 |    50 |  1289   (1)| 00:00:01 |        |      |
|*  4 |     HASH JOIN          |                   |     8 |   264 |  1148   (1)| 00:00:01 |        |      |
|   5 |      REMOTE            | TEST_TOURNAMENT   |     7 |    98 |     2   (0)| 00:00:01 |      ! | R->S |
|   6 |      TABLE ACCESS FULL | TEST_CLUB         |   130K|  2412K|  1146   (1)| 00:00:01 | REMOT~ |      |
|   7 |     TABLE ACCESS FULL  | TEST_STUDENT      |   130K|  2158K|   141   (1)| 00:00:01 | REMOT~ |      |
|   8 |    INDEX FAST FULL SCAN| TEST_ADVISER_IDX1 | 60000 |   292K|    38   (0)| 00:00:01 | REMOT~ |      |
------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("A3"."ADVISER_ID"="A4"."ADVISER_ID")
   3 - access("A1"."TOURNAMENT_ID"="A2"."TOURNAMENT_ID" AND "A2"."STUDENT_ID"="A3"."STUDENT_ID")
   4 - access("A1"."SLOT_NUMBER"="A3"."SLOT_NUMBER")


Remote SQL Information (identified by operation id):
----------------------------------------------------

   5 - SELECT "TOURNAMENT_ID","SLOT_NUMBER","TOURNAMENT_TIME" FROM "TEST_TOURNAMENT" "A1" 
       (accessing '!' )


Note
-----
   - fully remote statement
0 голосов
/ 11 мая 2018

Вы можете попытаться создать локальные копии всех таблиц, доступных по ссылке db

create table test_anviser as
  select * from ROOT.test_adviser@123test.com;

Подумайте, если эти тестовые таблицы в классовой среде не должны быть слишком большими.

Затем замените каждое имя таблицы на @ db-link просто именем таблицы

ROOT.test_adviser@123test.com -> test_adviser

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

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