Вы предоставили почти нет информации, необходимой для диагностики запроса.Поэтому невозможно дать вам ответ.Вместо этого я даю несколько советов, которые должны помочь вам решить проблему с производительностью.
Сначала вам нужно ...
Понять запрос
Лучший способ - нарисовать таблицы и пути соединения, как показано ниже.
![enter image description here](https://i.stack.imgur.com/VFd7T.jpg)
Вы видите, что маленькая таблица слева с 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