11

C#で作業した最後のプロジェクトでは、dbに接続するために多くのモデルを使用しました。エンティティフレームワーク、私はdbの接続のための静的クラスを作成したが、私は10-15以上の要求が一緒に来るとき私はエラーを与えるために接続の開閉に問題があったそしてすべての静的メソッドとクラスを削除しました。

今知りたい

接続するための最良のモデルは何ですか?

  1. クエリのたびに閉じてから使用する前に開くべきですか。
  2. 静的クラスでの接続は良いモデルです(私はする必要はありません)。 毎回作成しますか。
  3. この問題に対する良いデザインパターンはありますか?
  4. すべて同じ質問に対するものです。 データベース接続(静的、抽象的、リクエストごとなど)を作成しますか?

例えば私はSMS送信者のWebパネルに取り組んで、私は毎秒100KのSMSを送信する必要があります、これらのSMSは他人と収集し、すべてのパッケージは1〜20SMSを持つパッケージを作るパッケージ私はこれらの手順を実行する必要があります。

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

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

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


  • 見上げる作業単位そしてRepositoryパターン、始めるのに良い場所です。 - Belogix
  • トランザクションごとに新しい接続が必要なので(トランザクションが重複しないことを確認できない場合を除き)、トランザクションの使用方法が異なります。パフォーマンスを低下させる可能性がある分散トランザクションを使用する必要があるため、トランザクションごとに複数の接続を必要としません。トランザクションに関してこれを考えていないのであれば、おそらくそうするべきです。 - James Gaunt
  • オプション「2」はっきりしたノー - ノーです。絶対にしないでください。 - Marc Gravell
  • @ Mehdi - 作業単位は進むべき道です。私の主張は、Request / Static / Instance / Threadの観点から考えてはいけません。作業単位=トランザクションと考えます。データベースアクセスパターンは、実行する必要がある作業単位/トランザクション以外のものに結び付けてはいけません。これは、最高のパフォーマンスとトランザクションの安全性を得るための方法です。 Googleの作業ユニットには、さまざまな実装の中にたくさんのコード例があります。 - James Gaunt
  • 1秒あたりに多くのリクエストを処理する場合は、@ MehdiYeganeh TBQHを使用します。これは、1〜2億ドルの問題であるため、Amazonスケールの問題に対処したユーザーを見つけることができる最高のDBAを採用します。 - Yaur

3 답변


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

接続を管理するための考慮事項:(前のリンクから引用)

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

それが役に立てば幸い。


  • 何? 「オブジェクトを手動で破棄しないでください」。悪いアドバイスです。そして、using(IDisposable){ }GCに強制的に実行させるステートメント」不正確でもある - usingステートメントはGCに何もさせません。 - default.kramer
  • @ default.kramer:"Don't bother disposing your objects manually"誰がしますか各オブジェクトを手動で配置しますか? GCが自動的に何もしない各オブジェクトを手動で破棄しても、GCはリソースを解放する方法とタイミングについて非常に賢いと言っていました。そして"use the using{} statement to force the GC to do it's work":あなたはそれが内の資源であることを暗示しているかusingブロックは呼び出しませんDisposeスコープが終了したときのそのリソースのメソッドもしそうなら、なぜ実装する必要がありますIDisposableを使用する場合usingステートメント?これを反映するように回答を変更します。 - Esteban
  • あなたの最初の2つの文は、「DB接続を横にしたまま放置しないでください。GCが最終的にそれを処理します」のように聞こえます。多分それはあなたが意味するものではありません。 GCとの関係(またはその欠如)を誤解していると思いますIDisposable。 Ausing通話をブロックするDispose; GCはファイナライザを呼び出します。一般的な「セーフティネット」。ファイナライザに次のことを保証させることです。Disposeと呼ばれていましたが、行儀の良いアプリケーションではこのセーフティネットは必要ないはずです - すべてIDisposablesGCの関与なしに処分する必要があります。 - default.kramer
  • また、「テストを実行しないのであれば、本当にDIは必要ありません」と同意しません。良いオブジェクト指向設計は、DIを優先する傾向があります(コンテナ管理かどうかは関係ありません)。私は、主な利点を「合成可能性」として説明します。これはより保守しやすいアプリケーションにつながります。テスト容易性は確かに利点ですが、優れたデザインの副次的な効果です。 「テストを書くつもりなら、これを、そうでなければ、このようにしてください。」 - default.kramer
  • @ Esteban tanx私を助けてくれてありがとう。私は使い捨てオブジェクトとガベージコレクタについて知っていますが、ガベージコレクタによる接続を閉じる処理をさせて例をチェックさせるのは安全ではないと思います。いくつかのlinQクエリを持っています。スレッドとスレッドは、レコードのステータスを更新し、すべてのSMSパッケージとすべての携帯電話番号をロードして費用を計算する必要があります。非同期呼び出しメソッドを使用した場合、GCはそれを処理できません。そしてすべての接続は同時に開かれたままです。 - Mehdi Yeganeh

7

あなたの質問に対する答え:

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

  2. それを作成してください。毎回using(Connection conn = new ....)を使用してください。このようにして、.NETプーリング機構を最大限に活用できます。

  3. .NET ThreadPool(または独自のカスタムスレッド)を使用し、ThreadPoolを10スレッドのみを並列に使用するように定義し、次々に作業項目をエンキューすることができます。このようにすれば、10回以上の接続が同時に使用されることはなくなり、おそらくより速く動作するでしょう。 カスタムスレッドプールの詳細:カスタムThreadPoolの実装

  4. インスタンスごと


これが私のアーキテクチャーの提案です。

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

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

  3. ワーカープロセス、おそらくこのテーブルを絶えずサンプリングするwindowsサービスを作成しましょう - たとえば、5秒ごとにしましょう。 status = '送信待ち'のTOP〜20のSMSを選択します(intで表す必要があります)。ステータスを「送信中」に更新します

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

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

  6. ステータス更新ストアドプロシージャを 'getpending'と同じものにすることができます。このようにして、ロックなしでupdate-for-updateを実行し、データベースをより高速に動作させることができます。

  7. このようにして、複数のプロセッササービスを実行させることができます(ただし、その後nolockを解放する必要があります)。

できるだけ多くのロックを避けるようにしてください。

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

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


  • 私はそれをテストしようとしています。 - Mehdi Yeganeh
  • カスタムシアープールで作業するための良いデザインパターンを教えてください。 - Mehdi Yeganeh

5

私は要求ごとのスケールが最も良いと思います。スレッドセーフな接続プールを使用して、接続範囲を作業単位と一致させます。トランザクションの振る舞いと作業単位を担当するサービスが接続をチェックアウトし、それを使用して、作業単位がコミットまたはロールバックされたときにプールに戻すようにします。

更新:

ステータス更新をコミットするのに10〜12秒?あなたは他の何か悪いことをしました。書かれているあなたの質問は適切な答えを提供するのに十分ではありません。

毎日のNASDAQのボリューム1.3Bトランザクションであり、8時間の日には毎秒約45Kトランザクションになります。あなたのボリュームはナスダックの2倍です。もしあなたが一つのマシンでそれをやろうとしているなら、私はNASDAQが複数のサーバーを使っていると言うでしょう。

ACIDを使ってそのステータスを更新しなくてもできるかどうかも疑問に思います。結局、スターバックスは2フェーズコミットを使用しません。たぶんもっと良い解決策は、それらが送られた後にあなたができるときにそれらの状態を更新するためにブロッキングキューを持つプロデューサー/コンシューマーパターンを使うことでしょう。


  • 私は私のプロジェクトでより多くの問題を抱えていた..そのSMS送信者パネル私はそれを送信するすべての単一のSMSは毎秒100000 SMSを送信し、dbのステータスを更新する必要がありますそして一方で、ユーザーは送信SMSを追跡します。今私が要求ごとに使用したとき私のCPUパフォーマンスは最高になり、私は1秒でそれすべてを送信することができません、それは10-12秒かかります。その問題は私が私のハードウェアをアップグレードする、私が最善の方法を知っていて、安全なものが要求ごとに接続することであるなら! - Mehdi Yeganeh
  • 10000秒で1つのステータスの更新をコミットするのではなく、1秒間に100000を送信し、さらに1秒に100Kを送信したい場合は100000を送信します。差出人番号ユーザー送信数& A単一スレッドを数えるとSMSを送信し、私はユーザーの残高を減らし、私は1 SMSと他の多くのビジネスを送信するためのすべてのアカウント文書トランザクションを作成します。 - Mehdi Yeganeh
  • スターバックスへの感謝は、2フェーズコミットを使用していません。 - Mehdi Yeganeh
  • あなたはこれらのコメントには複雑で一貫性がないように聞こえます。私はあなたのためにもっと多くのことができるとは思わない。投票する - duffymo
  • すみません、私の英語は私の性格ではないと思います。私は5回dbに接続するために私のクラスのすべてを変更し、毎回それを読むたびに私は1つの方法がより優れています。と私は思う、私はDB接続に大きな問題を抱えていた。 dbに接続するためのパターンを教えてください。作業単位について読み始めます。スターバックスは2フェーズコミットを使用していません、私はそれが良いと思いますが、もっとパターンを見つけたいのです。 - Mehdi Yeganeh

リンクされた質問


関連する質問

最近の質問