Технологии Push и Pull при работе с linked servers в Microsoft SQL Server
Для обеспечения взаимодействия нескольких серверов Microsoft SQL Server наиболее часто используется технология linked-серверов. При этом, типичной является задача обмена данными между linked-серверами. В данной статье я проведу краткий сравнительный анализ технологий push и pull для решения задачи передачи новых данных.
Провести такой анализ проще всего на примере. Пусть у нас есть два сервера – сервер-источник (SourceServer) и целевой сервер (DestServer). В качестве примера рассмотрим задачу, когда нам необходимо передать таблицу Customers c сервера-источника на целевой сервер.

Технология Push характеризуется “заталкиванием” данных с исходного сервера на целевой сервер. Т.е. SQL-запрос для передачи таблицы Customers будет выполняться на исходном сервере и выглядеть так:
insert into DestServer.Northwind.dbo.Customers select * from Customers |
На исходном сервере запрос выполняется в неизменном виде –
insert into DestServer.Northwind.dbo.Customers select top * from Customers go |
set implicit_transactions on go declare @P1 int set @P1=1 declare @P2 bigint set @P2=8400823175122854 exec sp_getschemalock @P1 output, @P2 output, N'"Northwind"."dbo"."Customers"' select @P1, @P2 go declare @P1 int set @P1=180150000 declare @P2 int set @P2=2 declare @P3 int set @P3=4 declare @P4 int set @P4=-1 exec sp_cursoropen @P1 output, N'select * from "Northwind"."dbo"."Customers"', @P2 output, @P3 output, @P4 output select @P1, @P2, @P3, @P4 go exec sp_cursor 180150000, 4, 0, N'Northwind.dbo.Customers', @CustomerID = N'ALFKI', @CompanyName = N'Alfreds Futterkiste', @ContactName = N'Maria Anders', @ContactTitle = N'Sales Representative', @Address = N'Obere Str. 57', @City = N'Berlin', @Region = NULL, @PostalCode = N'12209', @Country = N'Germany', @Phone = N'030-0074321', @Fax = N'030-0076545' go exec sp_cursor 180150000, 4, 0, N'Northwind.dbo.Customers', @CustomerID = N'ANATR', @CompanyName = N'Ana Trujillo Emparedados y helados', @ContactName = N'Ana Trujillo', @ContactTitle = N'Owner', @Address = N'Avda. de la Constitucion 2222', @City = N'Mexico D.F.', @Region = NULL, @PostalCode = N'05021', @Country = N'Mexico', @Phone = N'(5) 555-4729', @Fax = N'(5) 555-3745' go exec sp_cursor 180150000, 4, 0, N'Northwind.dbo.Customers', @CustomerID = N'ANTON', @CompanyName = N'Antonio Moreno Taqueria', @ContactName = N'Antonio Moreno', @ContactTitle = N'Owner', @Address = N'Mataderos 2312', @City = N'Mexico D.F.', @Region = NULL, @PostalCode = N'05023', @Country = N'Mexico', @Phone = N'(5) 555-3932', @Fax = NULL go exec sp_cursorclose 180150000 go IF @@TRANCOUNT > 0 COMMIT TRAN Go set implicit_transactions off go |
- Открывает распределенную транзакцию, включая режим неявных транзакций.
- Открывает курсор и вставляет записи последовательно одну за другой, а не все сразу, как в случае обычной вставки, без использования linked-серверов.
Для примера – следующий триггер на таблице Customers:
CREATE TRIGGER dbo.I_Customers ON dbo.Customers AFTER INSERT AS BEGIN declare @InsertedCount int, @TotalRows int select @InsertedCount = count (*) from inserted select @TotalRows = count (*) from Customers insert into CallsCount (InsertedCount, TotalRows) values (@InsertedCount, @TotalRows) END |
После выполнения предыдущего запроса на вставку выборка из таблицы CallsCount дает следующий результат:
InstertedCount | TotalRows |
1 | 1 |
1 | 2 |
1 | 3 |
Можно провести интересный эксперимент, изменив триггер следующим образом:
ALTER TRIGGER dbo.I_Customers ON dbo.Customers AFTER INSERT AS BEGIN declare @InsertedCount int, @TotalRows int select @InsertedCount = count (*) from inserted select @TotalRows = count (*) from Customers insert into CallsCount (InsertedCount, TotalRows) values (@InsertedCount, @TotalRows) if @TotalRows = 3 rollback tran END |
Причина этого видна в перехваченной последовательности запросов - SQL Server объявляет распределенную транзакцию. Вызывая rollback tran в триггере мы откатываем не только данный триггер, но и всю транзакцию вставки. Это, вообще говоря, логично, так как при этом работа триггера выглядит так же как и при вставке не из удаленного сервера, с той лишь разницей, что вызывается он несколько раз. Именно для обработки таких ситуаций SQL Server и объявляет распределенную транзакцию. Побочным эффектом такой транзакции является длительная блокировка таблицы Customers на целевом сервере на все время вставки.
Все эти эффекты не имеют важного значения, если между серверами передаются небольшие объемы данных. Однако, последовательная вставка курсором большого кол-ва записей может занимать довольно длительное время, добавляя ко всему прочему блокировку таблиц на все это время. В качестве примера можно попробовать передать большой справочник, состоящий примерно из 130 000 строк.
insert into DestServer.Northwind.dbo.Dictionary select * from Dictionary |
Технология Pull
При использовании технологии pull запрос на вставку данных выполняется на целевом сервере. При этом происходит «втягивание» данных от сервера-источника.
Такой запрос, выполняющийся на целевом сервере, выглядит так:
insert into Customers select * from SourceServer.Northwind.dbo.Customers |
insert into Customers select * from SourceServer.Northwind.dbo.Customers go |
set implicit_transactions on go declare @P1 int set @P1=1 declare @P2 bigint set @P2=8381016049028902 exec sp_getschemalock @P1 output, @P2 output, N'"Northwind"."dbo"."Customers"' select @P1, @P2 go declare @P1 int set @P1=2 exec sp_prepexec @P1 output, NULL, N'SELECT Col1028,Col1027,Col1026,Col1025,Col1024, Col1023,Col1022,Col1021,Col1020,Col1019,Col1018 FROM (SELECT Tbl1001."CustomerID" Col1018, Tbl1001."CompanyName" Col1019, Tbl1001."ContactName" Col1020, Tbl1001."ContactTitle" Col1021, Tbl1001."Address" Col1022, Tbl1001."City" Col1023, Tbl1001."Region" Col1024, Tbl1001."PostalCode" Col1025, Tbl1001."Country" Col1026, Tbl1001."Phone" Col1027, Tbl1001."Fax" Col1028 FROM "Northwind"."dbo"."Customers" Tbl1001) Qry1029' select @P1 go exec sp_unprepare 2 go exec sp_releaseschemalock 1 go IF @@TRANCOUNT > 0 ROLLBACK TRAN Go set implicit_transactions off go |
InstertedCount | TotalRows |
3 | 3 |
insert into Dictionary select * from SourceServer.Northwind.dbo.Dictionary |
Сравнительные данные
В следующей таблице приведены сравнительные данные этих двух технологий
Push | Pull | |
Запрос | Выполняется на сервере-источнике. Пример: insert into DestServer.Northwind.dbo.Customers select * from Customers | Выполняется на сервере-приемнике. Пример: insert into Customers select * from SourceServer.Northwind.dbo.Customers |
Вставка | Курсором, по одной записи. | Пакетно. Все записи за один запрос. |
Триггер | Срабатывает для каждой вставляемой записи. | Срабатывает один раз для всех вставляемых записей. |
Скорость | Низкая, так как записи вставляются одна за одной. | Высокая (в десятки раз быстрее), так как все записи вставляются за один запрос. |
Заключение
Из всего вышесказанного следует, что технология pull обеспечивает более высокую производительность, чем технология push и является более предпочтительной.
Я с радостью рассмотрю любые комментарии и вопросы по данной тематике.
Мои координаты доступны на сайте www.msmirnov.ru
Михаил Смирнов
Руководитель проектов.