none
SqlDataSourceのItemDataBound内でさらにSQL発行 RRS feed

  • 質問

  •                                                       

    いつもお世話になっております。
    ListViewを用いた商品発注ページを作成しているのですが、
    利用者数が多くなるとアプリケーションプールが増加し、
    高負荷状態が続きサーバがダウンするといった現象が発生しております。

    ListViewに表示している商品数は約500アイテム(500行)ほどあり、
    商品、規格、ケース単価、単品単価、ケース発注数、単品発注数という
    簡単な表構成で、ケース発注数、単品発注数に必要数を入力し
    発注ボタンを押すことでデータベースに書き込む仕組みです。

    単価情報が仕入れの状況によって、変化するので、
    単価テーブルと注文テーブルは別々に管理しているのですが、
    ページを表示する度にSqlDataSourceコントロールのSelectCommandでデータを取得したのち、
    ItemDataBoundの中でそれぞれの商品、規格の500行に対して、注文テーブルを検索し
    あれば値をセットするといった処理を行っております。

    つまり1回LisViewを表示する間にItemDataBoundの中で約500回の
    MySqlConnectionの接続、SQL発行、切断が発生していることになります。
    この状態でおそらく10人が同時にアクセスを行うことによって
    ダウンするのかと推測しているのですが、
    SqlDataSourceのSelectCommand、ItemDataBoundの中で
    さらにMYSQLデータを接続し、検索しにいくような制御はまずいでしょうか?
    また、他の方法で発注データをセットするにはどのようにすれば良いでしょうか。


    <asp:SqlDataSource ID="MySqlDataSource" runat="server"
         ConnectionString="<%$ ConnectionStrings:MySqlDataSorce %>"
         ProviderName="<%$ ConnectionStrings:MySqlDataSorce.ProviderName %>"
         SelectCommand="SELECT m.item as item,m.lot as lot,t.price1 ,t.price2 FROM 商品マスタ m,単価テーブル t
       where m.item=t.item and m.lot=t.lot and m.col_hide=0 order by m.sort_id,m.item,m.lot"></asp:SqlDataSource>


    protected void _listView_ItemDataBound(object sender, ListViewItemEventArgs e)
        {

          DateTime now = DateTime.Now;
          TextBox txt_data_1 = (TextBox)e.Item.FindControl("txt_data8_1");
          DropDownList txt_data_2 = (DropDownList)e.Item.FindControl("txt_data8_2");
          TextBox freecoment = (TextBox)e.Item.FindControl("freecoment");

          //登録データ検索
          Label itemLabel = (Label)e.Item.FindControl("itemLabel");
          Label lotLabel = (Label)e.Item.FindControl("lotLabel");

          string conStr = WebConfigurationManager.ConnectionStrings["MySqlDataSorce"].ConnectionString;
          MySqlConnection con = new MySqlConnection(conStr);
          con.Open();

          DateTime date_day = DateTime.Parse(Session["select_day"].ToString().Substring(0, 4) + "/" + Session["select_day"].ToString().Substring(4, 2) + "/" + Session["select_day"].ToString().Substring(6, 2));

          MySqlCommand cmd = new MySqlCommand("select order_date1_1,order_date1_2,freetxt from 注文テーブル where item='" + itemLabel.Text + "' and lot='" + lotLabel.Text + "' and nouhin_date='" + date_day.ToString("yyyy/MM/dd") + "' and corp_id='" + Session["corp_id"].ToString() + "' ", con);
          MySqlDataReader reader = cmd.ExecuteReader();

          while (reader.Read())
          {
            if (reader["order_date1_1"].ToString() != "0")
            {
               //ケース
               txt_data_1.Text = reader["order_date1_1"].ToString();
               txt_data_2.SelectedIndex = 0;
                    }
               else
               {
                //単品
                txt_data_1.Text = reader["order_date1_2"].ToString();
                txt_data_2.SelectedIndex = 1; 
               }
               freecoment.Text = reader["freetxt"].ToString();
           }

           reader.Close();
           cmd = null;
           con.Close();

       }

    2015年9月25日 6:08

回答

  • 最初から単価テーブルと注文テーブルを結合して取得すれば良いように思えますが、もし、そうできない理由があるのでしたら教えて下さい。

    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    • 回答としてマーク hys73 2015年9月25日 16:35
    2015年9月25日 6:25
    モデレータ
  • > 利用者数が多くなるとアプリケーションプールが増加し、
    > 高負荷状態が続きサーバがダウンするといった現象が発生しております。

    「サーバがダウン」というのは具体的にどういう状況なのでしょうか?

    スレッドプールのスレッドが枯渇して、要求がキューにたまって、その結果 HTTP エラー 503 (サービス利用不可) になるということなのでしょうか?

    その場合、以下の記事に書いてあるような対策は既に取られているのでしょうか?

    ASP.NET 最大要求数
    http://surferonwww.info/BlogEngine/post/2015/06/26/aspnet-request-queue-limit.aspx

    • 回答としてマーク hys73 2015年9月25日 16:35
    2015年9月25日 8:20
  • 既に解決済みとなっているので今さらながらのレスかもしれませんが、今後のために気がついたことを書いておきます。

    (1) パラメータ化クエリを使う。

    ひょっとしたら、質問者さんのコードで一番のボトルネックは ItemDataBound イベントのハンドラの中でパラメータ化クエリを使ってないことかもしれません。

    パラメータ化クエリ
    http://surferonwww.info/BlogEngine/post/2012/02/02/Parameterized-query.aspx

    パラメータ化クエリを使うのは SQL インジェクション攻撃防止のためだけではなく、もう一つの重要なメリットにパフォーマンスの向上があります。


    (2) コネクションリークを防止する。

    MySQL と Connector/NET でもコネクションプールを利用していると思いますが、コネクションリークを防止するために try-finally 処理もしくは using 句を使いましょう。

    .NETの例外処理 Part.2
    http://blogs.msdn.com/b/nakama/archive/2009/01/02/net-part-2.aspx


    (3) バインドされたデータは DataRowView から取得する。

    ListView 内に配置された Labal, TextBox などの Text プロパティからバインドされたデータを取得しているようですが、ItemDataBound イベントのハンドラの引数の ListViewItemEventArgs オブジェクトから DataRowView を取得するほうが簡単です。以下の記事のサンプルコードを見てください。

    ListViewItemEventArgs クラス
    https://msdn.microsoft.com/ja-jp/library/system.web.ui.webcontrols.listviewitemeventargs(v=vs.110).aspx

    パフォーマンス上は大差ないかもしれませんが、コードが簡単になって、少なくとも開発工数の削減や保守性の向上には効果があると思います。


    (4) ObjectDataSource とクラスファイルを使う。

    SqlDataSource と ItemDataBound イベントのハンドラを使うというのはやめて、ObjectDataSource と単一のクラスファイルを使い、クラスファイルの中で DB からデータを取得し DataSet を作るようにすればかなり自由度が上がるはずです。

    ObjectDataSource Web サーバー コントロールの概要
    https://msdn.microsoft.com/ja-jp/library/9a4kyhcx(v=vs.100).aspx

    何らかの理由でテーブルを結合した単一の SELECT クエリの実装がうまく行かず、分けざるを得なくて、イベント発生のたびの Open / Close がホントにパフォーマンスに大きな影響を与えているなら、ObjectDataSource とクラスファイルの使用を検討されたほうがいいと思います。


    (5) 非同期プログラミング

    先のレスで紹介した記事で「6. バースト的に同時要求が増えることがある場合は、アプリケーションを非同期にする。」とありますが、そこで紹介した MSDN の記事「ASP.NET の非同期プログラミングを使ったスケール変換可能なアプリケーション」がリンク切れになってしまいました。代わりの記事を紹介しておきます。

    ASP.NET の非同期/待機の概要
    https://msdn.microsoft.com/ja-jp/magazine/dn802603.aspx

    その記事にも書いてありますが、Windows アプリの非同期プログラミングとは目的が違いますので注意してください。


    • 編集済み SurferOnWww 2015年9月26日 1:31 誤字訂正
    • 回答としてマーク hys73 2015年9月28日 18:31
    2015年9月26日 1:25

すべての返信

  • 最初から単価テーブルと注文テーブルを結合して取得すれば良いように思えますが、もし、そうできない理由があるのでしたら教えて下さい。

    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    • 回答としてマーク hys73 2015年9月25日 16:35
    2015年9月25日 6:25
    モデレータ
  • > 利用者数が多くなるとアプリケーションプールが増加し、
    > 高負荷状態が続きサーバがダウンするといった現象が発生しております。

    「サーバがダウン」というのは具体的にどういう状況なのでしょうか?

    スレッドプールのスレッドが枯渇して、要求がキューにたまって、その結果 HTTP エラー 503 (サービス利用不可) になるということなのでしょうか?

    その場合、以下の記事に書いてあるような対策は既に取られているのでしょうか?

    ASP.NET 最大要求数
    http://surferonwww.info/BlogEngine/post/2015/06/26/aspnet-request-queue-limit.aspx

    • 回答としてマーク hys73 2015年9月25日 16:35
    2015年9月25日 8:20
  • おっしゃる通り、結合した結果で取得すれば

    ItemDataBound内での検索はなくなりそうです。

    回答ありがとうございます。

    2015年9月25日 16:31
  • おっしゃる通り、 HTTP エラー 503 が発生いたします

    教えて頂いた対策は知識としてありませんでしたので、

    実施して様子を見てみます。

    回答ありがとうございました。

    2015年9月25日 16:35
  • 既に解決済みとなっているので今さらながらのレスかもしれませんが、今後のために気がついたことを書いておきます。

    (1) パラメータ化クエリを使う。

    ひょっとしたら、質問者さんのコードで一番のボトルネックは ItemDataBound イベントのハンドラの中でパラメータ化クエリを使ってないことかもしれません。

    パラメータ化クエリ
    http://surferonwww.info/BlogEngine/post/2012/02/02/Parameterized-query.aspx

    パラメータ化クエリを使うのは SQL インジェクション攻撃防止のためだけではなく、もう一つの重要なメリットにパフォーマンスの向上があります。


    (2) コネクションリークを防止する。

    MySQL と Connector/NET でもコネクションプールを利用していると思いますが、コネクションリークを防止するために try-finally 処理もしくは using 句を使いましょう。

    .NETの例外処理 Part.2
    http://blogs.msdn.com/b/nakama/archive/2009/01/02/net-part-2.aspx


    (3) バインドされたデータは DataRowView から取得する。

    ListView 内に配置された Labal, TextBox などの Text プロパティからバインドされたデータを取得しているようですが、ItemDataBound イベントのハンドラの引数の ListViewItemEventArgs オブジェクトから DataRowView を取得するほうが簡単です。以下の記事のサンプルコードを見てください。

    ListViewItemEventArgs クラス
    https://msdn.microsoft.com/ja-jp/library/system.web.ui.webcontrols.listviewitemeventargs(v=vs.110).aspx

    パフォーマンス上は大差ないかもしれませんが、コードが簡単になって、少なくとも開発工数の削減や保守性の向上には効果があると思います。


    (4) ObjectDataSource とクラスファイルを使う。

    SqlDataSource と ItemDataBound イベントのハンドラを使うというのはやめて、ObjectDataSource と単一のクラスファイルを使い、クラスファイルの中で DB からデータを取得し DataSet を作るようにすればかなり自由度が上がるはずです。

    ObjectDataSource Web サーバー コントロールの概要
    https://msdn.microsoft.com/ja-jp/library/9a4kyhcx(v=vs.100).aspx

    何らかの理由でテーブルを結合した単一の SELECT クエリの実装がうまく行かず、分けざるを得なくて、イベント発生のたびの Open / Close がホントにパフォーマンスに大きな影響を与えているなら、ObjectDataSource とクラスファイルの使用を検討されたほうがいいと思います。


    (5) 非同期プログラミング

    先のレスで紹介した記事で「6. バースト的に同時要求が増えることがある場合は、アプリケーションを非同期にする。」とありますが、そこで紹介した MSDN の記事「ASP.NET の非同期プログラミングを使ったスケール変換可能なアプリケーション」がリンク切れになってしまいました。代わりの記事を紹介しておきます。

    ASP.NET の非同期/待機の概要
    https://msdn.microsoft.com/ja-jp/magazine/dn802603.aspx

    その記事にも書いてありますが、Windows アプリの非同期プログラミングとは目的が違いますので注意してください。


    • 編集済み SurferOnWww 2015年9月26日 1:31 誤字訂正
    • 回答としてマーク hys73 2015年9月28日 18:31
    2015年9月26日 1:25
  • 有益な情報ありがとうございます!

    特に(1)、(2)については知識としてありませんでしたので、

    今後の開発にとても役に立つと思います。

    今回の不具合については、今のところ取得時のSQLを工夫することで逃げておりますが、

    おっしゃるように(4)を検討してみたいと思います。

    ありがとうございました。

    2015年9月28日 18:37