отложенные ответы от Oracle, вызванные анализом SQL - PullRequest
7 голосов
/ 07 марта 2019

У меня есть метод веб-службы Java getCardInformation, который получает данные из базы данных Oracle, используя JDBC. Около 300 звонков в секунду.

Java вызывает эту функцию pl / sql:

PROCEDURE GET_CARDS_BY_ID(p_card_id IN NUMBER
                         ,o_result  OUT VARCHAR2) IS
BEGIN

  SELECT 'some data'
    INTO o_result
    FROM 'my complicated SQL' c
   WHERE c.card_id = p_card_id;

END GET_CARDS_BY_ID;

Обычно SQL Select statement/procedure возвращает результат в течение 30 мс, но иногда бывают случаи, когда требуется более 20 секунд, и все другие потоки (вызовы) ждут этого отложенного вызова. Хотя эти вызовы не связаны друг с другом, они запускают один и тот же SQL Select statement с разными входными параметрами. Все параметры устанавливаются из Java с использованием bind variables.

Во время задержки мы выбрали активные сеансы и получили множество курсора : контакт S wait на X

    SELECT DISTINCT a.*, s.*
  FROM V$ACTIVE_SESSION_HISTORY a
      ,v$sql                    s
 WHERE a.sql_id = s.sql_id
   AND blocking_session IS NOT NULL
   AND a.sample_time > sysdate - 1

enter image description here

Мы решили проблему, добавив hint к нашему Select. Мы думаем, что во время этой задержки Oracle пытается проанализировать Select Statement и re для расчета плана, который требует некоторого времени. Все остальные вызовы, использующие тот же оператор, ожидают, пока Oracle завершит вычисление. Это причина задержки. Когда мы установили подсказку, мы сказали оракулу не делать никаких вычислений, поэтому задержки не было.

Но почему Oracle пытается пересчитать план?

Можно ли решить эту проблему без жесткого кодирования индекса?

Когда объем моих данных изменяется в базе данных, я не хочу менять свой код из-за этой подсказки.

Оператор SQL:

    SELECT (nvl((SELECT SUM(amount)
          FROM locks l
         WHERE l.account = a.account),
        0) - nvl((SELECT SUM(sl.amount)
                    FROM locks   sl
                        ,locks_2 m
                   WHERE sl.id = m.id
                     AND sl.account = a.account
                     AND m.text = '...'),
                  0)) / 100
  ,a.credit_limit / 100
  ,nvl((SELECT SUM(decode(ia.account,
                         ra.id,
                         ia.bal,
                         ceil(CASE
                                WHEN cc.ccy_num = '100' THEN
                                 (SELECT ia.bal / r.ccy_rate
                                    FROM my_ccy_rate r
                                   WHERE r.ccy_num = a.account_ccy)
                                WHEN a.account_ccy = '100' THEN
                                 (SELECT ia.bal * r.ccy_rate
                                    FROM my_ccy_rates r
                                   WHERE r.ccy_num = cc.ccy_num)
                                ELSE
                                 (SELECT ia.bal * r.ccy_rate / c.ccy_rate + 15000
                                    FROM my_ccy_rates r
                                        ,my_ccy_rates c
                                   WHERE r.ccy_num = cc.ccy_num
                                     AND c.ccy_num = a.account_ccy)
                              END)))
         FROM my_v           ia
             ,currency_codes cc
             ,my_table       pe
        WHERE ia.account = a.account_id
          AND cc.ccy_alpha = ia.ccy),
       0) / 100
  ,a.initial_amount / 100
  ,nvl((SELECT a.bonus_amount - nvl((SELECT SUM(sl.amount)
                                     FROM locks   sl
                                         ,locks_2 m
                                    WHERE sl.id = m.id
                                      AND sl.account_id = a.account_id
                                      AND m.text = '...'),
                                   0)
         FROM my_table_1 ra
             ,my_accounts  a
        WHERE ra.centre_id = o_centre_id
          AND ra.card_number = o_card
          AND a.centre_id = ra.centre_id
          AND a.account_id = ra.account_id),
       0) / 100
INTO o_amt_1
  ,o_amt_2
  ,o_amt_3
  ,o_amt_4
  ,o_amt_5
FROM my_table_1 ra
  ,my_table_2 a
  ,my_table_3 p
WHERE ra.card_number = &card_number_input_parameter
  AND a.account_id = ra.account_id;

Ответы [ 2 ]

1 голос
/ 16 марта 2019

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

Эта часть кода может вызывать проблемы:

SELECT ... (SELECT ... (SELECT ... = a.account_ccy ... ) ... )
...
FROM ...
  ,my_table_2 a

Предположительно, стандарт ANSI SQL ограничивает ссылки на таблицы только одним уровнем.(Я говорю, предположительно, потому что стандарт, к сожалению, не является свободно доступным, поэтому я не могу убедиться в этом сам.) У Oracle была плохая история периодического применения этого правила.Запросы, подобные приведенному выше, не выполнялись в большинстве версий 9, 10 и 11. Но эти запросы работали как минимум в одной версии 10 (а затем не работали при обновлении до более поздней версии 10), и было сделано исправлениеони работают в 11, и они снова работают в 12.

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

0 голосов
/ 15 марта 2019

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

PROCEDURE GET_CARDS_BY_ID(p_card_id IN NUMBER ,o_result  OUT VARCHAR2) IS
BEGIN
  v_sql := 'SELECT 'some data'  INTO o_result  FROM 'my complicated SQL' c
     WHERE c.card_id = :1';
  execute immediate v_sql into o_result using p_card;
END GET_CARDS_BY_ID;
...