データベース接続プールNET


作者:eaglet
転載は出典を明記してください
.netでSqlConnectionでsql serverを接続すると、最初の接続には常に時間がかかることがわかりますが、後で接続するのは速いです.これは実際にSqlConnectionの接続プールメカニズムと関係があり、この接続プールメカニズムを正しく理解することで、効率的なデータベースアプリケーションの作成に役立ちます.
 
SqlConnectionの接続は時間がかからないと考える人が多く、その理由はSqlConnectionをループ実行することである.Openで得られる平均時間はほぼ0ですが、初めてオープンするたびに数ミリ秒から数秒に達することが多いのはなぜですか.
まずMSDNの権威ある文書で何を言っているのか見てみましょう
Connecting to a database server typically consists of several time-consuming steps. A physical channel such as a socket or a named pipe must be established, the initial handshake with the server must occur, the connection string information must be parsed, the connection must be authenticated by the server, checks must be run for enlisting in the current transaction, and so on.
以上より抜粋http://msdn.microsoft.com/en-us/library/8xx3tyca%28VS.80%29.aspx
つまり、物理接続が確立されると、サーバと握手したり、接続文字列を解析したり、許可したり、制約をチェックしたりする必要がありますが、物理接続が確立されると、これらの操作は行われません.これらの操作には一定の時間がかかります.そのため、多くの人は1つの静的オブジェクトでSqlConnectionを保存して常に物理的な接続を維持するのが好きですが、静的オブジェクトを採用すると、マルチスレッドアクセスにいくつかの問題が発生します.実際には、SqlConnectionはデフォルトで接続プール機能が開いているため、プログラムがSqlConnectionを実行する必要はありません.Close後、物理的な接続はすぐに解放されないので、Open操作をループ実行する場合、実行時間はほぼ0となる.
次に、接続プールを開かないときに、SqlConnectionをループして実行することを見てみましょう.Openの消費時間
        public static void OpenWithoutPooling()
        {
            string connectionString =
                "Data Source=192.168.10.2; Initial Catalog=News; Integrated Security=True;Pooling=False;";

            Stopwatch sw = new Stopwatch();

            sw.Start();
            using (SqlConnection conn =
                new SqlConnection(connectionString))
            {
                conn.Open();
            }

            sw.Stop();
            Console.WriteLine("Without Pooling, first connection elapsed {0} ms", sw.ElapsedMilliseconds);

            sw.Reset();

            sw.Start();

            for (int i = 0; i < 100; i++)
            {
                using (SqlConnection conn = new SqlConnection(connectionString))
                {
                    conn.Open();
                }
            }

            sw.Stop();
            Console.WriteLine("Without Pooling, average connection elapsed {0} ms", sw.ElapsedMilliseconds / 100);
        }

 
SqlConnectionのデフォルトは接続プールを開きます.強制的に閉じるには、接続文字列にPooling=Falseを追加する必要があります.
呼び出しプログラムは次のとおりです.
                Test.SqlConnectionTest.OpenWithoutPooling();
                Console.WriteLine("Waiting for 10s");
                System.Threading.Thread.Sleep(10 * 1000);
                Test.SqlConnectionTest.OpenWithoutPooling();
                Console.WriteLine("Waiting for 600s");
                System.Threading.Thread.Sleep(600 * 1000);
                Test.SqlConnectionTest.OpenWithoutPooling();

次はテスト結果です
 
Without Pooling, first connection elapsed 13 ms Without Pooling, average connection elapsed 5 ms Wating for 10s Without Pooling, first connection elapsed 6 ms Without Pooling, average connection elapsed 4 ms Wating for 600s Without Pooling, first connection elapsed 7 ms Without Pooling, average connection elapsed 4 ms
このテスト結果から,接続プールを閉じると,接続ごとに平均4ミリ秒程度かかることが物理接続を確立する平均時間である.
 
デフォルトのテストコードを見てみましょう
        public static void OpenWithPooling()
        {
            string connectionString =
                "Data Source=192.168.10.2; Initial Catalog=News; Integrated Security=True;";
            
            Stopwatch sw = new Stopwatch();

            sw.Start();
            using (SqlConnection conn =
                new SqlConnection(connectionString))
            {
                conn.Open();
            }

            sw.Stop();
            Console.WriteLine("With Pooling, first connection elapsed {0} ms", sw.ElapsedMilliseconds);

            sw.Reset();

            sw.Start();

            for (int i = 0; i < 100; i++)
            {
                using (SqlConnection conn = new SqlConnection(connectionString))
                {
                    conn.Open();
                }
            }

            sw.Stop();
            Console.WriteLine("With Pooling, average connection elapsed {0} ms", sw.ElapsedMilliseconds / 100);
        }

 
コールコード
 
                Test.SqlConnectionTest.OpenWithPooling();
                Console.WriteLine("Waiting for 10s");
                System.Threading.Thread.Sleep(10 * 1000);
                Test.SqlConnectionTest.OpenWithPooling();
                Console.WriteLine("Waiting for 600s");
                System.Threading.Thread.Sleep(600 * 1000);
                Test.SqlConnectionTest.OpenWithPooling();

テスト結果
With Pooling, first connection elapsed 119 ms With Pooling, average connection elapsed 0 ms Waiting for 10s With Pooling, first connection elapsed 0 ms With Pooling, average connection elapsed 0 ms Waiting for 600s With Pooling, first connection elapsed 6 ms With Pooling, average connection elapsed 0 ms
このテスト結果を見ると、最初の時間は119 msである.これは私がテストコードの中で、まずこのテストプロセスを実行しているからである.119 msはプログラムの最初の起動時の最初の接続時間であり、この時間にはデータベースに接続する時間だけでなくadoも含まれる可能性がある.Netは自分で初期化するときに使うので、このときは放っておいてもいいです.10秒後にこのテストプロセスを実行すると、最初の実行時間が0 msになります.これは接続プールメカニズムが機能していることを示しています.SqlConnection Close後、物理的な接続は閉じられていないので、10秒後に実行し、接続にはほとんど時間がかかりません.
しかし,興味深い現象を発見し,10分後,最初の接続時間が6 msになり,これは前に接続プールを開かないテスト用とほぼ同じであり,すなわち10分後に物理接続が閉鎖され,また物理接続が再開された.この現象は、接続プールにタイムアウト時間があるため、デフォルトでは5~10分で、その間に接続操作がなければ物理接続が閉じられます.では、物理的な接続を維持する方法はありますか?方法はある.
接続プールの設定には、最小接続プールサイズがあります.デフォルトは0です.0より大きい値に設定すると、いくつかの物理的な接続が常に解放されません.コードを見る
 
 
        public static void OpenWithPooling(int minPoolSize)
        {
            string connectionString =
                string.Format("Data Source=192.168.10.2; Initial Catalog=News; Integrated Security=True;Min Pool Size={0}",
                    minPoolSize);

            Stopwatch sw = new Stopwatch();

            sw.Start();
            using (SqlConnection conn =
                new SqlConnection(connectionString))
            {
                conn.Open();
            }

            sw.Stop();
            Console.WriteLine("With Pooling Min Pool Size={0}, first connection elapsed {1} ms", 
                minPoolSize, sw.ElapsedMilliseconds);

            sw.Reset();

            sw.Start();

            for (int i = 0; i < 100; i++)
            {
                using (SqlConnection conn = new SqlConnection(connectionString))
                {
                    conn.Open();
                }
            }

            sw.Stop();
            Console.WriteLine("With Pooling Min Pool Size={0}, average connection elapsed {1} ms", 
                minPoolSize, sw.ElapsedMilliseconds / 100);
        }

実は接続文字列にMin Pool Size=nを1つ入れるだけです.
コールコード
                Test.SqlConnectionTest.OpenWithPooling(1);
                Console.WriteLine("Waiting for 10s");
                System.Threading.Thread.Sleep(10 * 1000);
                Test.SqlConnectionTest.OpenWithPooling(1);
                Console.WriteLine("Waiting for 600s");
                System.Threading.Thread.Sleep(600 * 1000);
                Test.SqlConnectionTest.OpenWithPooling(1);

 
With Pooling Min Pool Size=1, first connection elapsed 5 ms With Pooling Min Pool Size=1, average connection elapsed 0 ms Waiting for 10s With Pooling Min Pool Size=1, first connection elapsed 0 ms With Pooling Min Pool Size=1, average connection elapsed 0 ms Waiting for 600s With Pooling Min Pool Size=1, first connection elapsed 0 ms With Pooling Min Pool Size=1, average connection elapsed 0 ms
Min Pool Size=1の場合,初回接続用時5 msを除き,10分経過しても0 msであり,物理的接続は閉じられていないことが分かる.
 

マルチスレッド呼び出しの問題


マルチスレッド呼び出しもテストしましたが、ここではコードを貼らないので、結果を大体話します.マルチスレッドアクセスSqlConnectionの場合はnew SqlConnection方式でアクセスすることに注意し、
ここで2つの問題があるが、後者のスレッドが前のスレッドCloseの前でOpen操作を呼び出すと、Ado.Netは、2番目のスレッドに新しい物理接続を割り当てる物理接続を多重化することはできません.次のスレッドがOpenのときに前のスレッドがCloseになっている場合、新しいスレッドは前のスレッドの物理的接続を使用します.すなわち,n個のスレッドが同時にデータベースに接続されている場合,最大でn個の物理接続が作成され,最低で1個である.n本の物理接続を作成する場合、使用時間は理論的にはn*t/cpuに等しく、nはスレッド数、tは物理接続を作成するたびに使用され、前のテストの結果は約5-10 ms程度、cpuは現在のマシンのCPU数である.また、ネットワーク、サーバの負荷もこの使用に影響します.大きな同時性を保証するために、できるだけ少ないのは新しい物理の接続を創建して、私達は適当にMin Pool Sizeを少し大きくすることができて、しかしあまり大きくないでください、単一の機械のTCPリンクの数量が有限なため、詳しく私の別の1篇の文章のWindowsの下で単機の最大のTCPの接続数を参照してください
接続文字列の接続プールに関するパラメータ
次のリンクを参照してください.ConnectionString Property
 

IIS回収アプリケーションプールの接続プールへの影響


ASPをやっています.NETプログラムの場合、IISのデフォルトのidle timeoutは20分で、20分以内にアクセスが1つもない場合、IISはアプリケーションプールを回収し、アプリケーションプールを回収した結果、アプリケーションが再起動されることに相当し、元のグローバル変数、session、物理接続が空になることがわかります.アプリケーション・プールの回収後に初めてアクセスするのは、前に見たプログラムの起動後に初めてデータベースにアクセスするのと同じで、接続の確立時間が長くなります.そのため、サイトのアクセス量が少ない場合は、idle timeoutが適切に設定されているかどうかを考慮する必要があります.