2009-02-03 3 views
0

Моя система выполняет довольно тяжелую обработку, и я атакую ​​производительность, чтобы дать мне возможность запускать больше тестовых прогонов в короткие сроки.Работа с проблемами производительности UDF - Ручное кэширование

У меня довольно много случаев, когда UDF должен быть вызван, скажем, 5 миллионов строк (и я довольно много думал, что вокруг этого не было).

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

Рассмотрим UDF, который принимает набор входов и возвращает результат на основе сложной логики, но для набора входов на 5 м строк имеется всего 100 000 различных входов, скажем, и поэтому он будет производить только 100 000 различных результатов кортежи (мои конкретные случаи варьируются от процентных ставок до сложных присвоений кода, но все они дискретны - основной момент в этом методе состоит в том, что вы можете просто определить, будет ли трюк работать, запустив SELECT DISTINCT).

я обнаружил, что делать что-то вроде этого:

INSERT INTO PreCalcs 
SELECT param1 
     ,param2 
     ,dbo.udf_result(param1, param2) AS result 
FROM (
    SELECT DISTINCT param1, param2 FROM big_table 
) 

Когда PreCalcs соответствующим образом проиндексированы, сочетание этого с:

SELECT big_table.param1 
    ,big_table.param2 
    ,PreCalcs.result 
FROM big_table 
INNER JOIN PreCalcs 
    ON PreCalcs.param1 = big_table.param1 
    AND PreCalcs.param2 = big_table.param2 

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

Единственное, что вы должны следить за то, где NULL разрешены, то вам нужно исправить ваш присоединяется осторожно:

SELECT big_table.param1 
    ,big_table.param2 
    ,PreCalcs.result 
FROM big_table 
INNER JOIN PreCalcs 
    ON (
     PreCalcs.param1 = big_table.param1 
     OR COALESCE(PreCalcs.param1, big_table.param1) IS NULL 
    ) 
    AND (
     PreCalcs.param2 = big_table.param2 
     OR COALESCE(PreCalcs.param2, big_table.param2) IS NULL 
    ) 

Надежда это помогает и любые подобные трюки с UDF, или рефакторинга запросов для производительность приветствуется.

Я думаю, вопрос в том, почему такое кеширование вручную, как это необходимо - разве это не значит, что точка сервера знает, что функция детерминирована? И если это имеет большое значение, и если UDF настолько дороги, почему оптимизатор не делает это в плане выполнения?

+0

Что вы хотите сказать? –

+0

Я догадываюсь, вопрос в том, почему такое кеширование вручную, как это необходимо - разве это не значит, что точка сервера знает, что функция детерминирована? И если это имеет большое значение, и если UDF настолько дороги, почему оптимизатор не делает это в плане выполнения. –

ответ

1

Как SQL Server знает, что у вас есть 100 000 дискретных комбинаций в пределах 5 миллионов строк?

С помощью таблицы PreCalcs вы просто запускаете udf более 100 тыс. Строк, а не 5 миллионов строк, прежде чем снова отходить назад.

Ни один оптимизатор, существующий, не сможет объяснить эту полезную информацию. Скалярный udf - это черный ящик.

Для более практичного решения я бы использовал вычисленные, сохраненные столбцы, которые выполняют вызов udf. Таким образом, он доступен во всех запросах, которые могут быть проиндексированы/включены.

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

+0

Ну, это точно знает, так как это называется UDF 5 м раз. Какова точка базы данных, зная, что UDF является детерминированным, если это никогда не используется, чтобы избежать его повторения снова и снова с теми же параметрами? –

+0

Теперь я могу понять, если он кэширует определенное количество результатов и есть промахи в кеше, но, похоже, ничего не делает. –

+0

Он не кэширует. Детерминированный означает тот же вывод для одних и тех же входных данных (достаточно близко), но от строки до строки (5 миллионов) это не сохраняется. Оптимизатор не сохраняет эту информацию ». – gbn

3

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

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

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

+0

Да, к сожалению, все потенциальные параметры - проблема. Я пытаюсь переписать некоторые из этого кода, чтобы они были ориентированы на таблицы. Но в других случаях оригинальная логика очень устойчива к рефакторингу. Я нахожу, что UDF очень плохо выполняют до бесполезности. –

Смежные вопросы