0

環境:

私は十数台のサーバーを持っています。

各サーバーには、次の大きな SQL スクリプトを 5 分ごとに実行するサイトを持つ IIS があります。

一部のサーバーでは、サイトをホストするプールがクラッシュします。プールにはこのサイトのみが含まれます。

クラッシュのたびにプールをリサイクルする必要があります...現在、手で。

そのため、サイトに問題があり、大きな SQL スクリプトに問題があると思います。

SQL スクリプトを呼び出す C# コード:

string root = AppDomain.CurrentDomain.BaseDirectory;
string script = File.ReadAllText(root + @"..\SGBD\select_user_from_all_bases.sql").Replace("$date", dtLastModif);
string connectionString = @"Data Source=(local);Integrated Security=SSPI";

using (var connection = new SqlConnection(connectionString))
{
    connection.Open();

    var command = new SqlCommand(script, connection);
    var reader = command.ExecuteReader();

    var users = new List<UserModel>();

    while (reader.Read())
    {
        users.Add(new UserModel()
        {
            dbName = String.Format("{0}", reader[0]),
            idExternal = int.Parse(String.Format("{0}", reader[1])),
            firstname = String.Format("{0}", reader[2]),
            lastname = String.Format("{0}", reader[3]),
            login = String.Format("{0}", reader[4]),
            password = String.Format("{0}", reader[5]),
            dtContractStart = reader[6] != DBNull.Value ? (DateTime?)reader[6] : null,
            dtContractEnd = reader[7] != DBNull.Value ? (DateTime?)reader[7] : null,
            emailPro = String.Format("{0}", reader[8]),
            emailPerso = String.Format("{0}", reader[9])
        });
    }

    return users;
}

そしてSQLスクリプト:

USE master

DECLARE db_names CURSOR FOR
    SELECT name FROM sysdatabases WHERE [name] LIKE 'FOO_%' AND [name] NOT LIKE 'FOO_TRAINING_%'

DECLARE @db_name NVARCHAR(100)
DECLARE @query NVARCHAR(MAX)
DECLARE @queryFinal NVARCHAR(MAX)
SET @query = ''
OPEN db_names 

FETCH NEXT FROM db_names INTO @db_name

WHILE @@FETCH_STATUS = 0
BEGIN

    SET @query = @query + 'SELECT ''' + @db_name + ''', id_salarie, nom, prenom, login COLLATE SQL_Latin1_General_CP1_CI_AS, password COLLATE SQL_Latin1_General_CP1_CI_AS, date_arrivee, date_depart, email COLLATE SQL_Latin1_General_CP1_CI_AS, persoMail COLLATE SQL_Latin1_General_CP1_CI_AS FROM [' + @db_name + '].dbo.utilisateurs WHERE dt_last_modif >= ''$date'' UNION '

    FETCH NEXT FROM db_names INTO @db_name

END

DEALLOCATE db_names 
SET @queryFinal = left(@query, len(@query)-6)
    EXEC sp_executesql @queryFinal

サーバーに関する詳細情報:

  • Server0 : 8 つのデータベース、1050 人のユーザー、クラッシュなし
  • Server1 : 88 データベース、18954 ユーザー、頻繁にクラッシュ
  • Server2 : 109 データベース、21897 ユーザー、頻繁にクラッシュ
  • Server3 : 26 データベース、1612 ユーザー、クラッシュなし

質問:

  • スクリプトの問題は何ですか? クラッシュを止める方法はありますか?
  • 解決策がない場合、プールを自動的にリサイクルするにはどうすればよいですか?
4

2 に答える 2

1

私はここでいくつかのことをします...あなたの問題がそれほど持続するなら。まず、これらすべてのテーブルから一度にデータを取得しようとする1つの完全なSQLクエリを生成しません。次に、クエリはクエリを実行しており、おそらく更新の可能性がない場合でも、クエリに関連付けられているレコードをロックしようとしている可能性があります。

WITH (NOLOCK)fromテーブル にを追加します。

select columns from yourTable WITH(NOLOCK) where...

これにより、クエリに関連付けられているすべてのページをロックすることによるオーバーヘッドが防止されます。

次に、ループをより適切に処理する方法を説明します。フェッチループの直前に、期待される出力結果の一時テーブルを作成します...

(構造の列名の長さが不明です。..

create #C_TempResults
  ( fromDBName char(20), 
    id_salarie int,
    nom char(10),
    prenom char(10),
    login char(10),
    password char(10),
    date_arivee datetime,
    date_depart datetime,
    email char(60),
    persoMail char(60) );

次に、クエリを実行しているすべてのテーブルを既に循環しているループで、最後に実行する連結SQLステートメントを作成する代わりに、一度に1つずつ実行し、次のように一時テーブルに挿入します...

(same beginning to prepare your fetch cursor...)
BEGIN
       SET @query = 'INSERT INTO #C_TempResults '
                 + ' SELECT ''' + @db_name + ''' as fromDBName, id_salarie, nom, prenom, '
                       + 'login COLLATE SQL_Latin1_General_CP1_CI_AS, '
                       + 'password COLLATE SQL_Latin1_General_CP1_CI_AS, '
                       + 'date_arrivee, date_depart, '
                       + 'email COLLATE SQL_Latin1_General_CP1_CI_AS, '
                       + 'persoMail COLLATE SQL_Latin1_General_CP1_CI_AS '
                 +  'FROM [' + @db_name + '].dbo.utilisateurs WITH (NOLOCK) '
                 +  'WHERE dt_last_modif >= ''$date'' ';

        -- Run this single query now, get the data and release any "lock" resources
        EXEC sp_executesql @queryFinal

        -- now, get the next database to query from and continue
        FETCH NEXT FROM db_names INTO @db_name
END

DEALLOCATE db_names 

-- FINALLY, just run your select from the temp table that has everything all together...
select * from #C_TempResults;

-- and get rid of your "temp" table
drop table #C_TempResults;
于 2013-01-25T12:43:58.983 に答える
1

使用後もリーダーが閉じていることを確認しようとしましたか?

using(var reader = command.ExecuteReader()) { ...

接続が閉じているかどうかはわかりません

using (var connection = new SqlConnection(connectionString))

コマンドとリーダーのリソースを処理します。

于 2013-01-25T12:18:53.470 に答える