Oracle JDBC подготовил состояние плохой производительности со строковым параметром - PullRequest
1 голос
/ 03 июля 2019

То же самое при запуске в SQLDeveloper или через JDBC имеет совершенно другую производительность. Результат - всего одна строка.

SQLDeveloper 0,035 секунд Скриншот SqlDeveloper

JDBC 16 секунд. Снимок экрана JDBC

Я на одной машине и подключен к той же базе данных. Версия драйвера Oracle JDBC ojdbc6 11.2.0.3.

Если я изменю параметр на фильтр по первичному ключу, который долго работает, код выполняется быстро. Два столбца проиндексированы.

Определение таблицы Снимок экрана

Я уже пытался изменить версию драйвера oracle, и та же проблема возникает.

Connection conn = DriverManager.getConnection(url, "xxxx", "xxxx");
PreparedStatement preparedStatement = null;
ResultSet rs = null;

try {
System.out.println("Connected to database");

String consultaSQL = "SELECT SE.ID_SAIDA_ESTOQUE, SE.DATA_ULTIMA_ATUALIZACAO,  "
                + "SE.SITUACAO_VENDA, SE.ID_CLIENTE, SE.ID_ENTRADA_ESTOQUE_TROCA, SE.UUID, SE.ID_OPERACAO_MOVIMENTO, SE.ID_SETOR_ESTOQUE, SE.ID_EMPRESA, SE.ID_TERMINAL "
                + "FROM EST_SAIDA_ESTOQUE SE WHERE SE.UUID = ?"    
preparedStatement = conn.prepareStatement(consultaSQL);
preparedStatement.setQueryTimeout(40);
preparedStatement.setString(1, "000001c8-38d5-47c8-8e19-980c0c66e183");
//preparedStatement.setLong(1, 230998);
rs = preparedStatement.executeQuery();

1 Ответ

2 голосов
/ 03 июля 2019

Это всего лишь предположение, но хорошее (я думаю) и слишком длинное для комментария.

Я подозреваю, что произошла какая-то проблема преобразования набора символов, из-за которой база данных Oracleинтерпретировать значение привязки как NVARCHAR2.Результирующее неявное преобразование типов затем не позволяет Oracle использовать ваш индекс.

Вот краткий пример того, что я имею в виду:

Настройка

CREATE TABLE matt1 ( a varchar2(30) );

INSERT INTO matt1  SELECT dbms_random.string('X',20) FROM   dual CONNECT BY ROWNUM <= 50000;

COMMIT;

CREATE INDEX matt1_n1 ON matt1 (a);

Получитьпример значения

SELECT * FROM matt1 order by dbms_random.value fetch first 1 row only;

Я получил "UCBBTRAB0K8QV1UC8ERA" - вы получите другое значение, если вы попробуете это в своей собственной базе данных.

Имитируйте то, что вы делаете с помощью SQL * Developer:

EXPLAIN PLAN SET STATEMENT_ID='MM1' FOR
SELECT * FROM matt1 WHERE a = 'UCBBTRAB0K8QV1UC8ERA';

SELECT * 
FROM   TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE','MM1','ADVANCED'));
Plan hash value: 2474448389

----------------------------------------------------------------------------
| Id  | Operation        | Name     | Rows  | Bytes | Cost (%CPU)| Time    |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT |          |     1 |    17 |     1   (0)| 00:00:01|
|*  1 |  INDEX RANGE SCAN| MATT1_N1 |     1 |    17 |     1   (0)| 00:00:01|
----------------------------------------------------------------------------

Пока все хорошо.Индекс используется.

Имитация того, что я думаю происходит через JDBC

EXPLAIN PLAN SET STATEMENT_ID='MM2' FOR SELECT * FROM matt1 WHERE a =
N'UCBBTRAB0K8QV1UC8ERA';

SELECT *  FROM  
TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE','MM2','ADVANCED'));
Plan hash value: 1348340248

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       |     3 |    51 |    50  (10)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| MATT1 |     3 |    51 |    50  (10)| 00:00:01 |
---------------------------------------------------------------------------

... lots of stuff omitted...

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

   1 - filter(SYS_OP_C2C("A")=U'UCBBTRAB0K8QV1UC8ERA')

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

Как вы можете проверить это

Чтобы подтвердить, действительно ли это ваша проблема, измените JDBC SQL навключить отличительный комментарий.Например,

String consultaSQL = "SELECT /* THIS_IS_MY_JDBC_STATEMENT_1 */ SE.ID_SAIDA_ESTOQUE, SE.DATA_ULTIMA_ATUALIZACAO,  "
                + "SE.SITUACAO_VENDA, SE.ID_CLIENTE, SE.ID_ENTRADA_ESTOQUE_TROCA, SE.UUID, SE.ID_OPERACAO_MOVIMENTO, SE.ID_SETOR_ESTOQUE, SE.ID_EMPRESA, SE.ID_TERMINAL "
                + "FROM EST_SAIDA_ESTOQUE SE WHERE SE.UUID = ?"    

Затем запустите программу JDBC и посмотрите в базе данных, чтобы узнать, что Oracle сделал с ней.Сначала найдите выполненный оператор в библиотечном кэше, например:

select sql_id, child_number from gv$sql where sql_text like 
'%THIS_IS_MY_JDBC_STATEMENT_1%' and sql_text not like '%THIS_ONE%';

Затем используйте sql_id и child_number для просмотра плана.

SELECT *
FROM   TABLE (DBMS_XPLAN.display_cursor ('gyzm0fq259h5d' /* sql_id */,
                                         0 /* child_number */,
                                         'ADVANCED LAST'));

ЕслиВ плане указывается полное сканирование таблицы, и в предикатной информации есть функция SYS_OP_C2C (или аналогичная), тогда у вас есть объяснение.

Что вы можете с этим сделать

Самый простой способ, которымдолжно работать:

Измените свой JDBC SQL на этот:

String consultaSQL = "SELECT /* THIS_IS_MY_JDBC_STATEMENT_1 */ SE.ID_SAIDA_ESTOQUE, SE.DATA_ULTIMA_ATUALIZACAO,  "
                + "SE.SITUACAO_VENDA, SE.ID_CLIENTE, SE.ID_ENTRADA_ESTOQUE_TROCA, SE.UUID, SE.ID_OPERACAO_MOVIMENTO, SE.ID_SETOR_ESTOQUE, SE.ID_EMPRESA, SE.ID_TERMINAL "
                + "FROM EST_SAIDA_ESTOQUE SE WHERE SE.UUID = CAST(? AS VARCHAR2(255 CHAR))"    

(из OP: объясните план)

SqlDeveloper Screenshot JDBC Screenshot

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