- PR -

ファイルの排他制御

投稿者投稿内容
AKARI0418
会議室デビュー日: 2009/01/30
投稿数: 8
投稿日時: 2009-02-03 09:13
私の説明の不足にや、理解不足により皆様を混乱させてしまい大変申し訳ありません。
ASP.NETでのサーバー動作として、更新系のストアドを実行した際に、
クライアント情報や、SQL、実行時間をログとして、保存したいと考えています。
System.Web.HttpContext.Current.Application.Lock()
を使用することもできますが、
あらかじめサーバー上にログファイルを操作するアプリを準備し、ファイルにログを出力しようと考えました。
クライアントへのレスポンスの向上になると考えたからです。
またログファイルに、実行時間を持たせるため、ログファイルのレコード順は入れ替わっても良いと考えミューテックスを利用して排他をかけて、Webシステムと非同期で更新されていけばよいと考えました。

現在はできるかどうか、また実行速度や負荷を検証するために、WindowsApllicationを使用して、部分的ではありますが、検証を進めています。

あしゅ様解答ありがとうございます。
>サーバー/クライアントがどのような構成なのか不明ですし、
DBを更新するのがどちらなのかも文脈的に読み取れないので
的確なアドバイスは誰にもできないと思いますが、

大変失礼いたしました、ASP.NETで実装できればと考えています。

>サーバー上でのプロセス起動(「アプリの作成」から推測)にどんな方法を
取ったとしてもオーバーヘッドで逆に遅くなりそうな気がします。

VB.NETで実行してみているのですが、指摘の通り遅いです。
そもそもプロセス起動自体が遅いようです、
System.Web.HttpContext.Current.Application.Lock()
を使用したほうが現実的なのでしょうか?


Jitta様解答ありがとうございます。

>「メモ帳などのアプリケーションから書き込むのはよいのか?」について、「意識しておりませんでした。」というお答えですが、だからどうしたいのか?については、お答えいただいていませんよ?

失礼いたしました。
参照はできても他のプロセス、または手動での更新はできないようにしたいです。
また参照することによる、ログ書き出しへの影響も排除したいです。


>2台のクライアントが同時に書き込みを行おうとしたら、サーバーは、どうするのですか?書き込まれるのは1つのファイルですよね?そのファイルへの書き込む動作は、どうしたいのですか?1台のクライアントからの書き込みのみ直列化して、複数台のクライアントからの書き込みは並列化するのですか?

ログはログの項目として持たせる実行時間を利用するため、ログの書き込み順序は可能な方法であれば、何でもかまいません。
todo
ぬし
会議室デビュー日: 2003/07/23
投稿数: 682
投稿日時: 2009-02-03 12:32
引用:

ASP.NETでのサーバー動作として、更新系のストアドを実行した際に、
クライアント情報や、SQL、実行時間をログとして、保存したいと考えています。



ログをメモリ上に貯めて、定期的にファイルに落とすとか。

コード:
public static class LogWriter
{
	private class Log
	{
		public DateTime time;
		public string text;

		public Log(DateTime time, string text)
		{
			this.time = time;
			this.text = text;
		}
	}

	private static Queue<Log> queue = new Queue<Log>();

	public static void Enqueue(DateTime time, string text)
	{
		lock (queue) {
			queue.Enqueue(new log(time, text));
		}
	}

	private static Log Dequeue()
	{
		lock (queue) {
			if (queue.Count > 0)
				return queue.Dequeue();
			else
				return null;
		}
	}

	public static void WriteLog(string file)
	{
		Log log = Dequeue();
		if (log != null) {
			using (StreamWriter sw = new StreamWriter(file, true)) {
				do {
					sw.WriteLine(log.time + "," + log.text);
				} while ((log = Dequeue()) != null);
			}
		}
	}
}



Application_Startでタイマを立てて、定期的にWriteLogを呼ぶ
Application_EndでWriteLogを呼ぶ
武史
ベテラン
会議室デビュー日: 2007/09/21
投稿数: 71
投稿日時: 2009-02-03 13:03
ログを書き込むのに exe を別に起動するのは、あんまり勧められません。

なんか、ややこしい話になっていますが、
とりあえず、Mutex を名前付きで初期化して、そのまま ASP .Net の
サブルーチンにして、そのまま呼び出すのが、最初の一歩では
ないでしょうか。


その上で、、、

ポイント1 メモ帳とかで開いている時にどうするか?
エラーでよければ、例外を無視するようなロジックが必要でしょう。
書き込みたければ、リトライのロジックが必要でしょう。


ポイント2 同期処理か、非同期処理か。
パフォーマンスを要求するのであれば、非同期処理の方がいいでしょう。
todo さんのようなロジックも一つかと思いますが、
Generics の Queue って、マルチスレッドで大丈夫でしたっけ?

同期処理でよければ、そんなに深く考える必要はないかと思いますが、
リトライ処理の仕方によっては、メモ帳で開いている間は、
サーバーが固まるというような状況になりかねませんので、
注意が必要でしょう。


# ASP .Net を使わないんで詳しく知らないんですが、
# マルチプロセス?それとも、マルチスレッド?
# OS によって違ったりしましたっけ?
campylo
会議室デビュー日: 2008/12/15
投稿数: 5
投稿日時: 2009-02-03 18:27
引用:

ASP.NETでのサーバー動作として、更新系のストアドを実行した際に、


ASP.NETとRDBのシステムと思ってよいでしょうか。

引用:

クライアント、サーバー型のシステムにおいて、ログの出力を行うことを目的としています。
ログはDBの更新時のもので、ロールフォワードおよび、更新者の特定をするために使用します。

ログの保存量が多いため、操業データと同じDBに組み入れると、DBの負担が大きくなり、クライアントへのレスポンスの低下が生じると考えました。


チューニングは最後の手段です。
ふつうは有償のデータベースシステムは非常に高速ですし大容量にも耐えられるようになっています。
作られているシステムでは将来ログ出力がボトルネックになることが前提のお考えのようですが、そのような将来のことは将来に行うこととして、まずは機能を実装して速度が要件にあうか評価すべきです。

データベースシステムへの書き込みが遅い処理を自前の実装で書き直すというのは最後の手段でしょう。
データベースエンジンを超える速度を出すのはかなり苦労するのではないかと思います。
データベースエンジンの内部は非常に高度なチューニングがされておりWebシステム構築とは違う特殊な技術だと思ったほうがよいでしょう。

引用:

ASP.NETでのサーバー動作として、更新系のストアドを実行した際に、
クライアント情報や、SQL、実行時間をログとして、保存したいと考えています。


ここでおっしゃっているログの目的を整理したほうがよいのではないのでしょうか。

証跡としたいのでしょうか。
それとも、データベースの修復を目的としたデータベース操作の痕跡を残したいのでしょうか。

もしも証跡としたいのでしたら何がしかのトラブルが発生したときにでも可能な限り残っているように設計しなければなりません。
イベントログ、データベース、キュー、ファイル、メモリーなどいろんな方法で送信・保存できますので要件や環境によって使い分けることになります。

データベースの修復の目的でしたらRDBにその機能がありますのでそちらを活用すべきだと思います。

どちらも「ログ」という言葉が使われていますが、これらは別のものと思ったほうがよいでしょう。

引用:

System.Web.HttpContext.Current.Application.Lock()
を使用することもできますが、


これを不用意につかうとせっかくの並列で動作できるWebシステムがアプリケーションスコープを意識しなければならないものになるのではないかと思います。
使われるのでしたら挙動をよく把握してから作られることをお勧めします。

引用:

あらかじめサーバー上にログファイルを操作するアプリを準備し、ファイルにログを出力しようと考えました。

クライアントへのレスポンスの向上になると考えたからです。


このサーバーとは、Webシステムが乗っているサーバーですか。
つまりIIS上のWebシステムからログ書き出しをするたびにexeを起動するというイメージでしょうか。

証跡のためのログを書くためにWebシステムから外部のexeを実行しプロセスの起動をするというのはレスポンスを低下させますので期待通りにはならないと思います。

引用:

そもそもプロセス起動自体が遅いようです、


プロセスの起動は遅いものです。
高速に動作させたい場合は起動済みにしておきます。
サービスにすればありえなくもない実装かと思います。


スレッドのタイトルであるファイルの排他制御とは少し離れてきましたね。
これはだめとかあれはだめとかいう回答になってあまりすっきりしなくて申し訳ありません。

どのような目的があるのかもう少し整理していただいたほうがよいかもしれません。

_________________
campylo
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2009-02-04 09:41
中央にRDBMSを使っているクラサバ環境を前提として、

AKARI0418さんが実装したいログが、
1. データベース操作の痕跡
2. データベースの修復
の両方を目的としている場合を考えてみます。

データベース操作の痕跡は、まともなRDBMSならば、
ジャーナルログを走査して実行されたSQLを調査する機能を持ちます。
#ログを要件に応じた期間保持するアーカイブログの設定も必要となります。

データベースの修復の場合も、まともなRDBMSならば
コミットが確定したトランザクションの修復を行う機能は持ちます。

なので、コミットが未確定なトランザクションの修復(再試行)を考えると
コミットが完了していないトランザクションのSQLをログに記録しても、
障害回復後に再実行しても途中までなのであまり意味はないでしょう。

#最後のCOMMITだけ失敗したトランザクションなら救えるかもしれません

冗長構成を取っている場合はクラスタのミドルウェアやRDBMS自体の機能で
未コミットなトランザクションの引き継ぎを実現しているものがあります。
そういった製品を検討されてはいかがでしょうか?
AKARI0418
会議室デビュー日: 2009/01/30
投稿数: 8
投稿日時: 2009-02-04 11:37
todo様、武史様、campylo様、あしゅ様
貴重なご意見をありがとうございます。

DBエンジンにはOracle 10gを使用いたします。
冗長構成はとっておりません。

トレースを利用することもできたのですが、
ユーザー側にOracleを操作できる管理者がいないため、バックアップやDBへの更新不良の管理、ロールバック、ロールフォワードの簡単なインターフェースが求められたため、GUIで機能を提供するということになりました。
Oracleの機能を処理に組み込んで、実現できるかがわからず、独自のログを利用しようと考えました。

独自のログでなくてもできるのでしょうか?

話が変わりますが、ログの意味合いです、
データベース操作の痕跡はあしゅ様のご指摘のとおり、
RDBMSの機能を利用し、実装したいと考えています。

データベースの修復ですが、
今回のシステムは障害時の復旧時間よりも、
データの保守性を重要視して考えています。

GUIで機能を提供するという上記の理由+バックアップの冗長化につながればという思いがあり、DB機能に依存しないログの出力を目的としていました。
先の書き込みで、ログの保存量が多いため、操業データと同じDBに組み入れたくないということもあり、
ファイルへの出力を目指したという経緯です。

できないことを最もな書き方で、正当化しているような気もします。


>作られているシステムでは将来ログ出力がボトルネックになることが前提のお考えのようですが、そのような将来のことは将来に行うこととして、まずは機能を実装して速度が要件にあうか評価すべきです。
campylo様に指摘していただき、
考えを改める必要も感じました。

ご指摘していただいたころを参考に、以下のように考えました、
バックアップ
1.DBの修復を目的としたログをDBに保存する。
2.定周期でログDBからファイルへ出力するバッチ処理を行う。
3.2よりも長い周期で、DBのスナップショットをシャドーコピーでとる。
4.サーバークラッシュに備え、2および3を外部にバックアップする。

リストア
1.オラクルによるリストアを行う
2.1が行えない場合、スナップショットを復元し、ログをもとにリカバリを行う

批判をいただけるとありがたいです、よろしくお願いいたします。

todo様のおっしゃられる方法も魅力的でしたが、上記のようにさせていただきました。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2009-02-04 21:41
ちょっとごめん。整理。

ASP.NET + Oracle10g。
複数のクライアントから、同時にアクセスがあり得る。
DBMS での耐久構成はとっていない。
障害発生時、基本的にエンド ユーザーのみで対応する。
→エンド ユーザーが対応可能な、ロールフォワードの仕組みを作りたい。
→行ったことをログに落とし、そのログを元にもう一度処理を行えば良いのでは?
→では、どうやってログをとったらいい?



 Oracle のログ機能に任せ、そのログから復元するために、一連のコマンドを発行するアプリケーションを作ればいいのでは?
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2009-02-05 09:28
引用:

Jittaさんの書き込み (2009-02-04 21:41) より:
 Oracle のログ機能に任せ、そのログから復元するために、一連のコマンドを発行するアプリケーションを作ればいいのでは?


私もそう思います。
というか、基本的に一連のコマンドすら発行する必要がなく、
ロール{バック,フォワード}だけで解決する障害ならば再起動するだけです。

クラッシュリカバリ以上のリカバリが必要な場合だと
データファイルの破損やREDOログの喪失、物理的なディスク破損など
DBMSの知識がないエンドユーザーによる自動的な復旧は難しいです。

#RDBMS本体の不具合でデータファイルが破損する可能性もあるので
#そんな状況からの復旧も考えるのであればよいかもしれませんが、
#より信用ならないコードに無駄なお金がかかるだけかと。

バックアップを複数の方法で取っておくのは有効だと思います。
人為的なミス(間違って削除してしまうなど)に対しても強くなりますし。

それでも通常の{オンライン,オフライン}バックアップと
RMAN(Recovery Manager)を組み合わせたものの方がよいでしょう。

スキルアップ/キャリアアップ(JOB@IT)