Запит працює по-різному в SQL 2005 проти SQL 2008R2


9

У моєму офісі у нас є запит, який досить некрасивий, але працює досить добре у виробництві та в середовищі розробки (20 сек. Та 4с. Відповідно). Однак у нашому тестовому середовищі це займає понад 4 години. SQL2005 (+ останні патчі) працює у виробництві та розробці. SQL2008R2 працює в тестуванні.

Я переглянув План запитів, і він показує, що SQL2008R2 використовує TempDB за допомогою табличної котушки (ледачої котушки) для зберігання повернених рядків із пов'язаного сервера. Наступним кроком буде показ вкладених циклів (лівий анти-напівз'єднання), що з'їдає 96,3% запиту. Лінія між двома операторами становить 5 398 МБ!

План запитів для SQL 2005 показує відсутність використання tempdb та використання лівого антиполового приєднання.

Нижче наведено дезінфікований код та плани виконання 2005 року на вершині, 2008R2 знизу.

Що викликає різке сповільнення та зміни? Я очікував побачити інший план виконання, так що це мене не турбує. Драматичне уповільнення часу запитів - це те, що мене турбує.

Чи потрібно дивитись на базове обладнання, оскільки версія 2008R2 використовує tempdb, я повинен розглянути, як оптимізувати використання цього?

Чи є кращий спосіб написати запит?

Дякую за допомогу.

    INSERT INTO Table1_GroupLock (iGroupID, dLockedDate)
SELECT 
 Table1.iGroupID,
 GETDATE()
FROM Table1
WHERE 
 NOT EXISTS (
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE 
   (
    Alias2.FirstName + Alias2.LastName = dbo.fnRemoveNonLetter(Table1.FullName)
    AND NOT dbo.fnRemoveNonLetter(Table1.FullName) IS NULL
    AND NOT Alias2.FirstName IS NULL 
    AND NOT Alias2.LastName  IS NULL
   ) OR (
    Alias2.FamilyName = dbo.fnRemoveNonLetter(Table1.FamilyName)
    AND Alias2.Child1Name = dbo.fnRemoveNonLetter(Table1.Child1Name)
    AND NOT dbo.fnRemoveNonLetter(Table1.FamilyName) IS NULL
    AND NOT dbo.fnRemoveNonLetter(Table1.Child1Name) IS NULL
    AND NOT Alias2.Familyname IS NULL
    AND NOT Alias2.Child1Name IS NULL
   ) OR (
    Alias2.StepFamilyName = dbo.fnRemoveNonLetter(Table1.StepFamilyName)
    AND Alias2.StepFamilyNameChild1 = dbo.fnRemoveNonLetter(Table1.StepFamilyNameChild2)
    AND NOT Alias2.StepFamilyName IS NULL
    AND NOT Alias2.StepFamilyNameChild1 IS NULL
    AND NOT dbo.fnRemoveNonLetter(Table1.StepFamilyName) IS NULL
    AND NOT dbo.fnRemoveNonLetter(Table1.StepFamilyNameChild2) IS NULL
   )  
 ) AND NOT EXISTS (
  SELECT 1
  FROM Table3
  INNER JOIN Table4
   ON Table4.FirstNameType = Table3.FirstNameType 
  INNER JOIN table5
   ON table5.LastNameType = Table3.LastNameType 
  WHERE 
   Table3.iGroupID = Table1.iGroupID
   AND Table3.bIsClosed = 0
   AND Table4.sNameTypeConstant = 'new_lastname'
   AND table5.sFirstNameConstant = 'new_firstname'
 )

SQL-2005


SQL2008R2

:: EDIT :: Виконано запит з іншого екземпляра SQL2005, майже такий же план виконання, як і "хороший". Досі не впевнений, як дві версії 2005 року працюють краще на пов'язаному сервері 2008R2, ніж екземпляри 2008R2 для екземплярів 2008R2.

Хоча я не заперечую, що код міг би використовувати якусь роботу, якщо б це був проблема, чи не бачив би я те ж саме плани exec у всіх своїх випробуваннях? Незалежно від версії SQL?

:: EDIT :: Я застосував SP1 та CU3 до обох екземплярів 2008R2, досі немає кісток. Я спеціально встановив колокацію на пов'язаному сервері, без кісток. Я спеціально встановив дозволи мого користувача діяти як sysadmin в обох випадках, без кісток. Я також запам'ятав внутрішні внутрішні файли свого сервера sql 2008 та усунення несправностей, ми побачимо, чи можу я відстежити це як-небудь.

Дякуємо всім за допомогу та поради.

:: EDIT :: Я вносив різні зміни дозволу на пов'язаний сервер. Я використовував входи в SQL, реєстрацію в домені, вводив себе в одиницю користувачів, використовував параметр "бути зробленим за допомогою цього контексту безпеки". Я створив користувачів з обох сторін пов'язаного сервера, які мають права sysadmin на сервері. Я поза ідеями.

Я все одно хотів би дізнатися, чому SQL2005 виконує запит, який настільки різко відрізняється від SQL2008R2. Якби запит був поганим, я б бачив час роботи +4 години на SQL2005 та SQL2008R2.

Відповіді:


5

Я хотів би, щоб ви переробили запит.

У вас проблеми з саргабельністю, і ви навіть використовуєте скалярні дзвінки функції, які також зашкодять запиту. Ви можете створити обчислений стовпчик FullName на Table2 та поставити індекс на цьому, переконайтесь, що ваш індекс Включає Ім'я та прізвище. Також слід додати індекси, які допомагають іншому

Крім того, зробіть функцію вбудованої таблиці, щоб виконати свою функцію "RemoveNonLetter", і переробіть свій запит, щоб використовувати це, можливо, використовуючи APPLY, як я це зробив тут.

І обов'язково перевірте ту помилку, на яку йдеться у відповіді Павла .

INSERT INTO Table1_GroupLock (iGroupID, dLockedDate)
SELECT 
 Table1.iGroupID,
 GETDATE()
FROM Table1
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.FullName)) AS fn (FullName)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.FamilyName)) AS famn (FamilyName)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.Child1Name)) AS c1n (Child1Name)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.StepFamilyName)) AS sfn (StepFamilyName)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.StepFamilyNameChild2)) AS sfnc2 (StepFamilyNameChild2)
WHERE 
 NOT EXISTS (
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE Alias2.FullName = fn.FullName
  UNION ALL
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE Alias2.FamilyName = famn.FamilyName AND Alias2.Child1Name = c1n.Child1Name
  UNION ALL
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE Alias2.StepFamilyName = sfn.StepFamilyName AND Alias2.StepFamilyNameChild1 = sfnc2.StepFamilyNameChild2
 ) 
 AND NOT EXISTS (
  SELECT 1
  FROM Table3
  INNER JOIN Table4
   ON Table4.FirstNameType = Table3.FirstNameType 
  INNER JOIN table5
   ON table5.LastNameType = Table3.LastNameType 
  WHERE 
   Table3.iGroupID = Table1.iGroupID
   AND Table3.bIsClosed = 0
   AND Table4.sNameTypeConstant = 'new_lastname'
   AND table5.sFirstNameConstant = 'new_firstname'
 )
;

6

Додаючи до попередніх відповідей, причина регресу плану може бути пов’язана з відомою помилкою оцінки кардинальності, коли план включає в себе Anti Semi Join. Див. KB 2222998

Якщо припустити, що план 2005 року спричинив прийнятну ефективність, ви можете виявити, що приведення сервера до версії, яка включає виправлення (і дозволяє TF4199 активувати його) поверне вас до «хорошого» плану.

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


5

Я б припустив, що віддалені дані сформовані локально, оскільки один із них

  1. Налаштування пов'язаного сервера (наприклад, зіставлення) неоднакові
  2. Місцеве зіставлення не те саме, що віддалене, незважаючи на параметри пов'язаного сервера
  3. Запит не може запускатися коректно віддалено через дозволи

Для пункту 1 див. Sp_serveroption
А для пункту 2, але також перевірте порівняння сервера / db.

Точку 3 див. У Linchi Shea:

Ви просите SQL Server обробити всі дані локально, відповідно до моєї відповіді тут: Наслідки для продуктивності використання OPENQUERY у поданні

Редагувати

По-друге, я бачу 2 віддалені дзвінки на "хорошому" плані, а не один. Це підтверджує те, що я тут говорю


Вибачте за велику затримку. 1: Я перевірив налаштування пов'язаного сервера, вони однакові 2: Я також перевірив і неявно встановив порівняння властивостей пов'язаного сервера на віддалений сервер. 3: Контекст безпеки Я все ще працюю над тестуванням A / B. Я все ще спотикаюся за логікою, що стоїть за різними планами виконання. Від сервера R2 до сервера R2 він виконує лише вибір (підтягуючи понад 150 К рядків), а потім виконує з'єднання. У 2005 році до R2 він робить вибір і приєднується до віддаленого сервера. Контекст безпеки для обох сценаріїв однаковий.
RateControl

3

+1 на спробуйте переписати коментар із запитом із datagod.

Мені також цікаво, чи не стикаєтесь ви з проблемою дозволів на пов'язаній стороні сервера, що призводить до цього уповільнення. Я блогував про це пов'язане з уповільненням роботи сервера деякий час тому. Можливо, варто перевірити хімічні речовини (це сервер, пов’язаний з SQL? Або це інша СУБД? Якщо остання, то все одно ви не отримаєте чудової статистики)

Чи є у вас ще SQL Server 2005 у тестовому середовищі, щоб спробувати цей запит і виключити середовище?

Чи відновили ви статистику після оновлення?


3

З цим порівнянням існує стільки проблем ... Я просто не знаю, з чого почати.

  1. Отримайте точні характеристики для ваших виробничих та випробувальних машин.

  2. Визначте мережеві зв’язки між різними пов'язаними серверами в обох середовищах. Вони однакові швидкості? Чи розташовані сервери поруч один з одним в обох середовищах?

  3. Чи є взагалі якийсь спосіб, щоб ви могли переписати запит, щоб НЕ використовувати пов'язані сервери? Приєднання таблиць на серверах залишає вас вразливими до змін топології, і його жахливо повільно в більшості випадків.

  4. Використання NOT та OR зазвичай призводить до повного сканування таблиці. Спробуйте переписати запит.

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