3

以下のコードに従って構造化された、まだ運用中のレガシー システムから継承した SQL Server のテーブルがあります。table create ステートメントの下のコードで説明されているように、テーブルをクエリするための SP を作成しました。私の問題は、エンタープライズ ライブラリ 4 と DataReader オブジェクトの両方を介した .NET からこの SP への呼び出しが散発的に遅いことです。SP は、データ層のループ構造を介して呼び出されます。ループ構造は、ユーザー オブジェクトを設定する目的で SP に入るパラメーターを指定します。また、ループ構造を通過するたびに遅い呼び出しが発生するわけではないことに注意することも重要です。通常、ほとんどの場合、またはそれ以上は問題なく、その後表示を開始すると、デバッグが非常に困難になります。

問題のテーブルには、約 500 万行が含まれています。たとえば、遅い呼び出しには 10 秒もかかりますが、速い呼び出しには平均で 0 ~ 10 ミリ秒かかります。スロー コール中にトランザクションのロック/ブロックを確認しましたが、何も見つかりませんでした。呼び出し時間を監視するために、データ層にいくつかのカスタム パフォーマンス カウンターを作成しました。基本的に、パフォーマンスが悪いと、その 1 つの呼び出しが非常に悪くなります。でも、いいときは本当にいい。いくつかの異なる開発者マシンで問題を再現できましたが、もちろんより強力なハードウェアを備えた開発およびステージング データベース サーバーでは再現できませんでした。通常、問題は 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

4 に答える 4

1

古いインデックスでこれが発生するのを見てきました。また、ストアド プロシージャに渡されるさまざまなパラメーターに対して別のクエリ プランが使用されている場合、パラメーター スニッフィングの問題である可能性もあります。

遅い呼び出しのパラメーターを取得し、実行が遅くなるたびにそれらが同じものであるかどうかを確認する必要があります。

また、チューニング ウィザードを実行して、推奨されるインデックスがあるかどうかを確認することもできます。

更新と挿入が遅すぎること (インデックスの変更に必要な時間とロック/競合) が証明されるまで、またはそれらのディスク領域が不足していることを証明できるまで、インデックスが多すぎることを心配する必要はありません。

于 2008-09-11T21:54:14.287 に答える
0

テーブルをロックしている別のクエリがバックグラウンドで実行されており、無実のクエリが終了するのを待っているようです

于 2008-09-11T21:46:42.027 に答える
0

奇妙なエッジケースですが、最近遭遇しました。

クエリが Management Studio 内から実行される場合よりもアプリケーションで実行される時間が長い場合は、Arithabort がオフに設定されていることを確認する必要がある場合があります。Management Studio で使用される接続パラメーターは、.NET で使用されるものとは異なります。

于 2008-09-11T21:55:18.217 に答える
0

それは2つのうちの1つであるようです-遅い呼び出しのパラメーターが速い呼び出しとは何らかの方法で異なり、インデックスも使用できないか、何らかのタイプのロック競合が発生しています上。あなたは、特定のプロセスがハングしている間にロックをブロックしていないかどうかを確認したが、何も見られなかったと言います。これは、それが最初のものであることを示唆しています。ただし、ステージング サーバー (このエラーを再現できない) と開発サーバー (再現できる) のデータベース構成が同じであると確信していますか? たとえば、本番環境では「READ COMMITTED SNAPSHOT」が有効になっているが、開発環境では有効になっていない可能性があります。これにより、本番環境で読み取り競合の問題が解消されます。

パラメーターの違いである場合は、SQL プロファイラーを使用してトランザクションを監視し、低速のものと高速のものをいくつかキャプチャしてから、Management Studio ウィンドウで、上記の SP の変数をパラメーターに置き換えることをお勧めします。値を入力してから、「Control-L」を押して実行計画を取得します。これにより、SQL Server がクエリをどのように処理することを期待しているかが正確にわかり、さまざまなパラメーターの組み合わせの実行プランを比較して、1 つのセットに違いがあるかどうかを確認し、そこから作業して最適化することができます。

幸運を!

于 2008-10-22T04:44:30.417 に答える