Производительность для RBAR против обработки на основе множеств с различными размерами транзакций - PullRequest
2 голосов
/ 03 сентября 2011

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

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

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

  • Размер рабочей нагрузки
  • Размер и рост файла журнала
  • Объем оперативной памяти
  • Скорость дисковой системы

Любой другой? Количество процессоров / процессорных ядер?

Пример 1. У меня есть таблица на 12 миллионов строк, и мне нужно обновить одно или два поля в каждой строке данными из другой таблицы. Если я сделаю это за одно простое ОБНОВЛЕНИЕ, это займет ~ 30 минут на моем тестовом боксе. Но я закончу через ~ 24 минуты, если я разделю это на двенадцать кусков - то есть .:

WHERE <key> BETWEEN 0 AND 1000000
WHERE <key> BETWEEN 1000000 AND 2000000
...

Пример 2: таблица из 200 с лишним миллионов строк, в которой также необходимо выполнить несколько вычислений практически для всех строк. Если сделать полный набор все в одном, мой ящик будет работать в течение трех дней и даже тогда не будет сделано. Если я напишу простой C # для выполнения точно такого же SQL, но с добавленными WHERE-предложениями для ограничения размера транзакции до 100 тыс. Строк за раз, это будет сделано за ~ 14 часов.

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

И нет, я не пробовал «настоящий» RBAR, хотя мне, вероятно, следовало бы - хотя бы было только посмотреть, сколько времени это действительно займет.

1 Ответ

3 голосов
/ 03 сентября 2011

Нет, не существует правила, что набор на основе всегда быстрее.У нас есть курсоры по какой-то причине (и не обманывайте себя, полагая, что цикл while или какой-то другой тип цикла действительно сильно отличается от курсора).Ицик Бен-Ган продемонстрировал несколько случаев, когда курсоры намного лучше, особенно для решения проблем с итоговыми значениями.Вы также описываете случаи, когда вы пытаетесь обновить 12 миллионов строк, и из-за ограничений памяти, использования журнала или других причин для SQL слишком сложно обрабатывать как одну операцию, не обращаясь к базе данных tempdb, или не соглашаться нанеоптимальный план из-за досрочного прекращения из-за недостаточно быстрого получения более оптимального плана.

Одна из причин, по которой курсоры получают плохой рэп, заключается в том, что люди ленивы и просто говорят:

DECLARE c CURSOR FOR SELECT ...

Когда они почти всегда должны говорить:

DECLARE c CURSOR 
    LOCAL FORWARD_ONLY STATIC READ_ONLY 
    FOR SELECT ...

Это потому, что эти дополнительные ключевые слова делают курсор более эффективным по разным причинам.Исходя из документации, можно ожидать, что некоторые из этих параметров будут избыточными, но в моем тестировании это не так.См. этот пост моего и этот пост от другого участника MVP по SQL Server Хьюго Корнелиса для получения более подробной информации.

Все это, в большинстве случаев, лучше всего делатьбудет основан на множествах (или, по крайней мере, на коренастых множествах, как вы описали выше).Но для одноразовых задач администратора (которые, я надеюсь, ваши 12 миллионов обновлений строк) иногда проще / эффективнее просто написать курсор, чем тратить много усилий на создание оптимального запроса, который дает адекватный план.Для запросов, которые будут часто выполняться как обычные операции в рамках приложения, стоит потратить больше усилий, чтобы попытаться оптимизировать их как наборы (имея в виду, что вы все равно можете получить курсор).

...