|
|
|||||||||||||||||||||||||||||
|
SQL Server: Распараллеленный ПросмотрИсточник: msmvpscom Ирина Наумова, Александр Гладченко
В этой статье я собираюсь рассмотреть то, как SQL Server распараллеливает просмотр таблицы (сканирования - scans). Оператор просмотра - один из немногих операторов, которые адаптированы к параллелизму. Большинство других операторов ничего не знают о параллелизме, и не заботятся о том, выполняются ли они параллельно; оператор просмотра является в этом случае исключением. Как же в действительности работает распараллеленный просмотр? Потоки, которые составляют распараллеленный просмотр, сообща трудятся над тем, чтобы выполнить полный просмотр всех строк в таблице. Априори, нет никакого явного закрепления строк или страниц за конкретными потоками. Вместо этого движок хранилища раздаёт страницы потокам динамически. Доступ к страницам таблицы координирует поставщик распараллеленных страниц (parallel page supplier). Он гарантирует, что каждая страница будет отдана только одному потоку и, таким образом, попадёт на обработку только один раз. В начале распараллеленного просмотра каждый поток запрашивает у поставщика несколько страниц. После этого, потоки начинают обработку полученных строк и возвращают обработанные строки на выходе оператора. Когда поток заканчивает обработку полученного набора строк, он запрашивает у поставщика следующий набор. У этого алгоритма есть пара преимуществ: Примеры Давайте начнём с простого примера. Чтобы получить распараллеленный план, нам понадобится довольно большая таблица; если таблица будет слишком маленькой, то оптимизатор может прийти к заключению, что лучше подходит последовательный план исполнения. Показанный ниже сценарий создаёт таблицу из 1000000 строк, которые (благодаря фиксированной длине столбца char (200)) займут приблизительно 27000 страниц. set nocount on После этого, для самого простого запроса: select * from T /--Table Scan(OBJECT:([T])) Мы получаем последовательный план! Почему же мы не получили распараллеленный план? Параллелизм используется для ускорения запросов, за счёт использования нескольких процессоров в одной задаче. Стоимость же этого запроса зависит от стоимости чтения страниц с диска (которая облегчается упреждающим чтением, а не параллелизмом), и последующего возврата обработанных строк клиенту. Для исполнения этого запроса используется сравнительно небольшое число процессорных циклов и, фактически, вероятно он работал бы медленнее, если бы мы его распараллелили. Если мы добавим в запрос хороший с точки зрения селективности предикат, то мы можем получить распараллеленный план: select * from T where a < 1000 /--Parallelism(Gather Streams) Выполняя этот запрос параллельно, мы можем распределить затраты по оценке предиката по нескольким процессорам (впрочем, в этом примере, предикат обходится достаточно дёшево, и, вероятно, не имеет большого значения для времени исполнения, будет ли запрос работать параллельно или нет). Балансировка нагрузки Как я писал выше, алгоритм распараллеленного просмотра выделяет страницы потокам динамически. Мы можем наблюдать это с помощью следующего примера. Рассмотрим запрос, который возвращает все строки таблицы: select * from T where a % 2 = 0 or a % 2 = 1 "Хитрый" предикат запутывает оптимизатор, который неправильно оценивает количество элементов и генерирует параллельный план: /--Parallelism(Gather Streams) На SQL Server 2005 используя "SET STATISTICS XML ON" мы можем узнать, сколько строк обрабатывает каждый поток. Вот результирующий XML для двухпроцессорной системы: <RelOp NodeId="2" PhysicalOp="Table Scan" LogicalOp="Table Scan" ...> Как видно, оба потока (1 и 2), обрабатывают примерно половину строк. Поток 0 является координатором, или ещё его называют основным потоком. Он выполняет только ту часть плана исполнения запроса, которая выше самого верхнего итератора обмена. Таким образом, мы не ожидаем, что какие либо строки будут обработаны ещё какими-либо операторами с распараллеливанием. select min(T1.a + T2.a) from T T1 cross join T T2 option(maxdop 1) Такой последовательный запрос использует только один из двух процессоров. Давайте снова выполним другой запрос во время его работы: select * from T where a % 2 = 0 or a % 2 = 1 <RelOp NodeId="2" PhysicalOp="Table Scan" LogicalOp="Table Scan" ...> На этот раз распараллеленный поток с идентификатором 1 обработал больше 90% строк, в то время как поток 2, который был занят исполнением показанного выше запроса с последовательным планом, обработал заметно меньше строк. Распараллеленный просмотр автоматически сбалансировал работу между двумя потоками. Так как у потока 1 было больше свободных циклов (он не конкурировал с последовательным планом), он запросил и просмотрел больше страниц. По материалам статьи Craig Freedman: Parallel Scan Перевод Ирины Наумовой и Александра Гладченко Ссылки по теме
|
|