トップ回答者
SqlDataSourceのItemDataBound内でさらにSQL発行

質問
-
いつもお世話になっております。
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();
}
回答
-
> 利用者数が多くなるとアプリケーションプールが増加し、
> 高負荷状態が続きサーバがダウンするといった現象が発生しております。「サーバがダウン」というのは具体的にどういう状況なのでしょうか?
スレッドプールのスレッドが枯渇して、要求がキューにたまって、その結果 HTTP エラー 503 (サービス利用不可) になるということなのでしょうか?
その場合、以下の記事に書いてあるような対策は既に取られているのでしょうか?
ASP.NET 最大要求数
http://surferonwww.info/BlogEngine/post/2015/06/26/aspnet-request-queue-limit.aspx- 回答としてマーク hys73 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
すべての返信
-
> 利用者数が多くなるとアプリケーションプールが増加し、
> 高負荷状態が続きサーバがダウンするといった現象が発生しております。「サーバがダウン」というのは具体的にどういう状況なのでしょうか?
スレッドプールのスレッドが枯渇して、要求がキューにたまって、その結果 HTTP エラー 503 (サービス利用不可) になるということなのでしょうか?
その場合、以下の記事に書いてあるような対策は既に取られているのでしょうか?
ASP.NET 最大要求数
http://surferonwww.info/BlogEngine/post/2015/06/26/aspnet-request-queue-limit.aspx- 回答としてマーク hys73 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