Я работаю над веб-приложением, которое запрашивает таблицы, содержащие большие объемы данных. Из-за проблем с производительностью пользовательского интерфейса - я изучал способы повышения производительности долго выполняющихся запросов.
Пожалуйста, смотрите ниже пример нашего оригинального кода и плана объяснения.
EXPLAIN PLAN FOR
SELECT * FROM T1
INNER JOIN T2 ON (T2.ID = T1.ID)
WHERE
T1.EMPLOYEE_ID = '1001'
AND T1.RUN_TIMESTAMP = '16-JAN-19 17.39.36.000000000'
-----------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 37183 | 31M| | 37654 (1)| 00:00:02 |
|* 1 | HASH JOIN | | 37183 | 31M| 6688K| 37654 (1)| 00:00:02 |
|* 2 | TABLE ACCESS BY INDEX ROWID BATCHED| T1 | 37183 | 6245K| | 2492 (1)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_T1_RT | 76305 | | | 410 (1)| 00:00:01 |
| 4 | TABLE ACCESS FULL | T2 | 577K| 399M| | 14704 (1)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------------------------
РАЗДЕЛЕННЫЙ СТОЛ
В попытке улучшить производительность запросов - я решил разделить большую таблицу T1 по значению. Было создано 45 разделов, которые охватывают 45 различных значений, которые выделяются каждой записи в таблице. Для целей этого примера - значения 1-45.
После переноса данных из T1 на разделенную вкладку T1_PART и предоставления отдельного ключа раздела - я был разочарован, увидев, что, несмотря на то, что теперь сканируется только один раздел, как и ожидалось. Экономические выгоды были лишь незначительными.
EXPLAIN PLAN FOR
SELECT * FROM T1_PART
INNER JOIN T2 ON (T2.ID = T1.ID)
WHERE
T1.EMPLOYEE_ID = '1001'
AND T1.PARTITION_KEY = '1'
AND T1.RUN_TIMESTAMP = '16-JAN-19 17.39.36.000000000'
----------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 19912 | 17M| | 37341 (1)| 00:00:02 | | |
|* 1 | HASH JOIN | | 19912 | 17M| 3680K| 37341 (1)| 00:00:02 | | |
| 2 | PARTITION LIST SINGLE | | 19912 | 3441K| | 2131 (1)| 00:00:01 | KEY | KEY |
|* 3 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| T1_PART | 19912 | 3441K| | 2131 (1)| 00:00:01 | 19 | 19 |
|* 4 | INDEX RANGE SCAN | IDX_T1_RT | 57355 | | | 276 (1)| 00:00:01 | 19 | 19 |
| 5 | TABLE ACCESS FULL | T2 | 577K| 403M| | 14706 (1)| 00:00:01 | | |
----------------------------------------------------------------------------------------------------------------------------------------------------
Однако, что меня действительно заинтересовало - это то, что когда я больше не указываю ключ раздела, стоимость запроса резко снижается.
Мы можем понять из плана, что теперь мы выполняем полное сканирование таблицы, но теперь выполняем, используя некоторую форму паралича, которую я не ожидал.
EXPLAIN PLAN FOR
SELECT * FROM T1_PART
INNER JOIN T2 ON (T2.ID = T1.ID)
WHERE
T1.EMPLOYEE_ID = '1001'
AND T1.RUN_TIMESTAMP = '16-JAN-19 17.39.36.000000000'
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 19912 | 17M| 16926 (1)| 00:00:01 | | | | | |
| 1 | PX COORDINATOR | | | | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10002 | 19912 | 17M| 16926 (1)| 00:00:01 | | | Q1,02 | P->S | QC (RAND) |
|* 3 | HASH JOIN BUFFERED | | 19912 | 17M| 16926 (1)| 00:00:01 | | | Q1,02 | PCWP | |
| 4 | PX RECEIVE | | 19912 | 3519K| 2217 (1)| 00:00:01 | | | Q1,02 | PCWP | |
| 5 | PX SEND HYBRID HASH | :TQ10000 | 19912 | 3519K| 2217 (1)| 00:00:01 | | | Q1,00 | P->P | HYBRID HASH|
| 6 | STATISTICS COLLECTOR | | | | | | | | Q1,00 | PCWC | |
| 7 | PX PARTITION LIST ALL | | 19912 | 3519K| 2217 (1)| 00:00:01 | 1 | 45 | Q1,00 | PCWC | |
|* 8 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| T1_PART | 19912 | 3519K| 2217 (1)| 00:00:01 | 1 | 45 | Q1,00 | PCWP | |
|* 9 | INDEX RANGE SCAN | IDX_T1_RT | 57355 | | 383 (1)| 00:00:01 | 1 | 45 | Q1,00 | PCWP | |
| 10 | PX RECEIVE | | 577K| 403M| 14706 (1)| 00:00:01 | | | Q1,02 | PCWP | |
| 11 | PX SEND HYBRID HASH | :TQ10001 | 577K| 403M| 14706 (1)| 00:00:01 | | | Q1,01 | S->P | HYBRID HASH|
| 12 | PX SELECTOR | | | | | | | | Q1,01 | SCWC | |
| 13 | TABLE ACCESS FULL | T2 | 577K| 403M| 14706 (1)| 00:00:01 | | | Q1,01 | SCWP | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Кто-нибудь знает, что здесь может происходить? Есть ли способ использовать только одно сканирование разделов при параллельном выполнении SQL?
Спасибо за помощь!
Билли
EDIT:
В этом примере ...
T1 имеет 625 417 строк
T2 имеет 577 718 строк
Все строки из T1 соответствуют условию соединения в T2
57 355 строк в T1 соответствуют T1.EMPLOYEE_ID = '1001' И T1.RUN_TIMESTAMP = '16 -JAN-19 17.39.36.000000000
T2 имеет уникальный индекс по идентификатору и не разделен