SQL.RU
 client/server technologies
 Главная | Документация | Статьи | Книги | Форум | Блоги | Опросы | Гостевая | Рассылка | Работа | Поиск | FAQ |

Погружение в SQL Server 2000 User Mode Scheduler

ПУБЛИКАЦИИ  

По материалам статьи Ken Henderson: Inside the SQL Server 2000 User Mode Scheduler. 24 февраля 2004г. Библиотека MSDN.
В статье даётся развёрнутое описание используемого в работе SQL Server 2000 планировщика непривилегированного режима User Mode Scheduler (UMS), которое ориентировано на то, чтобы помочь разработчикам повысить эффективность своих приложений. UMS повышает масштабируемость сервера и предоставляет больше возможностей управления, чем это делает штатный планировщик Windows. Эта статья является выдержкой из книги The Guru's Guide to SQL Server Architecture and Internals (Addison-Wesley, 2003).

СОДЕРЖАНИЕ

Введение

По версию 6.5 включительно, SQL Server использовал средства планирования Windows, которые планировали обслуживание рабочих потоков, переключения между потоками и всю работу многозадачного режима. Всё это работало довольно хорошо и позволяло SQL Server задействовать все возможности масштабируемости Windows и эффективно использовать процессоры. Однако, между версиями 6.5 и 7.0 стало очевидно, что SQL Server достиг "потолка" масштабируемости. Его возможностям обработки тысяч параллельных подключений и эффективному масштабированию на многопроцессорных системах стал препятствовать тот факт, что планировщик Windows обслуживал SQL Server точно так же, как и любое другое приложение. Вопреки ходившим тогда слухам, SQL Server 6.5 не использовал никаких скрытых API для достижения присущего ему уровня масштабируемость и производительности. Он использовал базовый поток и примитивы синхронизации потока, которые задействуются для любых многопоточных приложений Windows, и Windows переключала рабочие потоки SQL Server для процессора (ов) точно так же, как она это делала и для других процессов. Стало ясно, что подход "одна мерка для всех" не способствовал оптимизации таких высокопроизводительных приложений, как SQL Server, и группа разработчиков SQL Server начала изучать способы оптимизации процесса планирования.

[В начало]

Цели дизайна UMS

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

  • Поддержка режима волокон, который появился в Windows NT 4.0, и обеспечить функционирование в этом режиме таким образом, чтобы движок сервера баз данных не нуждался бы в специальных фрагментах кода программ для поддержки режима потоков или волокон.

  • Сократить число переключений потока в привилегированный режим на столько, на сколько это возможно.

  • Максимально сократить число переключений контекста потока.

  • Обеспечить поддержку асинхронного I/O так, чтобы движок сервера баз данных не нуждался бы в специальных фрагментах кода программ для поддержки асинхронного ввода-вывода в тех версиях Windows, которые не поддерживают асинхронный I/O для файлов (например, Windows 9x и Windows ME).

В итоге, было решено, что SQL Server 7.0 должен иметь собственный планировщик. Вследствие этого решения, появился планировщик непривилегированного режима - UMS, который был реализован в виде программной прослойки между сервером и операционной системой. Он размёщён в файле UMS.DLL и при его разработке использовалась программная модель, которая очень похожа на модель планирования потоков в Win32, а также модель асинхронного I/O. Его основная задача планирования состоит в том, что бы как можно дольше удерживать процесс SQL Server в непривилегированном режиме. Это означает, что UMS обязательно будет стараться избегать переключений контекста, потому что они задействуют ядро. Переключения контекста могут обходиться очень дорого и ведут к ограничению масштабируемость. В патологических случаях, процесс может затратить больше времени на переключения контекста, чем на полезную работу.

[В начало]

Сравнение планирования непривилегированного и привилегированного режима

Вы можете задаться вопросом, в чём преимущество изменённого управления планированием в процессе SQL Server. SQL Server-у тогда не понадобилось бы дублировать функциональность Windows? Очень опытные специалисты в Microsoft работали над Windows и ее планировщиком, так почему же группа разработки SQL Server вдруг решила, что они смогут предложить более масштабируемое решение? Я постараюсь подробно осветить ответы на эти вопросы немного позже, но коротко ответить можно так: SQL Server лучше Windows знает в чём нуждается его собственное планирование. UMS не дублирует полностью функциональность планировщика Windows, так или иначе, он подменяет только основную функциональность, связанную с планированием задач, таймерами и асинхронным I/O. Фактически, он полагается на планирование потока и примитивы синхронизации, доступные в Windows, но некоторые понятия планирования в операционной системе (например, приоритет потока) не имеют никаких аналогий в UMS.

[В начало]

Сравнение приоритетного и кооперативного управления задачами

Самым важным отличием планировщика Windows от UMS в SQL Server является то, что планировщик Windows - приоритетный планировщик, в то время как UMS поддерживает кооперативную модель планирования. Что это означает? Это означает, что Windows не даёт одному потоку монополизировать процессор. Каждый поток в Windows получает фиксированный интервал времени использования процессора, после чего Windows отбирает у него процессор и позволяет использовать его другим, если они готовы это сделать. UMS же доверяется потокам и ожидает, что они отдадут процессор добровольно. Если рабочий поток SQL Server добровольно процессор не отдаёт, это может препятствовать исполнению других потоков. Вы можете задаться вопросом, почему в UMS реализован такой подход? Если Вы подобно мне не молоды, Вы могли бы вспомнить, что Windows 3.x работала точно тот же и использовала кооперативное планирование, и из-за этого плохого написанным приложениям не составляло труда подвесить всю систему. Фактически из-за этого была разработана Windows NT, которая использовала приоритетное планирование. Пока система могла зависнуть из-за одного приложения, невозможно было вести речь о приемлемой устойчивости операционной системы. UMS использует такое планирование для того, чтобы использование ядра Windows было не больше чем это необходимо. В системе, в которой исполнители поддаются точному расчету и могут освобождать ресурсы в случае необходимости, кооперативное планирование фактически может быть более эффективным, чем приоритетное, потому что процесс планирования можно приспособить к специфике приложения. Как я уже говорил ранее, UMS очень хорошо знает потребности планирования в SQL Server, и делает это лучше, чем операционная система.

[В начало]

Реализация планирования в UMS

Поскольку задачи планирования в SQL Server должен решать UMS, а не планировщик Windows, UMS должен каким - либо способом не позволять операционной системе переключать по своему усмотрению потоки процессоров. Для этого UMS использует довольно хитрые уловки, используя объекты Windows, называемые уведомлениями. Каждый поток у UMS имеет связанный с ним объект уведомления. Также, используется то, что планировщик Windows игнорирует те потоки, которые он не считает функционирующими, т.е. они находятся в состоянии бесконечного ожидания. Зная это, UMS устанавливает потокам состояние бездействия, что бы они не планировались системой. Для этого он использует вызовы WaitForSingleObject, что бы передать уведомление об установке бесконечной по времени блокировки, которую можно установить, используя значение INFINITE. После того, как для потока вызывается WaitForSingleObject и он "засыпает" на неопределённое время, единственным у UMS способом "разбудить" поток является создание соответствующего объекта уведомления. Получив уведомление, поток выходит из состояния ожидания, и разрешает Windows себя планировать для запуска на процессоре. Для того, чтобы Windows не могла планировать несколько потоков для одновременного исполнения на одном и том же процессоре, увеличивая этим затраты на переключения контекста, UMS пытается ограничить число активных потоков до одного на процессор. Есть исключения из этого правила, например, полнотекстовый поиск, проверка правил безопасности, вызовы xproc, запросы к прилинкованным серверам и так далее. Но в нормальном режиме работы система позволяет одновременно существовать только одному потоку на процессор.

[В начало]

Планировщик UMS

Механизм UMS управляет планированием работ для того, чтобы в любое время был активен только один поток на процессор. Когда происходит запуск SQL Server, для каждого процессора создаётся по одному планировщику UMS. По умолчанию, эти планировщики не привязаны к определенным процессорам, но алгоритм планирования Windows устроен таким образом, что через какое-то время каждый планировщик UMS, получая по одному потоку, распределяются и начинают работать каждый с одним процессором. После этого, рабочий пул сервера, независимо от режима потоков или волокон, будет равномерно распределен между планировщиками UMS. Это означает, что если максимальное число рабочих потоков установлено в значение по умолчанию - 255, и компьютер имеет четыре процессора, SQL Server создаст четыре планировщика UMS, и каждый сможет хостить до 64-х исполнителей.

[В начало]

Списки планировщика UMS

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

[В начало]

Список исполнителей

Список исполнителей содержит список доступных UMS исполнителей. В UMS, исполнитель, это абстракция понятия потока/волокна, которую можно использовать не заботясь в своём коде об установленном режиме. Как я уже говорил, одной из целей дизайна UMS являлось именно то, что нужно было обеспечить поддержку волокон таким образом, что бы не имело значение в низкоуровневом машинном коде, использует ли система волокна или потоки. Исполнитель формирует в UMS поток или волокно, которое исполняет задачи на сервере и делает это так, что бы сервер не должен был знать, выполняется ли задача в режиме потоков или волокон. Повсюду в этой статье, я буду оперировать понятием "Исполнитель UMS", а не понятиями поток или волокно. Если SQL Server находится в режиме потоков (значение по умолчанию), исполнитель UMS создаёт в Windows объект - поток. Если сервер находится в режиме волокон, исполнитель UMS создаёт в Windows волокно, обслуживание которого фактически происходит вне ядра Windows.

[В начало]

Процесс подключения

Когда клиент подключается к SQL Server, он назначается одному из планировщиков UMS. Эвристика выбора не замысловата: тот планировщик, у которого меньше всего подключений, получает новое подключение. Как только подключение связано с планировщиком, оно никогда не уходит с этого планировщика. Независимо от того, будет ли связанный с подключением планировщик занят и будут ли в системе существовать не активные планировщики, UMS никогда не будет перемещать spid между планировщиками. Это означает, что могут быть такие сценарии развития событий, в которых SQL Server, запущенный на симметричной многопроцессорной системе, не сможет работать эффективно из-за того, что приложение открывает много постоянных подключений, которые не исполняют полезную работу. Например, если на компьютере с двумя процессорами, приложение - клиент SQL Server открывает четыре постоянных подключения к серверу, и два из них выполняют 90 % всей работы, то после того, как эти два подключения попадают на одного и того же планировщика, будет наблюдаться картина, когда один процессор будет утилизироваться, в то время как другой остается относительно не загруженным. Решение подобных ситуации состоит в том, чтобы равномерно балансировать нагрузку подключений, а не сохранять постоянные подключения, когда рабочая нагрузка неравномерна. Отключение и повторное подключение, вот единственный способ перемещать spid от одного планировщика другому. Такое перемещение не гарантирует, что в результате переподключений задача попадёт на то же самый планировщик, поскольку это зависит от числа пользователей у других планировщиков.
Как только spid назначен планировщику, дальнейшее его обслуживание зависит от состояния списка исполнителей, и от того, было ли достигнуто задаваемое в конфигурации значение максимального числа рабочих потоков SQL Server (max. worker threads). Если исполнитель в этот момент числится доступным в списке исполнителей, он забирает запрос на подключение, и работает с ним. Если ни один из исполнителей не доступен, и максимальный порог потоков ещё не был достигнут, будет создан новый исполнитель, и уже он займётся обслуживанием запроса. Если нет доступных исполнителей и максимальное число потоков уже достигнуто, запрос на подключение будет помещен в список ожидающих и будет обслужен в порядке поступления, когда станет доступен исполнитель. Клиентские подключения обслуживаются в UMS как логические (а не физические) пользователи. Это нормальная практика, т.к. желательно иметь большое соотношение логических пользователей к числу исполнителей UMS. Тогда SQL Server, имея стандартное максимально число возможных потоков - 255, сможет обслуживать сотни или тысячи пользователей.

[В начало]

Запросы на исполнение

UMS работает с запросами на исполнение только атомарно. Это означает, что исполнитель делает всё работу, запрашивая исполнение пакета T-SQL, концом которого можно считать, к примеру, переход в состояние ожидания. Это также означает, что во время исполнения запроса исполнителем UMS не будет никаких переключений контекста. При исполнении имеющегося пакета T-SQL, исполнитель не будет куда то переключаться для обслуживания других пакетов. Единственное, что может заставить исполнителя начать обслуживать другой запрос, это если он завершил текущий запрос на исполнение. К примеру, это может приводить к такой ситуации, когда исполнение ранее поставленной в очередь другим исполнителем подпрограммы завершения ввода-вывода не буде считаться простоем, пока не будет полностью отработан запрос на исполнение; и обслуживание другого запроса на исполнение не начнётся, пока не будет закончено обслуживание текущего запроса. Как только это случится, исполнитель может активизировать другого исполнителя, и сделает необходимые записи об этом в списке исполнителей, или пропишет там запись о начале не активного цикла, если нет других готовых к работе исполнителей, и не осталось запросов на исполнение, о чём мы скоро поговорим. Из-за такого алгоритма атомарности запросов можно легко довести число исполняющихся потоков до предельного у SQL Server значения, достаточно только одновременно исполнять много запросов с WAITFOR. Поскольку каждый запрос с WAITFOR будет считаться исполняющимся, обслуживающий его исполнитель для SQL Server будет считаться занятым, и новые запросы подключения к серверу будут требовать создания новых исполнителей. Если будет инициализировано достаточно много таких запросов, можно быстро достигнуть максимальное число потоков сервера, а как только это случается, новые подключения приниматься не будут, пока не освободится исполнитель. Когда сервер работает в режиме потоков, и исполнитель простаивает больше 15 минут, SQL Server уничтожает этот поток, но при этом, он не будет уменьшать число исполнителей ниже установленного порога. При этом будет высвобождаться виртуальная память, которая связана со стеком потока не активного исполнителя (0,5 МБ), и она может быть использована сервером для других нужд.

[В начало]

Список исполнения

Список исполнения, это список исполнителей UMS, которые готовы исполнять имеющиеся запрос на исполнение. Каждый исполнитель в этом списке пребывает в состоянии бесконечного ожидания, пока не получит сигнальный объект уведомления. Пребывать в списке исполнения не значит для исполнителя, что он может планироваться Windows. Windows сможет его запланировать, когда объект уведомления сообщит о такой необходимости, согласно алгоритма, заложенного в UMS. Учитывая то, что UMS реализован в виде кооперативного планировщика, Вы можете задаться вопросом, что же фактически отвечает за сигнализацию (приведение) в виде уведомления исполнителю из списка исполнения, чтобы он мог приступить к работе? Ответ в том, что это может быть любой исполнитель UMS. По всему коду SQL Server, который обслуживает запросы, управление передаётся UMS, причём так, чтобы эта операция не монополизировала обслуживающего её планировщика. UMS реализует достаточно много разных типов функций приведения, которые могут вызвать исполнители. Как я уже говорил, в кооперативной среде управления задачами, потоки должны уметь добровольно уступить друг другу ресурсы системы, чтобы всё проходило гладко. SQL Server разработан так, чтобы они уступали так часто, как это необходимо и в таких местах, чтобы сохранить производительность системы. Когда у исполнителя UMS наступает необходимость приведения, или когда он закончил управление задачей (например, исполнение пакета T-SQL или запроса RPC), или потому, что он выполнял код с явным запросом к одной из функций приведения UMS, тогда только он будет ответственен за проверку списка исполнения планировщика, в поисках готового к работе исполнителя, и за выдачу сигнала, который уведомит другого исполнителя, что он может работать. Делает всё это сама подпрограмма, реализующая функцию приведения.
В процессе запроса одной из функций приведения UMS, исполнитель фактически делает работу за UMS и для этого не создаётся дополнительного потока в планировщике, которым бы нужно было управлять. Если бы такой поток создавался, тогда он должен был бы планироваться в Windows, причём в каждом случае, когда бы что-то происходило в планировщике. В таком случае, от всей затеи было бы совсем мало толку, т.к. мы пришли бы к тому, что всем планированием занималась бы реально Windows. На самом деле, было бы только хуже от такой реализации планировщика, и из-за дополнительной эксплуатации кода UMS. Предоставление любому исполнителю возможности обслуживания задач планирования позволяет уже исполняющемуся на процессоре потоку продолжить исполнение, пока у него есть работа. Такая реализация является фундаментом всего дизайна механизма планирования, позволяющая сократить число переключений контекста. Планировщик, что бы минимизировать переключения контекста потока, должен отцепить из рабочей очереди исполнителя, который эту работу делает. В идеальном случае, любой поток может обслужить любой запрос на работу. Именно это позволяет потоку, который уже запланирован операционной системой, и дальше оставаться запланированным и продолжать исполняться до завершения всех своих работ, пока всё не будет сделано. Этим устраняется расточительность в планировании других потоков, позволяя доделать работу тому потоку, который её начал или мог уже начать.

[В начало]

Список ожидания

Список ожидания содержит список исполнителей, которые ожидают ресурсы. Когда исполнитель UMS запрашивает принадлежащий другому исполнителю ресурс, он помещает себя в список ожидания для этого ресурса, и вводит себя в состояние бесконечного ожидания связанного с ним объекта уведомления. Когда исполнитель, который утилизировал ресурс, готов его отдать, только он ответственен за то, чтобы просмотреть список ожидания, найти ожидающих этот ресурс, и переместить их, соответственно, в список исполнения. В момент, когда происходит вызов приведения, этот же исполнитель отвечает за отправку уведомления первого по очереди исполнителя из списка исполнения, который ожидает освобождаемый ресурс. Потом всё может повториться, и уже следующий исполнитель, освободив ресурс, повторит всё те же действия, переместив ожидающего ресурс исполнителя из списка ожидания в список исполнения, просигнализировав в довершении всего ему о том, что он может начать работу.

[В начало]

Список ввода-вывода

Список ввода-вывода (I/O) содержит список не выполненных асинхронных запросов ввода-вывода. Эти запросы формируются у UMS в виде объектов запросов на I/O. Когда SQL Server передаёт UMS запрос на I/O, UMS использует один из двух вариантов своего кода, в зависимости от того, на какой версии Windows работает сервер. При работе на Windows 9x или Windows ME, он начинает синхронную операцию I/O (как я уже говорил выше, Windows 9x, и ME не поддерживают асинхронный файловый I/O). При работе на семействе операционных систем Windows NT, он начинает асинхронную операцию I/O. Когда поток Win32 приступает к исполнению операции асинхронного I/O, он создаёт специальную структуру OVERLAPPED, вызывая для этого функции ReadFile/ReadFileEx или WriteFile/WriteFileEx. В самом начале операции, Windows устанавливает для члена этой структуры Internal статус STATUS_PENDING, который указывать на то, что операция перешла в стадию исполнения. Пока операция выполняется, вызов через Win32 API функции HasOverlappedIoCompleted будет возвращать "false" (HasOverlappedIoCompleted фактически является макрокомандой, которая просто проверяет OVERLAPPED.Internal на предмет того, установлен ли для этого члена статус STATUS_PENDING).
Для инициализации через UMS асинхронного запроса на I/O, SQL Server предоставляет для UMS объекты запросов на I/O и передаёт их методу, который семантически схож с ReadFile/ReadFileScatter или WriteFile/WriteFileGather, в зависимости от того, осуществляется чтение или запись, и в зависимости от того, выполняется ли сбор фрагментов I/O. В UMS запрос на I/O является структурой, которая инкапсулирует запрос на асинхронный I/O и содержит в качестве своего члена, структуры OVERLAPPED. Метод UMS, реализующий асинхронный I/O, после вызова сервером передает структуру OVERLAPPED соответствующей функции асинхронного I/O Win32 (например, ReadFile) для использования с асинхронной операцией. UMS структура запроса на I/O тоже помещается в список I/O ведущего планировщика.
Как только запрос на I/O будет добавлен в список I/O, он станет доступен для обслуживания любого исполнителя, который выполняет приведение для проверки списка на предмет определения, были ли завершены операции асинхронного I/O. При этом просматривается список I/O и вызывается HasOverlappedIoCompleted для всех, кому членом OVERLAPPED в макрокоманду передавался запрос на I/O. Когда такой запрос находится, и он завершён, он удаляется из списка I/O, а затем вызывается подпрограмма завершения этого запроса на I/O (подпрограмма завершения I/O определяется в тот момент, когда в UMS создаётся запрос на I/O). Как я уже говорил ранее, одной из целей дизайна UMS было добиться, что бы планирование и асинхронные функции I/O, вызываемые из ядра OS, не требовали переключения в привилегированный режим. Поддержка в UMS подпрограмм завершения I/O, это ещё один пример избранной концепции дизайна.
Наиболее существенным различием между способами, с помощью которых Windows или UMS реализуют функциональность подпрограмм завершения I/O, является то, что в UMS подпрограмма завершения I/O работает в контексте одного из исполнителей, который занят приведением (т.е. он проверяет список I/O на предмет наличия в нём законченных операций ввода-вывода), а не в контексте потока, который изначально инициализировал асинхронную операцию. Выгода от этого в том, что отпадает необходимость переключения контекста на подпрограмму завершения I/O. Исполнитель, который уже находиться в процессе работы, и приступает к приведению, позаботится о запросе ещё до того, как он перейдёт в состояние бездействия. Из-за этого то и отпадает необходимость взаимодействия с ядром Windows. При работе на Windows 9x или ME, подпрограмма завершения I/O вызывается сразу после вызова Win32 I/O API. Так как операция обслуживается операционной системой синхронно, нет необходимость проверять список I/O и привлекать другого исполнителя для исполнения подпрограммы завершения I/O. Учитывая то, что заранее известно, что I/O будет завершён в момент возврата после вызова Win32 API, можно просто сразу перейти к вызову подпрограммы завершения I/O ещё до возврата из вызова метода I/O в UMS. Это означает, что на Windows 9x/ME подпрограмма завершения I/O всегда вызывается в контексте исполнителя, который в самом начале инициализировал асинхронную операцию I/O.

[В начало]

Список таймеров

Список таймеров содержит список запросов таймера UMS. Запрос таймера инкапсулирует хронометрируемые запросы на исполнение. Например, если исполнитель до синхронизации должен заданное время ожидать ресурс, он добавляется в список таймеров. Когда исполнитель выполняет приведение, после проверки завершения запросов на I/O, он проверяет также и список таймеров, пытаясь обнаружить просроченные таймеры. Если он находит просроченный запрос таймера, он удаляет его из списка таймеров и перемещает связанного с ним исполнителя в список исполнения. Если список исполнения в этот момент пуст, и если нет готовых к исполнению исполнителей, и если сервер работает в режиме потоков, генерируется уведомление этому исполнителю, чтобы он мог быть запланирован Windows для исполнения. Если список исполнения пуст, а сервер находится в режиме волокон, занимающийся приведением исполнитель просто переключит в непривилегированный режим этого активного исполнителя.

[В начало]

Цикл неактивного состояния

Если исполнитель, после проверки завершённых запросов на I/O и просроченных таймеров, обнаруживает, что список исполнения пуст, он инициирует цикл неактивного состояния. Для этого, он просматривает список таймеров, что бы найти ближайшее время истечения времени таймера, а затем передаёт посредством вызова объекта уведомления значение WaitForSingleObject, которое указывает планировщику время блокировки до следующего истечения времени таймера. Структура Win32 OVERLAPPED содержит специальный член уведомления, который хранит ссылку на объект уведомления Windows. Когда создается планировщик UMS, создаётся и связанный непосредственно с планировщиком объект уведомления. В момент инициализации планировщиком асинхронного запроса на I/O, это уведомление сохраняется в члене hEvent структуры OVERLAPPED объекта запроса на I/O. Такой механизм позволяет при завершении асинхронного I/O сигнализировать об этом объектом уведомления планировщика. Ожидающий это уведомление и блокированный до истечения таймера исполнитель будет ждать пока не завершиться I/O или пока не истечёт таймер, и не важно, что случиться первым. Поскольку он это делает через запрос WaitForSingleObject, не будет пулинга и утилизации процессорных ресурсов, пока не случиться одно из этих событий.

[В начало]

Приоритетное исполнение

Некоторые операции в SQL Server требуют, чтобы исполнитель подвергался приоритетному планированию, то есть что бы он не управлялся планировщиком UMS. Например, если обслуживается запрос к расширенной хранимой процедуре. Как я уже говорил ранее, т.к. UMS поддерживает кооперативную многозадачность, он во всём полагается на исполнителей, которые должны выполнять приведение в нужные моменты, и это позволяет серверу использовать выгоды такого планирования. Очевидно, что планировщик UMS не имеет понятия, умеет ли xproc выполнять приведение любого типа и с правильными интервалами, и, фактически, не существует функции ODS API, которые xproc могла бы вызывать, чтобы это делать. Поэтому, планировщик предполагает, что для xproc требуется собственный поток исполнения. До того как исполнитель приступит к исполнению расширенной хранимой процедуры, он удаляется из списка исполнения, и его оповещение устанавливается так, чтобы планировщик потом мог использовать его в качестве исполнителя для обслуживания запросов на исполнение. Тем временем, этот исполнитель выполняет xproc и практически игнорируется планировщиком до момента, когда он освободиться. Как только xproc возвратит управление, исполнитель продолжит обработку её запроса на исполнение (например, оставшуюся часть блока операторов T-SQL, в котором был вызов xproc), а уже затем возвратит себя в список исполнителей, что происходит, когда он попадает в состояние простоя. Важным моментом здесь является то, что некоторые операции сервера требуют наличия собственных исполнителей, и это может быть нужным для того, чтобы в какой то момент существовало несколько потоков, активных для данного процессора. Это означает, что Windows будет планировать их в основном по своим правилам, и это может порождать переключения контекста между ними. Также это означает, что с момента запуска, xproc фактически присваивает исполнителя UMS, и одновременный запуск большого числа расширенных хранимых процедур может дать отрицательный эффект для масштабируемости и параллелизма. Каждая исполняемая xpro c уменьшает возможности UMS обслужить большое число логических пользователей, используя для этого относительно маленькое число исполнителей. Кроме xprocs, есть и другие случаи, когда исполнитель вынужден будет работать в приоритетном планировании. Примером могут быть запросы с sp_OA, запросы к прилинкованным серверам, распределённые запросы, server-to-server удалённые вызовы процедур (RPC), отладка T-SQL, и ещё несколько других случаев. Очевидно, что стоит по возможности их избегать, если масштабируемость и эффективное использование ресурсов является первостепенной задачей.

[В начало]

Режим волокон

Когда сервер работает в режиме волокон, всё работает немного по-другому. Волокна Win32 - это концепция непривилегированного режима, и ядро ничего о них не знает. Поскольку потоки в реальности реализуются только механизмами Windows, тот код, который исполняется посредством волокна, тоже должен быть в некоторый момент времени исполнен потоком. Управление волокнами реализуется таким образом, что API Windows связывает группу волокон с одним объектом потока. Когда одно из волокон исполняет часть кода, этот код фактически будет выполнен в его ведущем потоке. На каком то этапе этот пользовательский код должен выполнить переключение на другое волокно, причём так, чтобы это концептуально было очень похоже на кооперативное управление задачами в UMS.
Учитывая то, что когда SQL Server находится в режиме волокон, множество исполнителей может совместно использовать один поток Windows, не сможет работать тот процесс, которым сопровождается переключение исполняемого потока в приоритетный режим, т.е. когда нужно переключить в приоритетный режим волокно исполнителя. Поскольку механизм исполнения в Windows основан на потоках, хостящий волокна поток должен быть изъят у планировщика, а это, в свою очередь, понудит и всех других, хостящих там волокна исполнителей, уйти от планировщика, что является не желательным. Поэтому для исключения описанной ситуации, на основе потока создаётся скрытый планировщик, который будет обслуживать внешние расширенные хранимые процедуры и другие внешние запросы, которые заставляют исполнителя переключаться в приоритетный режим (планировщик является скрытым в том смысле, что он не отображается в результатах исполнения в DBCC SQLPERF(umsstats)). И когда волокно исполнителя должно быть переключено в приоритетный режим, чтобы исполнить один из его компонентов, запрос на исполнение передаётся для обслуживания этому скрытому планировщику. Когда он закончит необходимую работу, волокно возвращается первоначальному планировщику, и работа продолжается в нормальном режиме. В результате такого положения вещей, исполнение xproc или запросов к прилинкованным серверам может в режиме волокон оказаться чрезвычайно неэффективным. Фактически, у сервера существует множество компонент, которые даже не поддерживаются в режиме волокон (например, sp_xml_preparedocument и ODSOLE). Если Вам необходимо исполнять множество внешних расширенных хранимых процедур, запросов к прилинкованным серверам, распределённых транзакций и т.п., режим волокон может оказаться для Вас плохим выбором.

[В начало]

Скрытые планировщики

Сервер создает скрытых планировщиков и для других целей. Другие процессы сервера требуют точно таких же типов кратких блокировок, подобного управления ресурсами и планирования, которые предоставляет планирование запросов на исполнение в UMS, так что сервер создает скрытых планировщиков для того, что бы и другие процессы могли использовать подобную функциональность, без необходимости реализовать её самостоятельно. В качестве примера такого процесса можно привести средства копирования-восстановления в SQL Server. Учитывая, что многие из устройств резервирования не поддерживают асинхронный I/O, и тот факт, для них характерен продолжительный синхронный I/O через один и тот же планировщик UMS, эти процессы могут негативно воздействовать на параллелизм всего планирования, потому что они допускают, что бы один блокирующий синхронный запрос на I/O монополизировал работу SQL Server (что мало чем отличающийся от того, что делает запрос внешнего кода), который для предотвращения такого поворота событий помещает операции копирования-восстановления в отдельный, собственный для них планировщик. В таком случае эти процессы будут бороться за процессорное время только друг с другом, и Windows сможет осуществлять над ними приоритетное планирование вместе с другими планировщиками.

[В начало]

DBCC SQLPERF(umsstats)

Ранее я уже упоминал DBCC SQLPERF(umsstats), и Вы уже могли читать об этой команде, хотя она и не документирована, но упоминалась в публичной библиотеке Microsoft Knowledge Base. DBCC SQLPERF(umsstats) возвращает в результирующем наборе список статистики по видимых планировщикам UMS в системе. В этом списке перечисляется общее число пользователей и исполнителей для планировщика, число исполнителей в списке исполнения, число неактивных исполнителей, число не выполненных запросов на исполнение и так далее. Команда очень удобна, когда Вы подозреваете наличие каких то проблем с планировщиком и хотите узнать, что продолжает незаметно исполняться. Например, Вы сможете быстро узнать, достиг ли планировщик предельного числа исполнителей и занят ли они чем - нибудь в настоящее время.

[В начало]

Перевод: Александра Гладченко  2005г.

Rambler's Top100 Рейтинг@Mail.ru  Administrator: Обратная связь 
Copyright: SQL.Ru 2000-2013