Продуктивність пов'язаного сервера SQL Server: Чому віддалені запити такі дорогі?


14

У мене є два сервери баз даних, підключені через пов'язані сервери. Обидва є базами даних SQL Server 2008R2, і з'єднання зв'язаним сервером здійснюється за допомогою звичайного посилання "SQL Server", використовуючи поточний контекст безпеки входу. З’єднані сервери обидва в одному центрі обробки даних, тому з'єднання не повинно бути проблемою.

Я використовую наступний запит, щоб перевірити, які значення стовпця identifierдоступні віддалено, але не локально.

SELECT 
    identifier 
FROM LinkedServer.RemoteDb.schema.[TableName]

EXCEPT

SELECT DISTINCT
    identifier 
FROM LocalDb.schema.[TableName] 

В обох таблицях є некластеризовані індекси стовпця identifier. Місцеве значення складає близько 2,6М рядків, віддалено лише 54. Проте, дивлячись на план запитів, 70% часу на виконання приділяється "виконанню віддаленого запиту". Також при вивченні повного плану запитів 1замість 2695380(приблизно, кількість передбачуваних рядків при виборі лише запиту, що надходить після EXCEPT), замість ( приблизно, кількість оцінюваних місцевих рядків ). План виконання Виконання цього запиту дійсно займає багато часу.

Мене змушує замислитися: чому це? Невже оцінка "просто" відсторонена, чи віддалені запити на пов'язаних серверах насправді такі дорогі?


2
BTW: "Орієнтовна кількість страт" ви повинні шукати для пошуку індексу. Орієнтовна кількість рядків - це вихід рядків за виконання, який не буде пов'язаний з кількістю рядків у самій таблиці, якщо план не має повного сканування.
Мартін Сміт

Відповіді:


9

План, який ви маєте на даний момент, виглядає як найоптимальніший для мене план.

Я не згоден з твердженням в інших відповідях про те, що він надсилає рядки 2.6M на віддалений сервер.

План виглядає так, ніби для кожного з 54 рядків, повернених із віддаленого запиту, він виконує індекс, пошукайте у вашу локальну таблицю, щоб визначити, чи відповідає він чи ні. Це майже оптимальний план.

Заміна хеш-об'єднання або об'єднання об'єднань буде контрпродуктивною, враховуючи розмір таблиці, а додавання проміжної #tempтаблиці просто додає додатковий крок, який, здається, не дає вам жодної переваги.


6

Підключення до віддаленого ресурсу дорого. Період.

Однією з найдорожчих операцій у будь-якому середовищі програмування є мережевий IO (хоча дисковий IO, як правило, карликовим).

Це поширюється на віддалені пов'язані сервери. Сервер, що викликає віддалений зв'язаний сервер, спочатку повинен встановити з'єднання, після чого потрібно виконати запит на віддаленому сервері, повернути результати та закрити з'єднання. Це все вимагає часу через мережу.


Ви також повинні структурувати свій запит таким чином, щоб ви переносили мінімальну кількість даних по лінії. Не чекайте, що БД оптимізується для вас.

Якби я писав цей запит, я вибрав би віддалені дані в змінну таблиці (або в таблицю темпів), а потім використав би це разом із локальною таблицею. Це гарантує, що будуть передані лише ті дані, які потрібно передавати.

Запит, який ви виконуєте, може легко надсилати 2,6М рядків на віддалений сервер, щоб обробити EXCEPTпункт.


Гаразд, тому для встановлення з'єднання це великі витрати на запуск. Запит потрібно надсилати, обробляти віддалено (для цього не потрібна мережа), і нарешті результати надсилаються та обробляються. Але для передачі даних через мережеве з'єднання не піде хвилин, чи не так?
vstrien

@vstrien - Це може. Залежить від підключення до мережі, затримки, насиченості та інших факторів. Точка буття - це не детерміновано.

@vstrien - Додано більше інформації у мою відповідь. Я вірю, що запит, як написано, надішле локальні рядки на віддалений сервер для обробки.

2
Звідки випливає той факт, що він надсилає рядки 2.6M на віддалений сервер? Я не маю великого досвіду роботи з планами з віддаленими операторами запитів, але схоже, що 54 рядки виходять з оператора віддаленого запиту, тоді він робить анти-напівприєднання проти локальної таблиці.
Мартін Сміт

2
@Lieven - Можливо, це буде логічно, але не думаю, що це правильно з наведеного плану.
Мартін Сміт

1

Я не є експертом, але якщо ви використовуєте Union, крім або Intersect, вам не доведеться використовувати "Distinct". Залежно від значень LocalDb.schema. [TableName], ефективність запиту може бути покращена.

SELECT 
    identifier 
FROM LinkedServer.RemoteDb.schema.[TableName]

EXCEPT

SELECT 
    identifier 
FROM LocalDb.schema.[TableName]

0

Один правильний, проблема продуктивності викликається надсиланням рядків 2,6М на віддалений сервер.

Щоб вирішити цю проблему, ви можете змусити віддалені дані (54 рядки), які надсилаються вам, використовуючи темп або таблицю пам'яті.

Використання тимчасової таблиці

SELECT  identifier 
INTO    #TableName
FROM    LinkedServer.RemoteDb.schema.[TableName]

SELECT  identifier
FROM    #TableName
EXCEPT
SELECT  DISTINCT identifier 
FROM    LocalDb.schema.[TableName] 

DROP    #TableName

Використання тимчасової таблиці може допомогти в оцінці кардинальності в будь-якому випадку, хоча вкладені петлі здаються розумними лише для 54 рядків.
Мартін Сміт

Використання тимчасової таблиці працює правильно з 54 рядками; але у випадках з великими столами з обох сторін це вже неможливо. Яким було б ваше рішення для двох «величезних» столів однакового розміру? Створення UserTable в іншій базі даних?
vstrien

1
@vstrien - насправді немає хорошого рішення для двох величезних таблиць однакового розміру. Можливо, створення розподіленого розподіленого перегляду цікавить вас, але я не маю жодного досвіду з цим.
Lieven Keersmaekers

0

Думаю, вам краще реплікувати віддалену таблицю на сервер, від якого ви запитуєте, а потім запустити всі свої SQL локально.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.