Skip to content

Instantly share code, notes, and snippets.

@daisukenishino2
Last active August 9, 2018 10:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daisukenishino2/6ab8fd7a9f6eb0c32782c73dc95c3f27 to your computer and use it in GitHub Desktop.
Save daisukenishino2/6ab8fd7a9f6eb0c32782c73dc95c3f27 to your computer and use it in GitHub Desktop.
Open棟梁:バッチ・サンプルの性能測定(2)に使用した、各種リソース情報と、その測定結果。
//**********************************************************************************
//* フレームワーク・テストクラス(B層)
//**********************************************************************************
// テスト用サンプルなので、必要に応じて流用 or 削除して下さい。
//**********************************************************************************
//* クラス名 :LayerB
//* クラス日本語名 :B層のテスト
//*
//* 作成日時 :-
//* 作成者 :生技セ
//* 更新履歴 :
//*
//* 日時 更新者 内容
//* ---------- ---------------- -------------------------------------------------
//* 20xx/xx/xx XX XX XXXX
//**********************************************************************************
using RerunnableBatch_sample3.Common;
using System;
using System.Data;
using System.Text;
using System.Collections;
using Touryo.Infrastructure.Business.Business;
using Touryo.Infrastructure.Business.Dao;
using Touryo.Infrastructure.Public.Db;
namespace RerunnableBatch_sample3.Business
{
/// <summary>
/// LayerB の概要の説明です
/// </summary>
public class LayerB : MyFcBaseLogic
{
public const int INTERMEDIATE_COMMIT_COUNT = 2000;
/// <summary>バッチ処理を実行する</summary>
/// <param name="parameter">引数クラス</param>
private void UOC_ExecuteBatchProcess(ExecuteBatchProcessParameterValue parameter)
{
// 戻り値クラスを生成して、事前に戻り値に設定しておく。
this.ReturnValue = new VoidReturnValue();
// ↓業務処理-----------------------------------------------------
DataTable pkTable = new DataTable();
// ↓DBアクセス-----------------------------------------------------
// 共通Daoを生成
CmnDao cmnDao = new CmnDao(this.GetDam());
// 動的SQLを指定
cmnDao.SQLFileName = "SelectAllOrderID.xml";
// 共通Daoを実行
cmnDao.ExecSelectFill_DT(pkTable);
// ↑DBアクセス-----------------------------------------------------
// 戻り値を設定
ArrayList pkList = new ArrayList();
for (int index = 0; index < pkTable.Rows.Count; index++)
{
//データテーブルからArrayListに詰め直す
pkList.Add(pkTable.Rows[index]["OrderID"]);
}
// ↑業務処理-----------------------------------------------------
int recordCount = pkList.Count; // 全レコード数
int initialIndex = 0; // 処理開始インデックス ※ todo:リラン時に途中から再開する場合は初期値を変更する
int transactionCount = Convert.ToInt32(Math.Ceiling(((double)(recordCount - initialIndex)) / INTERMEDIATE_COMMIT_COUNT)); // 更新B層実行回数
int i = 0;
for (int transactionIndex = 0; transactionIndex < transactionCount; transactionIndex++)
{
ArrayList subPkList; // 主キー一覧(1トランザクション分)
int subPkStartIndex; // 主キー(1トランザクション分)の開始位置
int subPkCount; // 主キー数(1トランザクション分)
// 取り出す主キーの開始、数を取得
subPkStartIndex = initialIndex + (transactionIndex * INTERMEDIATE_COMMIT_COUNT);
if (subPkStartIndex + INTERMEDIATE_COMMIT_COUNT - 1 > recordCount - 1)
{
subPkCount = (recordCount - initialIndex) % INTERMEDIATE_COMMIT_COUNT;
}
else
{
subPkCount = INTERMEDIATE_COMMIT_COUNT;
}
// 主キー一覧(1トランザクション分)を取り出す
subPkList = new ArrayList();
subPkList.AddRange(pkList.GetRange(subPkStartIndex, subPkCount));
// ↓業務処理-----------------------------------------------------
DataTable dataTable = new DataTable(); //データ一覧(主キーを元に検索したデータ)
//Ordersテーブルからデータを検索する
// ↓DBアクセス-----------------------------------------------------
// 共通Daoを生成
cmnDao = new CmnDao(this.GetDam());
// 動的SQLを指定
cmnDao.SQLFileName = "SelectInOrderID.xml";
// パラメータを設定
cmnDao.SetParameter("OrderID", subPkList);
// 共通Daoを実行
//string sql =
cmnDao.ExecSelectFill_DT(dataTable);
//ExecGenerateSQL(new SQLUtility(DbEnum.DBMSType.SQLServer));
//cmnDao = new CmnDao(this.GetDam());
//cmnDao.SQLText = sql;
//cmnDao.ExecSelectFill_DT(dataTable);
// ↑DBアクセス-----------------------------------------------------
//Orders2テーブルに複数件まとめて追加する。
string insertHeader = "INSERT INTO [Orders2] {0} VALUES {1}";
string columnList = "";
SQLUtility sQLUtility = new SQLUtility(DbEnum.DBMSType.SQLServer);
string[] insertSQLParts = sQLUtility.GetInsertSQLParts(dataTable);
StringBuilder sb = new StringBuilder();
foreach (string insertSQLPart in insertSQLParts)
{
if (string.IsNullOrEmpty(columnList))
{
// columnList
columnList = insertSQLPart;
}
else
{
// insertSQLPart
sb.Append(string.Format(insertHeader, columnList, insertSQLPart) + Environment.NewLine);
}
}
// 共通Daoでバッチ更新
CmnDao cd = new CmnDao(this.GetDam());
cd.SQLText = sb.ToString();
cd.ExecInsUpDel_NonQuery();
i += INTERMEDIATE_COMMIT_COUNT;
Console.WriteLine("1 interval was executed. " + i.ToString());
// ↑業務処理-----------------------------------------------------
}
}
}
}
//**********************************************************************************
//* フレームワーク・テストクラス(B層)
//**********************************************************************************
// テスト用サンプルなので、必要に応じて流用 or 削除して下さい。
//**********************************************************************************
//* クラス名 :LayerB
//* クラス日本語名 :B層のテスト
//*
//* 作成日時 :-
//* 作成者 :生技セ
//* 更新履歴 :
//*
//* 日時 更新者 内容
//* ---------- ---------------- -------------------------------------------------
//* 20xx/xx/xx XX XX XXXX
//**********************************************************************************
using RerunnableBatch_sample3.Common;
using System;
using System.Data;
using System.Text;
using System.Collections;
using Touryo.Infrastructure.Business.Business;
using Touryo.Infrastructure.Business.Dao;
using Touryo.Infrastructure.Public.Db;
namespace RerunnableBatch_sample3.Business
{
/// <summary>
/// LayerB の概要の説明です
/// </summary>
public class LayerB : MyFcBaseLogic
{
public const int INTERMEDIATE_COMMIT_COUNT = 2000;
/// <summary>バッチ処理を実行する</summary>
/// <param name="parameter">引数クラス</param>
private void UOC_ExecuteBatchProcess(ExecuteBatchProcessParameterValue parameter)
{
// 戻り値クラスを生成して、事前に戻り値に設定しておく。
this.ReturnValue = new VoidReturnValue();
// ↓業務処理-----------------------------------------------------
DataTable dataTable = new DataTable();
// ↓DBアクセス-----------------------------------------------------
// 共通Daoを生成
CmnDao cmnDao = new CmnDao(this.GetDam());
// 動的SQLを指定
cmnDao.SQLFileName = "SelectAllOrderID.xml";
// 共通Daoを実行
cmnDao.ExecSelectFill_DT(dataTable);
// ↑DBアクセス-----------------------------------------------------
// ↑業務処理-----------------------------------------------------
// ↓業務処理-----------------------------------------------------
//Orders2テーブルに複数件まとめて追加する。
string insertHeader = "INSERT INTO [Orders2] {0} VALUES ";
SQLUtility sQLUtility = new SQLUtility(DbEnum.DBMSType.SQLServer);
string[] insertSQLParts = sQLUtility.GetInsertSQLParts(dataTable);
dataTable = null; // GCへ。
int length = insertSQLParts.Length;
StringBuilder sb = new StringBuilder();
// ColumnList
sb.Append(string.Format(insertHeader, insertSQLParts[0]));
for (int i = 1; i < length; i++)
{
// InsertSQLPart
sb.Append(Environment.NewLine + insertSQLParts[i] + ", ");
//if (sb.Length > 1000) break;
}
sb.Remove(sb.Length - 3, 2); // ", " を消す。
sb.Append(";"); // ";" を足す。
//Console.WriteLine(sb.ToString());
//Console.ReadKey();
// 共通Daoでバッチ更新
CmnDao cd = new CmnDao(this.GetDam());
cd.SQLText = sb.ToString();
cd.ExecInsUpDel_NonQuery();
// ↑業務処理-----------------------------------------------------
}
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<ROOT>
SELECT
*
FROM
Orders
ORDER BY
OrderID
</ROOT>
//**********************************************************************************
//* リラン可能バッチ処理・サンプル アプリ
//**********************************************************************************
// テスト用サンプルなので、必要に応じて流用 or 削除して下さい。
//**********************************************************************************
//* クラス名 :Program
//* クラス日本語名 :サンプル バッチ
//*
//* 作成日時 :-
//* 作成者 :生技
//* 更新履歴 :
//*
//* 日時 更新者 内容
//* ---------- ---------------- -------------------------------------------------
//* 20xx/xx/xx XX XX XXXX
//**********************************************************************************
using RerunnableBatch_sample3.Business;
using RerunnableBatch_sample3.Common;
using System;
using System.Collections;
using System.Collections.Generic;
using Touryo.Infrastructure.Business.Util;
using Touryo.Infrastructure.Public.Db;
using Touryo.Infrastructure.Public.Util;
namespace RerunnableBatch_sample3
{
/// <summary>Program</summary>
class Program
{
/// <summary>Main</summary>
static void Main(string[] args)
{
// コマンドラインをバラす関数がある。
List<string> valsLst = null;
Dictionary<string, string> argsDic = null;
PubCmnFunction.GetCommandArgs('/', out argsDic, out valsLst);
// 引数クラス値(B層実行用)
string screenId = System.Reflection.Assembly.GetExecutingAssembly().Location;
string controlId = "-";
string actionType = "SQL"; // argsDic["/DAP"] + "%" + argsDic["/MODE1"] + "%" + argsDic["/MODE2"] + "%" + argsDic["/EXROLLBACK"];
MyUserInfo myUserInfo = new MyUserInfo("userName", "ipAddress");
// B層クラス
LayerB layerB = new LayerB();
// 引数クラスを生成
VoidParameterValue selectPkListParameterValue = new VoidParameterValue(screenId, controlId, "SelectPkList", actionType, myUserInfo);
//// B層呼出し
SelectPkListReturnValue selectPkReturnValue = null;
// 性能測定
// 性能測定 - 開始
PerformanceRecorder pr = new PerformanceRecorder();
pr.StartsPerformanceRecord();
// ↓B層実行:バッチ処理を実行(1トランザクション分)----------------------------------------------------
// 引数クラスを生成
ExecuteBatchProcessParameterValue executeBatchProcessParameterValue = new ExecuteBatchProcessParameterValue(screenId, controlId, "ExecuteBatchProcess", actionType, myUserInfo);
//executeBatchProcessParameterValue.SubPkList = subPkList;
// B層呼出し
VoidReturnValue executeBatchProcessReturnValue = (VoidReturnValue)layerB.DoBusinessLogic(executeBatchProcessParameterValue, DbEnum.IsolationLevelEnum.ReadCommitted);
// 実行結果確認
if (executeBatchProcessReturnValue.ErrorFlag == true)
{
// 結果(業務続行可能なエラー)
string error = "ErrorMessageID:" + selectPkReturnValue.ErrorMessageID + "\r\n";
error += "ErrorMessage:" + selectPkReturnValue.ErrorMessage + "\r\n";
error += "ErrorInfo:" + selectPkReturnValue.ErrorInfo + "\r\n";
Console.WriteLine(error);
Console.ReadKey();
return; // バッチ処理終了
}
// ↑B層実行:バッチ処理を実行(1トランザクション分)----------------------------------------------------
// 性能測定 - 終了
Console.WriteLine(pr.EndsPerformanceRecord());
Console.ReadKey();
}
}
}
@daisukenishino2
Copy link
Author

daisukenishino2 commented Aug 8, 2018

追加の性能測定1

目的

上記の

の測定モデルは、

の続きで、

「分割コミット」から「一括コミット」に変更することで、
コミットが性能にどのような影響を与えるを調べるためのものである。

結果

結果として、性能向上するケースと、性能劣化するケースを観測した。

( 1 ) ローカル・ループバック・アドレス & コミット・インターバル ≒ 100

193,083 ---> 174,496 (性能向上)

  • ExT:177619[msec], CT:66938[msec], KT:9984[msec], UT:56953[msec]
  • ExT:171372[msec], CT:65906[msec], KT:8641[msec], UT:57266[msec]

( 2 ) ローカル・ループバック・アドレス & コミット・インターバル ≒ 2,000

290,963 ---> 292,086 (大きな変化初めてなし)

  • ExT:293534[msec], CT:69094[msec], KT:7219[msec], UT:61875[msec]
  • ExT:290638[msec], CT:70141[msec], KT:7406[msec], UT:62734[msec]

( 3 ) ローカルIPアドレス & コミット・インターバル ≒ 100

1053,107 ---> 1235,646 (性能劣化)

  • ExT:1237385[msec], CT:85344[msec], KT:19125[msec], UT:66219[msec]
  • ExT:1233906[msec], CT:83375[msec], KT:18188[msec], UT:65188[msec]

( 4 ) ローカルIPアドレス & コミット・インターバル ≒ 2,000

781,757 ---> 858,291 (性能劣化)

  • ExT:844829[msec], CT:82641[msec], KT:14578[msec], UT:68063[msec]
  • ExT:871752[msec], CT:80594[msec], KT:12828[msec], UT:67766[msec]

原因

特筆すべき、ポイント

( 1 )

結果、( 1 ), ( 2 )の比較。

  • ローカル・ループバック・アドレスでコミット・インターバルを長くすると性能劣化が起きる可能性があるもよう。
  • コミット・インターバルを長くするのは、ラウンド・トリップのオーバーヘッド軽減のためにやっているが、
    ローカル・ループバック・アドレスでは、もともとオーバーヘッドが少なく、送信SQL文が長くなることで、
    ネットワーク上か、DBMS上で性能劣化が起きたものと考える。

( 2 )

結果、( 3 ), ( 4 )のケースの分析。

ロング・トランザクションで性能劣化が起きる可能性があるもよう。

@daisukenishino2
Copy link
Author

daisukenishino2 commented Aug 8, 2018

追加の性能測定2

目的

上記の

の測定モデルは、

の続きで、

「分割コミット」と「疑似フェッチ」を無くし、
これらが性能にどのような影響を与えるを調べるためのものである。

結果

( 1 )

結果として、以下の例外が発生。

System.Data.SqlClient.SqlException (0x80131904): サーバーに要求を送信しているときに、トランスポート レベルの
  エラーが発生しました。 (provider: Shared Memory Provider, error: 0 - パイプの他端にプロセスがありません。)
  ---> System.ComponentModel.Win32Exception (0x80004005): パイプの他端にプロセスがありません。

( 2 )

例外発生を抑止すべく、Insert文を短縮を試みる。

が、問題解決せず(カラム・リスト短縮のレベルでは解決せず)。

原因

  • 100万件のInsert文は、サイズが大き過ぎるため実行不可能。
  • DataTableの利用で、バッチEXEのメモリも5.0GBまで膨れたが、
    Insert文が巨大過ぎるという話だとPOCO化しても無意味。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment