asp.Net下のデータベース操作最適化の一例

4215 ワード

次に、LargerResultProcessorは汎用パラメータTが指すデータベース・テーブルを巡回し、ページごとに100項目ずつページを分割し、各項目に対してProcessItem関数を呼び出し、サブクラスはProcessItem関数を実現するだけでよいベース・クラスです.
 
  
public class ItemRenameCompanyId : LargerResultProcessor
{
protected override void ProcessItem(Item item)
{
const string template1 = @"select top 1 shop_id from orders where Item_id = '{0}'";
var sql1 = string.Format(template1, item.Id);
const string template2 = @"update Items set shop_id={0} where id = {1};
update skus set shop_id={0} where item_id = {1};";
try
{
var obj = DbEntry.Context.ExecuteScalar(sql1);
var sql2 = string.Format(template2, long.Parse(obj.ToString()), item.Id);
DbEntry.Context.ExecuteNonQuery(sql2);
}
catch (Exception exception)
{
Logger.Default.Warn(exception + item.Id.ToString());
}
}
}

上のこのコード、ロジックは比較的に簡単で、すべての項目に対して、Select文を使ってShop_を取り出しますId、そしてUpdateを実行するには、実行速度が遅いという問題があります.6万程度のItem、4万程度のSku、99万程度のOrderのテーブルについては、変換が完了するまで約40分かかります.
これらのコードは、使い捨ての操作ですが、システムを実行するには、ダウンタイムが短いほど良いので、いくつかの最適化作業を行い、データベースは大量の繰り返し文に対して、パラメータを使用すると、文の繰り返し解析作業を避けることができるので、速度が速くなります.この考え方に従って、簡単な修正は以下の通りです.
 
  
public class ItemRenameCompanyId : LargerResultProcessor
{
protected override void ProcessItem(Item item)
{
const string template1 = @"select top 1 shop_id from orders where Item_id = @id";
const string template2 =
@"update Items set shop_id=@sid where id = @id;
update skus set shop_id=@sid where item_id = @id;";
try
{
var sql1 = new SqlStatement(template1, new DataParameter("@id", item.Id));
var sid = Convert.ToInt64(DbEntry.Context.ExecuteScalar(sql1));
var sql2 = new SqlStatement(template2, new DataParameter("@sid", sid), new DataParameter("@id", item.Id));
DbEntry.Context.ExecuteNonQuery(sql2);
}
catch (Exception exception)
{
Logger.Default.Warn(exception + item.Id.ToString());
}
}
}

このプログラムをテストして、約25分で変換を完了することができます.いくつかの向上がありますが、私たちが本当に修正しなければならないデータの量は大きくなく、全部で6万と4万、約10万件のデータしかありませんので、25分は少し長くなりました.簡単な分析の後で、Ordersは最大の表で、もし全体の速度が遅いならば、速度が遅い最大の可能性の要素を招いて、Ordersを照会するべきで、だから少し考えを変えて、早めにItem_をIdとShop_Idの対応関係が検出され、メモリに格納され、プロセスItemごとにOrdersテーブルのクエリーが行われないようにします.メモリの中のデータについては、Dictionaryを使うつもりだったが、後で考えてみると、Idはlong型のデータであり、「疎」行列ではなく、基本的に「稠密」行列と呼ぶことができるので、直接配列を使うのはもっと速いはずだ.
 
  
public class ItemRenameCompanyId : LargerResultProcessor
{
private readonly long[] _dic;
public ItemRenameCompanyId()
{
var count = Convert.ToInt64(DbEntry.Context.ExecuteScalar("select top 1 Id from items order by id desc")) + 10;
_dic = new long[count];
var sql =
new SqlStatement(
"select items.id as xiid,orders.shop_id as xsid from items inner join orders on orders.item_id = items.id group by items.id,orders.shop_id")
{SqlTimeOut = 300};
dynamic list = DbEntry.Context.ExecuteDynamicList(sql);
foreach(dynamic row in list)
{
_dic[row.xiid] = row.xsid;
}
}
protected override void ProcessItem(Item item)
{
const string template2 =
@"update Items set shop_id=@sid where id = @id;
update skus set shop_id=@sid where item_id = @id;";
try
{
var sid = _dic[item.Id];
var sql2 = new SqlStatement(template2, new DataParameter("@sid", sid), new DataParameter("@id", item.Id));
DbEntry.Context.ExecuteNonQuery(sql2);
}
catch (Exception exception)
{
Logger.Default.Warn(exception + item.Id.ToString());
}
}
}

更にこの段のプログラムをテストして、70秒を実行してデータの変換を完成して、また、対応関係のあの1つのSQLを検索して、回復したばかりのデータベースのため、大体3、40秒を使って、実際にクエリーマネージャを使って、実行中のデータベースでそのSQLを実行して、1秒ぐらいで完成することができて、だから、実際に変換する時、3、40秒で変換を完成することができると推定します.