Нечетная проблема производительности запросов SQL 2000 - PullRequest
1 голос
/ 03 марта 2009

В некоторых настройках производительности SQL-запросов я заметил, что следующий запрос выполнялся медленно, но он не перегружал процессор и, похоже, не было других узких мест в системе, из-за которых он работал медленно. На самом деле среднее значение ЦП составляло 15%, в то время как оно работало:

UPDATE: Данный запрос выполняется в цикле курсора, который содержит 800 записей:

cursor = SELCT DISTINCT param1,param2, param3, param4 
    FROM t_Table 
    GROUP BY param1,param2, param3, param4 
    ORDER BY param1,param2, param3, param4 DESC 

cursor loop:
  SELECT @maxval1 = max(iField1), 
    @maxval2 = max(iField2), 
    @dtDateMin = Min(dtDate),
    @dtDateMax = Max(dtDate) 
  FROM t_Table 
  WHERE iSomeField1 = @param1 
    AND iSomeField2 = @param2 
    AND iSomeField3 = @param3 
    AND iSomeField4 = @param4
next

(Примечание: для iSomeField1-4 настроены индексы)

Затем я разбил отдельные минимальные / максимальные части на четыре запроса, чтобы увидеть, как сервер ответил и получил полную пропускную способность с пиковым значением ЦП на 100% и блоком выполнялся в течение 2 секунд, в отличие от> 5 минут для вышеописанного. Хотя я действительно доволен повышением производительности, я хотел бы, чтобы какой-нибудь умный администратор БД объяснил, почему это так, и какие еще советы они могли бы дать по этим типам запросов?

SELECT TOP 1 @maxval1 = iField1 
FROM t_Table 
WHERE iSomeField1 = @param1 
  AND iSomeField2 = @param2 
  AND iSomeField3 = @param3 
  AND iSomeField4 = @param4 
ORDER BY field1 DESC

SELECT TOP 1 @maxval2 = iField2 
FROM t_Table 
WHERE iSomeField1 = @param1 
  AND iSomeField2 = @param2 
  AND iSomeField3 = @param3 
  AND iSomeField4 = @param4 
ORDER BY field2 DESC

SELECT TOP 1 @dtDateMin = dtDate 
FROM t_Table 
WHERE iSomeField1 = @param1 
  AND iSomeField2 = @param2 
  AND iSomeField3 = @param3 
  AND iSomeField4 = @param4 
ORDER BY dtDate ASC

SELECT TOP 1 @dtDateMax = dtDate 
FROM t_Table 
WHERE iSomeField1 = @param1 
  AND iSomeField2 = @param2 
  AND iSomeField3 = @param3 
  AND iSomeField4 = @param4 
ORDER BY dtDate DESC

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

ОБНОВЛЕНИЕ: скриншоты использования ЦП запроса 1 для запроса 2+, выполняемые в цикле курсора:

загрузка ЦП для запроса 1 и запроса 2 http://img22.imageshack.us/img22/3262/sqlperfodd.png Запрос 1 EP http://img513.imageshack.us/img513/5681/query1.png Запрос 2 EP http://img365.imageshack.us/img365/9715/query2.png

Схема показана ниже (это урезанная версия нашей реальной таблицы, но мы используем структуру и индексы).

CREATE TABLE [dbo].[t_Table] (
[ID] [int] IDENTITY (1, 1) NOT NULL ,
[dtDate] [datetime] NULL ,
[iField1] [int] NULL ,[iField2] [int] NULL ,
[iSomeField1] [int] NULL ,[iSomeField2] [int] NULL ,
[iSomeField3] [int] NULL ,[iSomeField4] [int] NULL) ON [PRIMARY]

CREATE  CLUSTERED  INDEX [IX_dtDate] ON [dbo].[t_Table]([dtDate], [iField1],
[iSomeField1]) WITH  FILLFACTOR = 90 ON [PRIMARY]

ALTER TABLE [dbo].[t_Table] ADD 
CONSTRAINT [PK_t_Table] PRIMARY KEY  NONCLUSTERED 
([ID]) ON [PRIMARY] 

CREATE  INDEX [idx_field1234] ON [dbo].[t_Table]([iSomeField1], [iSomeField2],
[iSomeField3], [iSomeField4]) ON [PRIMARY]

CREATE  INDEX [idx_field1] ON [dbo].[t_Table]([iSomeField1]) [PRIMARY]

Ответы [ 4 ]

1 голос
/ 03 марта 2009

Я посмотрел на ваш запрос и провел несколько быстрых тестов. Как отметил Диего, план выполнения в Query Analyzer является отличным инструментом для устранения неполадок такого рода.

Прежде всего, обратите внимание на индексы - когда SQL получает запрос, он будет проходить через ряд шагов - чтобы упростить этот процесс, одна из основных задач здесь состоит в том, чтобы решить, какой способ получения данных наиболее эффективен. SQL будет смотреть на структуру таблицы, статистику и индексы, чтобы определить, какой путь он считает оптимальным. В конечном итоге он выберет один индекс, который будет использоваться для извлечения данных (но не несколько индексов).

Индекс - это в основном поиск того, где данные хранятся в таблице (или в случае кластерного индекса он фактически определяет, КАК данные хранятся на диске). Если в вашем запросе используется несколько столбцов (либо в запросе, где или в предложениях заказа), то запрос должен будет извлечь их, чтобы он мог вернуть ваш запрос. SQL будет смотреть на индекс, а затем смотреть на порядок, в котором ему нужно получить индексы. В идеале, SQL сможет «искать» непосредственно запрашиваемые вами данные (наиболее распространенная альтернатива - «сканирование», что в основном означает, что весь индекс / структура была отсканирована для данных). Эти термины используются в приведенном выше плане выполнения. Выше есть много дополнительных сложностей (например, в некоторых запросах вы можете увидеть поиск по закладкам, где SQL использует индекс для поиска строки, а затем выполняет поиск для получения любых связанных данных), но это должно быть хорошим начальным шагом .

Помимо этого, я упомяну сайт, который, я думаю, отлично подходит для информации о производительности SQL - www.sql-server-performance.com

Принимая вышеизложенное и применяя к вашей информации, мы видим, что ваш основной запрос выполняет кластеризованный индекс «сканирование» - то есть он просматривает всю таблицу, чтобы получить запрошенную вами информацию. Это означает, что не было найдено ни одного индекса, который позволил бы ему напрямую искать требуемую информацию. Для выполнения этого запроса SQL должен был бы выполнить фильтрацию для ваших полей 1 - 4, а затем агрегировать информацию (запрос MAX) по столбцам поля и даты.

В качестве крайнего примера, этот индекс разрешил бы поиск:

CREATE INDEX [idx_field1234567] ON [dbo].[t_Table](
   [iSomeField1], 
   [iSomeField2], 
   [iSomeField3], 
   [iSomeField4], 
   [dtDate], 
   [iField1],
   [iField2]
) 
WITH FILLFACTOR = 90, PAD_INDEX  ON [PRIMARY]

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

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

Удачи!

1 голос
/ 03 марта 2009

Это зависит. Какие индексы на столе? Какой объем строк в таблице? Я только что создал пример, который показал хорошие результаты, но он может сильно отличаться от вашего сценария. Как правило, если у оптимизатора возникают проблемы, запрос необходимо упростить. То, что вы сделали, может быть, то, что требуется. Это зависит. Вот SQL-код, который я выбрал, чтобы узнать, смогу ли я найти что-нибудь очевидное при включенном плане выполнения show.

установить nocount на

GO

если object_id ('tempdb .. # MaxMinExample') не является пустой таблицей перетаскивания # MaxMinExample

GO

создать таблицу #MaxMinExample ([ключ] int identity (1,1) кластеризованный первичный ключ, iField1 int, iField2 int, dtDate datetime, iSomeField1 int, iSomeField2 int, iSomeField3 int, iSomeField4 int)

GO

- исходный набор данных, который мы будем декартовым

вставить в #MaxMinExample (iField1, iField2, dtDate, iSomeField1, iSomeField2, iSomeField3, iSomeField4) значения (1,2, getdate (), 1,2,3,4) вставить в #MaxMinExample (iField1, iField2, dtDate, iSomeField1, iSomeField2, iSomeField3, iSomeField4) значения (2,3, getdate () + 1,4,5,6,7) вставить в #MaxMinExample (iField1, iField2, dtDate, iSomeField1, iSomeField2, iSomeField3, iSomeField4) значения (3,4, getdate () + 2,5,6,7,8) вставить в #MaxMinExample (iField1, iField2, dtDate, iSomeField1, iSomeField2, iSomeField3, iSomeField4) значения (5,6, getdate () + 3,6,7,8,9) вставить в #MaxMinExample (iField1, iField2, dtDate, iSomeField1, iSomeField2, iSomeField3, iSomeField4) значения (6,7, getdate () + 4,7,8,9,10)

GO

- создать загрузку данных

объявить @count int set @ count = 1 while (выберите количество (*) из #MaxMinExample) <865830 начать вставить в #MaxMinExample (iField1, iField2, dtDate, iSomeField1, iSomeField2, iSomeField3, iSomeField4) выберите a.iField1+@count, a.iField2 + @ count, a.dtDate + @ count, a.iSomeField1 + @ count, a.iSomeField2 + @ count, a.iSomeField3 + @ count, a.iSomeField4 + @ count из #MaxMinПример перекрестного соединения #MaxMinEx б set @ count = @ count + 1 конец </p>

GO

- создать индексы

создать индекс MaxMinExample_iSomeField1 для #MaxMinExample (iSomeField1) создать индекс MaxMinExample_iSomeField2 на #MaxMinExample (iSomeField2) создать индекс MaxMinExample_iSomeField3 для #MaxMinExample (iSomeField3) создать индекс MaxMinExample_iSomeField4 для #MaxMinExample (iSomeField4) создать индекс MaxMinExample_dtDate для #MaxMinExample (dtDate)

GO

объявите @ maxval1 int, @ maxval2 int, @ dtDateMin datetime, @ dtDateMax datetime, @ param1 int, @ param2 int, @ param3 int, @ param4 int

select @ param1 = 4, @ param2 = 5, @ param3 = 6, @ param4 = 7

select @ maxval1 = max (iField1), @ maxval2 = max (iField2), @dtDateMin = Min (dtDate), @ dtDateMax = Max (dtDate) из #MaxMinExample где iSomeField1 = @ param1 и iSomeField2 = @ param2 и iSomeField3 = @ param3 и iSomeField4 = @ param4

выберите top 1 @ maxval1 = iField1 из #MaxMinExample, где iSomeField1 = @ param1 и iSomeField2 = @ param2 и iSomeField3 = @ param3 и iSomeField4 = @ param4 упорядочены по iField1 DESC выберите top 1 @ maxval2 = iField2 из #MaxMinExample, где iSomeField1 = @ param1 и iSomeField2 = @ param2 и iSomeField3 = @ param3 и iSomeField4 = @ param4 упорядочены по iField2 DESC выберите top 1 @dtDateMin = dtDate из #MaxMinExample, где iSomeField1 = @ param1 и iSomeField2 = @ param2 и iSomeField3 = @ param3 и iSomeField4 = @ param4 упорядочены по dtDate ASC выберите top 1 @dtDateMax = dtDate из #MaxMinExample, где iSomeField1 = @ param1 и iSomeField2 = @ param2 и iSomeField3 = @ param3 и iSomeField4 = @ param4 упорядочены по dtDate DESC

Марк Бэкдал - www.dbghost.com - SQL Developer

0 голосов
/ 18 января 2010

В итоге проект перешел на новый стек DWH и BI, основанный на MicroStrategy, так что это стало проблемой, и новый DWH имеет другую схему и был выполнен в SQL 2008 (но я так и не дошел до ее: /)

0 голосов
/ 03 марта 2009

К сожалению, предоставляемой вами информации недостаточно, чтобы дать вам точный ответ, но я думаю, что могу дать вам полезную подсказку. SQL Server позволяет просматривать план запроса, который он использует для доступа к данным; Такой план подробно расскажет вам, к чему обращаются, когда, каким образом и сколько строк обрабатывается на каждом шаге. Он также сообщает вам, сколько времени / ресурсов занимает каждый шаг, что позволяет легко найти узкое место.

Чтобы отобразить план выполнения в Query Analyzer, откройте меню запросов и нажмите «Показать план выполнения». Затем выполните свой первый запрос и проверьте план; в другом окне выполните второй запрос и проверьте план еще раз. Таким образом, вы можете увидеть, в чем разница между ними, какие индексы (если таковые имеются) используются и лучше понять SQL Server.

Один намек: не расстраивайтесь, если в начале все кажется сложным, это просто вопрос медленной работы.

Наконец, полезным ресурсом для SQL Server (кроме MSDN, конечно) является www.sqlservercentral.com, где вы можете найти ответы от пользователей и экспертов. Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...