Спорадически медленные вызовы из приложения .NET к SQL Server - PullRequest
3 голосов
/ 12 сентября 2008

У меня есть таблица в SQL Server, которую я унаследовал от устаревшей системы, которая все еще находится в производстве, и которая структурирована в соответствии с приведенным ниже кодом. Я создал SP для запроса таблицы, как описано в коде под оператором создания таблицы. Моя проблема в том, что время от времени вызовы из .NET к этому SP как через Enterprise Library 4, так и через объект DataReader выполняются медленно. SP вызывается через структуру цикла на уровне данных, которая определяет параметры, которые входят в SP с целью заполнения пользовательских объектов. Также важно отметить, что медленный вызов не будет происходить при каждом прохождении структуры цикла. Обычно это будет хорошо в течение большей части дня или более, а затем начнется представление, что делает его чрезвычайно трудным для отладки.

Таблица содержит около 5 миллионов строк. Например, медленные вызовы будут занимать до 10 секунд, а быстрые вызовы - в среднем от 0 до 10 миллисекунд. Я проверил блокировку / блокировку транзакций во время медленных звонков, ни одной не было найдено Я создал несколько пользовательских счетчиков производительности в слое данных для контроля времени вызовов. По сути, когда производительность плохая, это действительно плохо для одного звонка. Но когда это хорошо, это действительно хорошо. Я смог воссоздать проблему на нескольких разных машинах для разработчиков, но не на наших серверах разработки и промежуточных баз данных, которые, конечно, имеют более мощное оборудование. Как правило, проблема решается путем перезапуска служб сервера SQL, но не всегда. В таблице есть индексы для полей, которые я запрашиваю, но есть больше индексов, чем мне бы хотелось. Однако я не решаюсь удалить какие-либо или игрушку с индексами из-за влияния, которое она может оказать на унаследованную систему. Кто-нибудь сталкивался с подобной проблемой раньше, или у вас есть рекомендации по ее устранению?

CREATE TABLE [dbo].[product_performance_quarterly](
    [performance_id] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [product_id] [int] NULL,
    [month] [int] NULL,
    [year] [int] NULL,
    [performance] [decimal](18, 6) NULL,
    [gross_or_net] [char](15) NULL,
    [vehicle_type] [char](30) NULL,
    [quarterly_or_monthly] [char](1) NULL,
    [stamp] [datetime] NULL CONSTRAINT [DF_product_performance_quarterly_stamp]  DEFAULT (getdate()),
    [eA_loaded] [nchar](10) NULL,
    [vehicle_type_id] [int] NULL,
    [yearmonth] [char](6) NULL,
    [gross_or_net_id] [tinyint] NULL,
 CONSTRAINT [PK_product_performance_quarterly_4_19_04] PRIMARY KEY CLUSTERED 
(
    [performance_id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[product_performance_quarterly]  WITH NOCHECK ADD  CONSTRAINT [FK_product_performance_quarterlyProduct_id] FOREIGN KEY([product_id])
REFERENCES [dbo].[products] ([product_id])
GO
ALTER TABLE [dbo].[product_performance_quarterly] CHECK CONSTRAINT [FK_product_performance_quarterlyProduct_id]

CREATE PROCEDURE [eA.Analytics.Calculations].[USP.GetCalculationData]
(
    @PRODUCTID INT,                     --products.product_id
    @BEGINYEAR INT,                     --year to begin retrieving performance data
    @BEGINMONTH INT,                    --month to begin retrieving performance data
    @ENDYEAR INT,                       --year to end retrieving performance data
    @ENDMONTH INT,                      --month to end retrieving performance data
    @QUARTERLYORMONTHLY VARCHAR(1),     --do you want quarterly or monthly data?
    @VEHICLETYPEID INT,                 --what product vehicle type are you looking for?
    @GROSSORNETID INT                   --are your looking gross of fees data or net of fees data?
)
AS
BEGIN

    SET NOCOUNT ON

    DECLARE @STARTDATE VARCHAR(6),
            @ENDDATE   VARCHAR(6),
            @vBEGINMONTH VARCHAR(2),
            @vENDMONTH VARCHAR(2)   

IF LEN(@BEGINMONTH) = 1 
    SET @vBEGINMONTH = '0' + CAST(@BEGINMONTH AS VARCHAR(1))
ELSE
    SET @vBEGINMONTH = @BEGINMONTH

IF LEN(@ENDMONTH) = 1
    SET @vENDMONTH = '0' + CAST(@ENDMONTH AS VARCHAR(1))
ELSE
    SET @vENDMONTH = @ENDMONTH

SET @STARTDATE = CAST(@BEGINYEAR AS VARCHAR(4)) + @vBEGINMONTH
SET @ENDDATE = CAST(@ENDYEAR AS VARCHAR(4)) + @vENDMONTH

--because null values for gross_or_net_id and vehicle_type_id are represented in 
--multiple ways (true null, empty string, or 0) in the PPQ table, need to account for all possible variations if 
--a -1 is passed in from the .NET code, which represents an enumerated value that
--indicates that the value(s) should be true null.

IF @VEHICLETYPEID = '-1' AND @GROSSORNETID = '-1'
    SELECT
        PPQ.YEARMONTH, PPQ.PERFORMANCE
    FROM PRODUCT_PERFORMANCE_QUARTERLY PPQ
        WITH (NOLOCK)
    WHERE 
        (PPQ.PRODUCT_ID = @PRODUCTID)
        AND (PPQ.YEARMONTH BETWEEN @STARTDATE AND @ENDDATE)
        AND (PPQ.QUARTERLY_OR_MONTHLY = @QUARTERLYORMONTHLY)
        AND (PPQ.VEHICLE_TYPE_ID IS NULL OR PPQ.VEHICLE_TYPE_ID = '0' OR PPQ.VEHICLE_TYPE_ID = '')
        AND (PPQ.GROSS_OR_NET_ID IS NULL OR PPQ.GROSS_OR_NET_ID = '0' OR PPQ.GROSS_OR_NET_ID = '')
    ORDER BY PPQ.YEARMONTH ASC

IF @VEHICLETYPEID <> '-1' AND @GROSSORNETID <> '-1'
    SELECT
        PPQ.YEARMONTH, PPQ.PERFORMANCE
    FROM PRODUCT_PERFORMANCE_QUARTERLY PPQ
        WITH (NOLOCK)
    WHERE 
        (PPQ.PRODUCT_ID = @PRODUCTID)
        AND (PPQ.YEARMONTH BETWEEN @STARTDATE AND @ENDDATE)
        AND (PPQ.QUARTERLY_OR_MONTHLY = @QUARTERLYORMONTHLY)
        AND (PPQ.VEHICLE_TYPE_ID = @VEHICLETYPEID )
        AND (PPQ.GROSS_OR_NET_ID = @GROSSORNETID)
    ORDER BY PPQ.YEARMONTH ASC

IF @VEHICLETYPEID = '-1' AND @GROSSORNETID <> '-1'
    SELECT
        PPQ.YEARMONTH, PPQ.PERFORMANCE
    FROM PRODUCT_PERFORMANCE_QUARTERLY PPQ
        WITH (NOLOCK)
    WHERE 
        (PPQ.PRODUCT_ID = @PRODUCTID)
        AND (PPQ.YEARMONTH BETWEEN @STARTDATE AND @ENDDATE)
        AND (PPQ.QUARTERLY_OR_MONTHLY = @QUARTERLYORMONTHLY)
        AND (PPQ.VEHICLE_TYPE_ID IS NULL OR PPQ.VEHICLE_TYPE_ID = '0' OR PPQ.VEHICLE_TYPE_ID = '')
        AND (PPQ.GROSS_OR_NET_ID = @GROSSORNETID)
    ORDER BY PPQ.YEARMONTH ASC

IF @VEHICLETYPEID <> '-1' AND @GROSSORNETID = '-1'
    SELECT
        PPQ.YEARMONTH, PPQ.PERFORMANCE
    FROM PRODUCT_PERFORMANCE_QUARTERLY PPQ
        WITH (NOLOCK)
    WHERE 
        (PPQ.PRODUCT_ID = @PRODUCTID)
        AND (PPQ.YEARMONTH BETWEEN @STARTDATE AND @ENDDATE)
        AND (PPQ.QUARTERLY_OR_MONTHLY = @QUARTERLYORMONTHLY)
        AND (PPQ.VEHICLE_TYPE_ID = @VEHICLETYPEID)
        AND (PPQ.GROSS_OR_NET_ID IS NULL OR PPQ.GROSS_OR_NET_ID = '0' OR PPQ.GROSS_OR_NET_ID = '')
    ORDER BY PPQ.YEARMONTH ASC

END

Ответы [ 4 ]

1 голос
/ 12 сентября 2008

Я видел, как это случилось с индексами, которые устарели. Это также может быть проблемой перехвата параметров, когда для разных параметров, входящих в хранимую процедуру, используется другой план запроса.

Вы должны зафиксировать параметры медленных вызовов и видеть, являются ли они одинаковыми каждый раз, когда он работает медленно.

Вы также можете попробовать запустить мастер настройки и посмотреть, рекомендует ли он какие-либо индексы.

Вам не нужно беспокоиться о наличии слишком большого количества индексов, пока вы не докажете, что обновления и вставки выполняются слишком медленно (время, необходимое для изменения индекса, плюс блокировка / конкуренция) или пока у вас недостаточно дискового пространства .

0 голосов
/ 22 октября 2008

Похоже, что это одна из двух вещей: либо параметры медленных вызовов в некотором роде отличаются от параметров быстрых вызовов, и они также не могут использовать индексы, либо существует некоторый тип конфликтов блокировки это держит тебя Вы говорите, что проверяли блокировку блокировок, пока какой-то конкретный процесс завис, и не видели ни одного - это говорит о том, что это первый. Однако - уверены ли вы, что ваш промежуточный сервер (на котором вы не можете воспроизвести эту ошибку) и серверы разработки (на которых вы можете воспроизвести ее) имеют одинаковую конфигурацию базы данных? Например, возможно, «READ COMMITTED SNAPSHOT» включен в рабочей среде, но не в разработке, что может привести к исчезновению проблем конкуренции за чтение в рабочей среде.

Если есть разница в параметрах, я бы предложил использовать SQL Profiler для просмотра транзакций и перехвата нескольких - некоторых медленных и некоторых более быстрых, а затем в окне Management Studio замените переменные в этом SP выше со значениями параметров, а затем получить план выполнения, нажав «Control-L». Это точно скажет вам, как SQL Server ожидает обработки вашего запроса, и вы можете сравнить план выполнения для различных комбинаций параметров, чтобы увидеть, есть ли разница с одним набором, и поработать над этим, чтобы оптимизировать его.

Удачи!

0 голосов
/ 12 сентября 2008

Странный, крайний случай, но я столкнулся с ним недавно.

Если запросы выполняются в приложении дольше, чем при запуске из Management Studio, вы можете проверить, отключен ли Arithabort. Параметры подключения, используемые Management Studio, отличаются от параметров, используемых в .NET.

0 голосов
/ 12 сентября 2008

Похоже, что другой запрос выполняется в фоновом режиме, который заблокировал таблицу, и ваш невинный запрос просто ждет его завершения

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