12

私はdbに接続するために多くのモデルを使用しました.C#とエンティティフレームワークを使用した最後のプロジェクトで、db接続用の静的クラスを作成しましたが、接続の開閉に問題があり、10-15を超えるとエラーが発生しましたリクエストが集まり、リクエストごとに今すぐ接続してdbへの接続方法を変更し、すべての静的メソッドとクラスを削除して解決しました。

今知りたいのは、

接続するのに最適なモデルは何ですか?

  1. すべてのクエリの後に閉じて、または...を使用する前に開く必要がありますか?
  2. 静的クラスの接続は良いモデルですか (毎回作成する必要はありません)?
  3. この問題に適した設計パターンはありますか?
  4. それはすべて同じ質問ですデータベース接続を作成するための最良の方法は何ですか(静的、抽象、リクエストごとなど)?

たとえば、SMS 送信者 Web パネルで作業している場合、1 秒あたり 100K の SMS を送信する必要があります。これらの SMS は他のユーザーと収集し、すべてのパッケージに 1 ~ 20 個の SMS が含まれるパッケージを作成し、1 秒あたり 5K ~ 100K のパッケージを送信する必要があります。私はパッケージを送信します。次の手順を実行する必要があります。

  1. 単一の SMS を配信済みまたは未配信に更新する
  2. 配信された場合はユーザー残高を更新して、useraccounts テーブルのユーザー残高を減らします
  3. ユーザーテーブルの SMS 送信回数を更新する
  4. 携帯電話番号テーブルの SMS 送信回数を更新する
  5. 送信者番号テーブルの SMS 送信回数の更新
  6. パッケージ テーブルの配信済みおよび失敗した SMS のパッケージを更新します
  7. スレッドがパッケージ テーブルでこのパッケージを送信する方法のパッケージを更新します。
  8. このトレッドによって送信された SMS の数と失敗した数のスレッド テーブルを更新します。
  9. この取引の口座伝票を AccountDocument テーブルに追加します

すべてのステップと、ログ、ユーザー インターフェイス、監視ウィジェットなどの他の多くのことを実行する必要があり、このトランザクションのすべてを実行するには DB 接続が必要です。

さて、DBに接続するのに最適なモデルは何ですか? 人間の要求、スレッド要求、またはすべてのトランザクションによって..

4

3 に答える 3

8

あなたの質問への答え:

  1. 閉じます。.NET は内部で接続プールを行います。

  2. 作成します。毎回 using (Connection conn = new ....) を使用してください。これにより、.NET プーリング メカニズムを最大限に活用できます。

  3. .NET ThreadPool (または独自のカスタム スレッド) を使用し、ThreadPool を定義して 10 スレッドのみを並列で使用し、作業項目を次々とエンキューできます。この方法では、同時に 10 を超える接続が使用されることはありません + おそらくより高速に動作します。カスタム ThreadPool の詳細:カスタム ThreadPool の実装

  4. インスタンスごと。


アーキテクチャに関する私の提案は次のとおりです。

  1. 保留中の SMS を送信するためのデータベース テーブル (キュー) を作成します。

  2. 各行には、SMS に必要なすべての情報と現在のステータスが含まれます。

  3. おそらく、このテーブルを絶えずサンプリングする Windows サービスなどのワーカー プロセスを作成します (たとえば、5 秒ごとに)。status = 'pending to be sent' の上位 ~20 件の SMS を選択します (int で表す必要があります)。ステータスを「送信中」に更新します

  4. 各 SMS は、Windows サービス側のカスタム スレッドプールを使用して送信されます。

  5. プロセスの最後に、処理されたすべての SMS ステータスが CTE を使用して「完了」に更新されます (共通テーブル式 - 「一括更新」を実行するために処理されたばかりのすべての SMS 行 ID を含む cte を送信できます)。 「完了」ステータスに)。

  6. ステータス更新ストアド プロシージャを「getpending」と同じにすることができます。このようにして、ロックなしで更新を選択し、データベースの動作を高速化できます。

  7. この方法では、複数のプロセッサ サービスを実行できます (ただし、その場合は nolock を解除する必要があります)。

可能な限りロックを避けることを忘れないでください。

ところで、保留中の SMS テーブルに行を追加するだけで、システム内の任意の場所から SMS を送信できるため、これも優れています。

もう 1 つ、これにはエンティティ フレームワークを使用することをお勧めしません。この種のタスクに必要なのは、3 ~ 4 個のストアド プロシージャを呼び出すだけです。それだけです。Dapper-dot-NETを見てみましょう。これは非常に軽量な MicroDal フレームワークで、ほとんどの場合、EF (Entity Framework) よりも 10 倍以上高速に動作します。

于 2013-05-13T06:54:49.283 に答える
7

1. Should i close it after every query?

.Net がそれを行うので、それを処理させます。これはガベージ コレクターのタスクです。したがって、オブジェクトを手動で破棄しないでください。これは、Jon Skeet による良い回答です: https://stackoverflow.com/a/1998600/544283。ただし、using(IDisposable){ }ステートメントを使用して、GC にその作業を強制することができます。リソースの再割り当てに関する素晴らしい記事があります: http://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About

2. A connection in static class is good?

データ コンテキストを静的にしないでください。データ コンテキストは、スレッド セーフでもコンカレント セーフでもありません。

3. Is there a good design pattern for this problem?

Belogix が言及したように、依存性注入と作業単位パターンは優れています。実際、エンティティ フレームワーク作業単位そのものです。DI と UoW は少し過大評価されていますが、IoC コンテナーを初めて扱う場合は実装が容易ではありません。その道を進む場合は、Ninject をお勧めします。もう 1 つは、テストを実行しない場合、DI は本当に必要ないということです。これらのパターンの素晴らしい点は分離することです。そのため、汗をかかずにテストとモックを行うことができます。

要するに: コードに対してテストを実行する場合は、これらのパターンを使用してください。そうでない場合は、必要なサービス間でデータ コンテキストを共有する方法の例を提供します。これが 4 番目の質問に対する答えです。

4. What is the best method for making database connection (static, per request)?

コンテキスト サービス:

public class FooContextService {
    private readonly FooContext _ctx;

    public FooContext Context { get { return _ctx; } }

    public FooContextService() {
        _ctx = new FooContext();
    }
}

他のサービス:

public class UnicornService {
    private readonly FooContext _ctx;

    public UnicornService(FooContextService contextService) {
        if (contextService == null)
            throw new ArgumentNullException("contextService");

        _ctx = contextService.Context;
    }

    public ICollection<Unicorn> GetList() {
        return _ctx.Unicorns.ToList();
    }
}

public class DragonService {
    private readonly FooContext _ctx;

    public DragonService(FooContextService contextService) {
        if (contextService == null)
            throw new ArgumentNullException("contextService");

        _ctx = contextService.Context;
    }

    public ICollection<Dragon> GetList() {
        return _ctx.Dragons.ToList();
    }
}

コントローラ:

public class FantasyController : Controller {
    private readonly FooContextService _contextService = new FooContextService();

    private readonly UnicornService _unicornService;
    private readonly DragonService _dragonService;

    public FantasyController() {
        _unicornService = new UnicornService(_contextService);
        _dragonService = new DragonService(_contextService);
    }

    // Controller actions
}

再考 (ほぼ編集): エンティティのプロキシを作成しないようにコンテキストが必要なため、遅延読み込みも必要ない場合は、次のようにコンテキスト サービスをオーバーロードできます。

public class FooContextService {
    private readonly FooContext _ctx;

    public FooContext Context { get { return _ctx; } }

    public FooContextService() : this(true) { }

    public FooContextService(bool proxyCreationEnabled) {
        _ctx = new FooContext();
        _ctx.Configuration.ProxyCreationEnabled = proxyCreationEnabled;
    }
}

ノート:

  • プロキシの作成を false に設定すると、すぐに遅延読み込みが行われなくなります。
  • API コントローラーを使用している場合、本格的なオブジェクト グラフを処理したくありません。

編集:

最初にいくつか読んでください:

これを成し遂げる:

(_context as IObjectContextAdapter).ObjectContext.Connection.Open();

これは、接続とトランザクションの管理に関するすばらしい記事です。

エンティティ フレームワークは、Connection プロパティを通じて EntityConnection を公開します。読み方: public sealed class EntityConnection : DbConnection.

接続の管理に関する考慮事項: (前のリンクから取得)

  • 操作の前に接続がまだ開かれていない場合、オブジェクト コンテキストは接続を開きます。操作中にオブジェクト コンテキストが接続を開くと、操作が完了すると常に接続が閉じられます。
  • 接続を手動で開いた場合、オブジェクト コンテキストは接続を閉じません。CloseまたはDisposeを呼び出すと、接続が閉じます。
  • オブジェクト コンテキストが接続を作成する場合、コンテキストが破棄されると、接続は常に破棄されます。
  • 実行時間の長いオブジェクト コンテキストでは、不要になったときにコンテキストを確実に破棄する必要があります。

それが役に立てば幸い。

于 2013-05-15T02:55:37.847 に答える
5

リクエストごとのスケーリングが最適だと思います。スレッドセーフな接続プールを使用し、接続スコープを作業単位と一致させます。トランザクションの動作と作業単位を担当するサービスが接続をチェックアウトして使用し、作業単位がコミットまたはロールバックされたときに接続をプールに戻します。

アップデート:

ステータスの更新をコミットするのに 10 ~ 12 秒かかりますか? あなたは何か他の間違ったことをしました。あなたの質問は、適切な回答を提供するには不十分です。

NASDAQ の 1 日あたりの取引量は 13 億トランザクションで、1 日 8 時間で 1 秒あたり約 45,000 トランザクションに相当します。あなたの出来高は NASDAQ の 2 倍です。1 台のマシンで実行しようとしている場合、NASDAQ は複数のサーバーを使用していると言えます。

また、ACID を使用してそのステータスを更新せずに済むのではないかと思います。結局のところ、スターバックスは 2 フェーズ コミットを使用していません。おそらく、より良い解決策は、プロデューサー/コンシューマー パターンとブロッキング キューを使用して、送信後にステータスを更新できるようにすることです。

于 2013-04-09T12:59:28.383 に答える