2009-10-23 3 views
3

Я уже более недели работаю над проблемой производительности хранимой процедуры и связан с другим моим сообщением в Stackoverflow here. Позвольте мне дать вам некоторую справочную информацию.Интригующая проблема настройки производительности SQL Server

У нас есть ночной процесс, который запускается и запускается хранимой процедурой, которая вызывает много многих других хранимых процедур. Многие вызываемые хранимые процедуры вызывают другие и т. Д. Я посмотрел на некоторые из вызванных процессов и там есть всевозможные пугающие сложные вещи, такие как обработка XML-строк, ненужное чрезмерное использование курсоров, подсказки NOLOCK чрезмерно используемые, редкое использование комплексной обработки и т. д. - список продолжается, это довольно ужасно.

Этот ночной процесс в нашей производственной среде занимает в среднем 1:15 для запуска. Иногда требуется 2 часа, что неприемлемо. Я создал тестовую среду на идентичном аппаратном обеспечении для производства и запускает proc. Потребовалось 45 минут, когда я его запустил. Если я восстановил базу данных в одну и ту же точку и запустил ее снова, это займет больше времени: действительно, если я повторю это действие несколько раз (восстановление и повторное выполнение), процесс занимает более продолжительное время, пока он не станет плато около 2 часов. Это действительно озадачивает меня, потому что я каждый раз восстанавливаю базу данных в одну и ту же точку. На сервере нет других пользовательских баз данных.

Я думал, что из двух направлений исследований, чтобы продолжить:

  1. планов запросов и параметры подмены
  2. базы данных Tempdb

В качестве теста, я перезагрузил SQL Server, чтобы очистить этот кэш и tempdb и перезапустить proc с тем же восстановлением базы данных. Процесс занял 45 минут. Я повторил это несколько раз, чтобы убедиться, что это повторяемо - снова это заняло 45 минут каждый раз. Затем я начал несколько тестов, чтобы попытаться изолировать загадочное увеличение времени работы, когда SQL Server не получает перезапущен:

  1. Выполнить начальную хранимую процедуру С RECOMPILE
  2. Перед выполнением процедуры, executre DBCC FREEPROCCACHE очистить из кэша процедур
  3. Перед выполнением процедуры, выполните CHECKPOINT с последующим DBCC DROPCLEANBUFFERS для того, чтобы кэш был пуст и чист
  4. Выполненный следующий сценарий, чтобы обеспечить все хранимые процедуры были отмечены для перекомпиляции:

    DECLARE @proc_schema SYSNAME 
    DECLARE @proc_name SYSNAME 
    
    DECLARE prcCsr CURSOR local 
        FOR SELECT specific_schema, 
           specific_name 
         FROM INFORMATION_SCHEMA.routines 
         WHERE routine_type = 'PROCEDURE' 
    
    OPEN prcCsr 
    
    FETCH NEXT FROM prcCsr INTO @proc_schema, @proc_name 
    
    DECLARE @stmt NVARCHAR(MAX) 
    WHILE @@FETCH_STATUS = 0 
        BEGIN 
         SET @stmt = N'exec sp_recompile ''[' + @proc_schema + '].[' 
          + @proc_name + ']''' 
    --  PRINT @stmt -- DEBUG 
         EXEC (@stmt 
          ) 
    
         FETCH NEXT FROM prcCsr INTO @proc_schema, @proc_name 
        END 
    

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

Я также исследовал tempdb и попытался очистить старые таблицы там, как описано в моей другой записи stackoverflow, но я не могу вручную очистить временные таблицы, созданные из переменных таблицы вручную, и они не кажутся хотеть исчезнуть самостоятельно (даже после того, как они оставят их в течение 24 часов).

Любое понимание или предложения по дальнейшему тестированию были бы весьма признательны. Я запускаю 64-разрядную версию SQL Server 2005 с пакетом обновления 3 (SP3) для Windows 2003 R2 Ent. .

С уважением, Mark.

+1

Вот предложение, которое суммирует все для меня. «Взгляд в код на этом этапе - это вариант, но реально его будет занимать 3-6 месяцев, чтобы оптимизировать его, поскольку там есть много возможностей для улучшения». – SQLMenace

+0

ОБНОВЛЕНИЕ : Я решил предложить щедрость человеку, который может понять, почему процедура увеличивается, когда я восстанавливаю базу данных в тот же момент времени и запускаю ее снова. Я могу исключить следующее: курсоры, подготовка/удаление XML, кеш плана запроса (я выполнил восстановление и выполнил DBCC FREEPROCCACHE). Благодаря всем, кто внес вклад, были действительно большие ответы, но пока ничего такого не было указано в этом странном поведении. –

+2

У вас есть процесс, который «настолько велик, что проверка планов запросов заставила мою SSMS генерировать ошибки из памяти», и ожидаете, что общие советы будут полезны? Почему вы не знаете, какая часть SQL становится медленнее? Кэши делают вещи быстрее. Оставлять вещи в tempdb/усекать их, а не удалять их быстрее. Почему вы думаете, что tempdb виноват? Вам нужно записать прошедшее время для каждого оператора SQL. Затем запустите его снова и посмотрите, какие заявления занимают больше времени. Тогда у вас есть куда начать. –

ответ

2

Одна вещь, которая может привести к этому, заключается в том, что процесс протекает по XML-документам. Это заставит SQL Server использовать больше памяти, а части этого могут быть записаны в файл страницы на диске, что приведет к замедлению процесса.

код, который создает документ XML выглядит следующим образом:

EXEC sp_xml_preparedocument @idoc OUTPUT, @strXML 

Это утечка, если нет соответствующих:

EXEC sp_xml_removedocument @idoc 

XML документы COM объекты, хранящиеся вне сконфигурированной памяти SQL Server. Даже если вы установите SQL Server на максимальный размер 5 ГБ, утечка XML-документов увеличит использование памяти за ее пределами.

+0

Я сильно подозреваю, что это может быть причиной, потому что многие из процессов, используемых в процессе, используют sp_xml_preparedocument. У меня будет проверка через sys.sql_modules и проверьте, указан ли sp_xml_removedocument для каждого случая. Спасибо за ваш ответ. –

+0

Andomar, в то время как у меня были большие надежды на это - я проверил все хранимые процедуры и каждый раз, когда вызывается sp_xml_preparedocument, есть соответствующий sp_xml_removedocument. Итак, я все еще ищу решение для увеличения времени выполнения, со статическим набором данных. –

+0

Вы контролируете использование памяти? Найдите рабочий набор, частный рабочий набор и размер фиксации. – Andomar

0

Что делает общий процесс, какова цель выполняемой операции?

Я бы предположил, что выполнение процесса приводит к модификации данных в базе данных. Это так?

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

Предполагая, что изменение в данных базы данных встречающийся, то вы должны также исследовать:

  • обновление соответствующей статистики базы данных между каждым процессом запуска.
  • Оценка уровня индекса фрагментация между каждым процессом запустите и определите, может ли дефрагментация быть доказанной.
+0

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

+0

Ах да, действительно. В этом случае следующим логическим шагом для меня будет проверка того, что одни и те же планы выполнения действительно используются. Я подозреваю, что они есть, но это нужно подтвердить в рамках ваших исследований. Учитывая объем процедур, выполняемых процессом, вам может потребоваться проверить кеш плана, чтобы проверить его. Кроме того, насколько велика база данных (GB) и какой тип хранилища вы используете –

+0

Джон, сложность процесса настолько велика, что проверка планов запросов заставила мой SSMS генерировать ошибки из памяти. Что вы думаете о проверке кеша плана? У вас есть хорошие стратегии? Процесс генерирует тысячи и тысячи планов запросов, поскольку тысячи запросов выполняются рекурсивно (в основном в курсорах). –

0

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

+0

У вас не так много курсоров. Поверьте мне, это займет месяцы. –

+2

Начните по одному, каждый, который вы удаляете, будет определять время. Выберите тот, который сначала обрабатывает наибольшее количество записей. – HLGEM

+0

Согласен. Вместо исправления процесса было бы лучше исправить это правильно. –

0

Я бы записал информацию в log_table и время, затраченное на выполнение каждого шага ..., что поможет вам сузить проблему, а также поможет вам постепенно улучшить процесс, решая его по одному (от улучшения процессов, которые возьмите самый длинный).

Лучший способ - просто вставить в начале и в конце каждой процедуры.

+0

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

+0

Вы можете сделать это в виде трассировки, и вам не придется вручную добавлять все виды ведения журнала. См. Мой ответ. – BradC

0

Курсоры не являются ускорителями торможения, другие называют это. (не ваше решение)

Посмотрите на использование/управление временными таблицами. Являются ли они глобальными временными таблицами или сессионными/локальными временными таблицами? То, что они висят вокруг, выглядит интересным. Tempdb заблокирован, когда создаются временные таблицы, которые могут быть частью проблемы.

Локальные таблицы темпа (синтаксис #mytable) должны уходить, когда сеанс выходит из области видимости, но вы ДОЛЖНЫ отбросить эти (освободить раньше), чтобы высвободить ресурсы.

Использование локальных временных таблиц в транзакции, а затем отмена без COMMIT/ROLLBACK может увеличить блокировку в tempdb, вызывая проблемы с производительностью. Говоря о транзакциях - это вызовет блокировки на syscolumns, sysindexes и т. Д., Если в транзакции создаются временные таблицы, поэтому другие исключения блокируются от использования одного и того же запроса.

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

ЕСЛИ вам нужны временные таблицы (чтобы устранить курсоры :), а затем избегайте SELECT INTO - чтобы избежать блокировок системных объектов.

Следует избегать использования глобальных временных таблиц (## myglobaltable синтаксиса), поскольку может возникать и выдавать несколько сеансов доступа (таблица висит вокруг до тех пор, пока все сеансы не будут очищены), и для меня, по крайней мере, не делает аддитивного логического предложения (вместо этого обратите внимание на использование постоянной таблицы). Вопрос, если глобальный, существуют ли блокирующие процедуры?

Есть много разреженных временных таблиц (растут с большими данными, но имеют меньшие наборы данных в них?)

Microsoft SQL Server Book Online, «Рассмотрите возможность использования таблицы переменных вместо временных таблиц. Временные таблицы полезны в случаях, когда индексы должны быть явно созданы на них или когда значения таблиц должны быть видимыми в нескольких хранимых процедурах или функциях. В общем случае переменные таблицы вносят вклад в более эффективную обработку запросов ».

Конечно, если для таблицы temp нужны индексы, переменные таблиц не являются опцией.

+0

Обратите внимание, что иногда переменные таблицы barf без видимых причин и переключение на временную таблицу устраняет проблему. У меня это случилось недавно. Хранимая процедура завершится с ошибкой после запуска в течение 20 минут, когда она будет работать быстро.Я переключился с переменной таблицы на временную таблицу (и никаких других изменений), и она вернулась к обычной полутора с половиной и успеху. – ErikE

+0

@ Emtucifor, да, выбор должен быть сделан с осторожностью и рассмотрим область действия, попытайтесь сохранить переменные таблицы в числовых рядов (100?), Например. –

2

Оценка всех сообщений, актуальных на данный момент, и связанный с этим вопрос, это, безусловно, звучит так, как будто ваше самое сильное лидерство - это тайна за этими объектами tempdb. Некоторые ведущие вопросы:

  • После нового запуска, после запуска процесса, сколько объектов находится в tempdb? Это то же количество после каждого нового старта?
  • Происходят ли цифры после «последовательных» пробегов? Они растут с одинаковой скоростью?
  • Можете ли вы определить, занимают ли они пространство?
  • В этом случае ваши файлы tempdb растут с каждым последующим запуском вашего процесса?

Я следил за ссылками, но не нашел ссылки на настоящую проблему. Возможно, вы захотите поднять эту проблему на форумах Microsoft SQL Technet here - они могут быть довольно хорошими с абстрактными материалами. (Если все остальное не удастся, вы можете открыть дело с технической поддержкой MS. Это может занять несколько дней, но шансы очень хорошие, что они все поймут. И если это ошибка MS, они возвратят ваши деньги!)

Вы сказали, что переписывание кода не является вариантом. Однако, если злоупотребление temp table является фактором, то идентификация и рефакторинг этих частей кода вначале может помочь. Чтобы найти, какие из них могут быть, запустите SQL Profiler во время выполнения вашего процесса. Такая работа, увы, субъективна и очень итеративна (это означает, что вы вряд ли когда-либо получите только правильный набор счетчиков на первом проходе). Некоторые мысли:

  • Начать с отслеживания SP: Начат, чтобы отслеживать, какие хранимые процедуры вызывают.
  • SQL Profiler может использоваться для группировки данных; это неудобно, и я не уверен, как описать его в простом тексте, но настроенный правильно, вы получите дисплей Profiler, показывающий количество раз, когда выполнялась каждая процедура. В идеале это показало бы наиболее часто называемые procs, и вы можете проанализировать их для злоупотребления и рефакторинга temp по мере необходимости.
  • Если ничего не выпрыгивает, вы можете проследить SP: StmtStarting и сделать то же самое для отдельных утверждений. Проблема здесь в том, что при запуске спагетти в 2 +/- час может закончиться нехватка дискового пространства, и анализ 100 МБ данных трассировки может стать кошмаром. (Подсказка: загрузите его в таблицу, создайте индексы, затем аккуратно удалите крут.) Опять же, целью было бы идентифицировать чрезмерно используемый/злоупотребляемый код таблицы temp, который нужно реорганизовать.
+0

Hi Philip. В tempdb остаются 1124 объекта, все из которых созданы из переменных таблицы. Это то же самое число каждый раз. Я выполнил трассировку на стороне сервера и опубликовал свои результаты в ответ на вопрос. –

0

У меня нет ответа, но некоторые идеи о том, что я сделал бы, чтобы изолировать такие проблемы.

Во-первых, я сделал бы снимки sys.dm_os_wait_stats до и после каждого исполнения. Вы вычитаете 2 моментальных снимка (получите дельта) и посмотрите, является ли какой-либо конкретный WAIT заметным или ухудшается с каждым прогоном. Простой способ расчета дельт - скопировать значения sys.dm_os_wait_stats в таблицы Excel и использовать VLOOKUP() для вычитания соответствующих значений. Я использовал эту методику исследования сотен раз. Вы не знаете, в каком аспекте SQL Server завис? Позвольте SQL Server «рассказать» вам через sys.dm_os_wait_stats!

Другая вещь, которую я могу попробовать, - это настроить поведение цикла, чтобы понять, имеют ли последующие более медленные исполнения постоянную пропускную способность для всех записей от начала до конца или это замедляет только определенные sproc (s) в INFORMATION_SCHEMA. подпрограмм ... 2 метода для изучения этого:

1) Добавьте «верхнее N» предложение SQL SELECT, такое как «top 100» или «top 1000» (создайте искусственный лимит), чтобы увидеть, замедления для всех сценариев подсчета записей ... или ... вы получаете только замедление, когда набор результатов курсора достаточно велик, чтобы включить оскорбительный sproc.

2) Вместо добавления «верхнего N» вы можете добавить больше операторов печати (контрольно-измерительные приборы) для расчета пропускной способности по мере ее обработки.

Конечно, вы можете сделать комбинацию обоих.

Возможно, эта диагностика приблизит вас к первопричине.

Отредактировано для добавления: Btw, SQL2008 имеет новый монитор производительности, который позволяет легко «заглядывать» в число sys.dm_os_wait_stats. Однако для SQL2005 вам придется вручную вычислять deltas через Excel или скрипт.

2

Mark-

Так это может занять 3-6 месяцев, чтобы полностью переписать эту процедуру, но это не значит, что вы не можете сделать некоторые относительно быстрой оптимизации производительности.

Некоторые из подпрограмм, которые я должен поддерживать, запускают 30 часов +, я был бы в восторге, чтобы заставить их работать в 2 часа! вид оптимизации, которые вы делаете на эти процедуры немного отличается от вашей обычной базы данных OLTP:

  1. Захват следа всего процесса, убедившись в том, чтобы захватить SP: StmtCompleted и SQL: StmtCompleted события. Не забудьте установить фильтр на длительность (> 10 мс или что-то еще), чтобы устранить все быстрые, несущественные утверждения.

  2. Потяните эту трассировку в таблицу и выполните некоторую фильтрацию/сортировку/группировку, сосредоточив внимание на длительности и чтении. Вы, вероятно, в конечном итоге с одной из двух ситуаций:

    (А) кучки отдельных запросов/заявлений ответственны за большую часть времени процедуры (хорошие новости)

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

В сценарии (A) просто сосредоточьте свое внимание на этих запросах. Оптимизируйте их с помощью индексов или используя другие стандартные методы. Я очень рекомендую книгу Дэна Буша «SQL Tuning» для мощного метода оптимизации запросов, особенно грязных, со сложными объединениями.

В сценарии (B) немного отступите и посмотрите на утверждений в целом. Все ли они похожи друг на друга? Можете ли вы добавить индекс на ключ, общую таблицу, которая улучшит их все? Можете ли вы исключить цикл, который выполняет 10 000 динамических запросов, и вместо этого сделать один запрос на основе набора?

Еще две другие возможности, я полагаю:

(C) 15000 совершенно разные динамические операторы SQL, каждый из которых требует своей собственной кропотливой оптимизации. В этом случае попытайтесь сосредоточиться на оптимизации на уровне сервера, например, улучшения на основе ввода-вывода, которые принесут им пользу.

(D) Что-то еще странное происходит с помощью TempDB или чего-то неправильно настроенного на сервере. Не так много, что я могу здесь сказать, кроме как найти проблему и исправить ее!

Надеюсь, это поможет.

+0

Отличный ответ Брэд - см. Мой ответ на мой вопрос. –

1

Выполните следующий скрипт в начале теста, а затем после каждой итерации:

select sum(single_pages_kb) as sum_bp_kb 
    , sum(multi_pages_kb) as sum_va_kb 
    , type 
from sys.dm_os_memory_clerks 
group by type 
having sum(single_pages_kb+multi_pages_kb) > 16 
order by sum(single_pages_kb+multi_pages_kb) desc 

select sum(total_pages), type_desc 
from tempdb.sys.allocation_units 
group by type_desc; 

select * from sys.dm_os_performance_counters 
where counter_name in (
    'Log Truncations' 
    ,'Log Growths' 
    ,'Log Shrinks' 
    ,'Data File(s) Size (KB)' 
    ,'Log File(s) Size (KB)' 
    ,'Active Temp Tables'); 

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

+0

Спасибо, Ремус. Я попробую это в будущем, когда у меня будет время. У меня есть еще одна работа, которую нужно сделать прямо сейчас. Похоже, это может отделить вещи для меня, и я кое-что узнал из ваших запросов! –

2

Вы можете попробовать следующий сценарий на тестовом сервере:

  1. Сделайте две копии базы данных на сервере: [A] и [B]. [A] - соответствующая база данных, [B] - копия.
  2. рестарта
  3. Запустите ваш процесс
  4. падение базы данных [A]
  5. Rename [B] на [A]
  6. запустить свой процесс

Это было бы как горячие базы данных замена. Если второй запуск занимает больше времени, происходит что-то на уровне сервера (tempdb, память, ввод-вывод и т. Д.). Если второй запуск занимает примерно одно и то же время, проблема возникает на уровне базы данных (блокировки, фрагментация индекса и т. Д.).

Удачи вам!

0

Эти длинные кадры:

  • Быстро просматривать все хранимых процедур для вещей, которые являются необычным и SQL Server не должен быть действительно делают, например, отправка электронной почты или записи файлов и т.д. SQL, пытающийся отправить электронную почту на не существующий почтовый сервер, может вызвать задержки.
  • Другая вещь, чтобы иметь в виду, что в восстановлении базы данных перед каждым испытанием, возможно, ваш диск становится все более фрагментированным (не действительно уверены в этом, хотя). Итак, , которые могут объяснить, почему время запуска увеличивается до тех пор, пока они не станут плато.
0

Во-первых, спасибо всем за помощь. Я очень ценю ваше время и знания, помогая мне решить эту очень странную проблему. У меня есть обновление.

Я начал трассировку на стороне сервера, чтобы попытаться изолировать сохраненные процессы, которые выполнялись медленнее между итерациями. Меня это удивило. В процессе участвуют 96 хранимых процедур. Большинство из этих хранимых процедур выполнялись медленнее во второй раз - около 50 из них. Остальные были очень быстрыми, чтобы бежать и не влияли на общее время вообще, и на самом деле некоторые из них бежали немного быстрее (как и следовало ожидать).

Я провалил экземпляр базы данных на другой узел в моем кластере и провел там тесты с такими же результатами - поэтому я могу исключить любые различия ОС между узлами кластера - при создании кластеров, которые я очень сознавал, чтобы их создавать одинаково.

1100 temp tables создаются во время процесса и сохраняются после его завершения - это все переменные таблицы, и я нашел способ их удалить. Запуск sp_recompile для каждой функции proc и функции в базе данных заставил все временные таблицы очищаться. Однако это не улучшить время выполнения вообще. Единственное, что помогает время выполнения, это перезапуск службы SQL Server. К сожалению, я сейчас не в курсе, чтобы исследовать это дальше - у меня есть другая работа, но я хотел бы продолжать это. Возможно, я вернусь к нему позже, если у меня появятся свободные часы. Тем временем, однако, я должен признать поражение без решения и без щедрот дать.

Еще раз спасибо.

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