【IRIS/Cache】ジャーナルファイルの参照

本記事は、コマンドによるジャーナルファイルの参照方法についての解説になります。

※この記事は下記の方向けになります。
  • 異なるディレクトリにバックアップしたジャーナルファイルを参照したい方
  • 管理ポータル以外で参照する必要がある方

はじめに

ジャーナル・ファイルは、データベースのトランザクション履歴を記録する重要なファイルです。
障害時の復旧や、データ整合性・トランザクションログの確認等々に役立ちます。

各ジャーナル・レコードの参照方法は、管理ポータルから参照する方法が一番楽だと思いますが、管理ポータルが使えない場合の代替手段も知っておくと便利です。

前々回の中級編では、「d ^JRNDUMP」コマンドでジャーナルファイルを参照しました。

ただし、この方法では、一覧から選択する以外に方法はなく、別ディレクトリ等にバックアップしたジャーナルファイルの参照は行えません。

そこで、異なるディレクトリにあるジャーナルファイルを含め、ファイルパスを指定してジャーナルファイルの参照を行いたいと思います。

では解説を始めます。

SELECT^JRNDUMP

「d ^JRNDUMP」コマンドと同じく、対話型で参照を行います。

コマンドは下記になります。

SELECT^JRNDUMP(%jfile, %pid, %dir, %glo, %gloall, %operation, %remsysid)

【引数の説明】

引数説明
$jfileジャーナルファイルのフルパス
%pidジャーナルファイル内のプロセスIDを指定する。任意
%dirジャーナルファイル内のディレクトリを指定する。任意
%gloジャーナルファイル内のグローバル名を指定する。任意
%gloall0 : %gloで指定した名前に完全一致するグローバル(default)
1 : %gloで指定した名前を使用しているグローバル
%operationジャーナル・レコードの処理タイプ。任意
%remsysidジャーナルファイル内のECPシステムIDを指定する。任意

【%operationの説明】

引数(数値)引数(文字列)Type説明
6“s”SetSET文を抽出する
14“bs”BitSetビットのSET文を抽出する
7“k”KillNodeKILL文を抽出する
8“k”KillDesc下位ノードのKILL文を抽出する
9“zk”ZKillZKILL文を抽出する
4BeginTransトランザクション開始
16BeginTrans with Levelトランザクション・レベル開始
5CommitTransトランザクションをコミット
18CommitTrans with Level分離されたトランザクション・レベルをコミット
17CommitTrans Pending with Level保留中のトランザクション・レベルをコミット
21Rollbackトランザクションのロールバック
13JrnMarkジャーナル・マーカ
15NetReqECPネットワーキング

リモートSET, KILL, ZKILLはクラスタ関連で出力するため、割愛します。
ミラーSET, KILLも内部仕様のため割愛します。

動作の確認

各引数に対し、動作の検証を行っていきます。

準備

2つのターミナルからグローバルの操作を実行し、ジャーナルファイルを別ディレクトリに移します。

ターミナル(3704)から実行する

s ^test = "テスト"
s ^test(1) = "テスト(1)"
s ^test(2) = "テスト(2)"
k ^test(2)
s ^test(3) = "テスト(3)"
s ^test(3,1) = "テスト(3,1)"
zk ^test(3)

別ターミナル(1840)にて実行する

s ^sample = "サンプル"
s ^sample(1) = "サンプル(1)"
s ^sample(2) = "サンプル(2)"
k ^sample(2)
s ^sample(3) = "サンプル(3)"
s ^sample(3,1) = "サンプル(3,1)"
zk ^sample(3)

プロセス指定でのジャーナルレコード出力検証

プロセス毎のジャーナルを出力するには、下記コマンドになります。

zn "%SYS"
s path = [ジャーナルファイルのフルパス]
d SELECT^JRNDUMP(path, 3704)

ターミナルにて実行

出力された内容を下記に張り付けます。

Address:                 135744
Type:                    Set
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76799 - 03/12/2025 21:19:59
Collation sequence:      5
Prev address:            135728
Next address:            135792

Global:    ^["^^d:\irisdb\sample-data\"]test
New Value: "テスト"

Address:                 135792
Type:                    Set
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76799 - 03/12/2025 21:19:59
Collation sequence:      5
Prev address:            135744
Next address:            135840

Global:    ^["^^d:\irisdb\sample-data\"]test(1)
New Value: "テスト(1)"

Address:                 135840
Type:                    Set
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76799 - 03/12/2025 21:19:59
Collation sequence:      5
Prev address:            135792
Next address:            135892

Global:    ^["^^d:\irisdb\sample-data\"]test(2)
New Value: "テスト(2)"

Address:                 135892
Type:                    KillNode
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76799 - 03/12/2025 21:19:59
Collation sequence:      5
Prev address:            135840
Next address:            135932

Global:    ^["^^d:\irisdb\sample-data\"]test(2)

Address:                 135932
Type:                    Set
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76799 - 03/12/2025 21:19:59
Collation sequence:      5
Prev address:            135892
Next address:            135984

Global:    ^["^^d:\irisdb\sample-data\"]test(3)
New Value: "テスト(3)"

Address:                 135984
Type:                    Set
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76799 - 03/12/2025 21:19:59
Collation sequence:      5
Prev address:            135932
Next address:            136040

Global:    ^["^^d:\irisdb\sample-data\"]test(3,1)
New Value: "テスト(3,1)"

Address:                 136040
Type:                    ZKill
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76799 - 03/12/2025 21:19:59
Collation sequence:      5
Prev address:            135984
Next address:            136080

Global:    ^["^^d:\irisdb\sample-data\"]test(3)

Address:                 140736
Type:                    KillNode
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76799 - 03/12/2025 21:19:59
Collation sequence:      5
Prev address:            140720
Next address:            140772

Global:    ^["^^d:\irisdb\sample-data\"]test

Address:                 199728
Type:                    Set
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76837 - 03/12/2025 21:20:37
Collation sequence:      5
Prev address:            199712
Next address:            199776

Global:    ^["^^d:\irisdb\sample-data\"]test
New Value: "テスト"

Address:                 199776
Type:                    Set
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76837 - 03/12/2025 21:20:37
Collation sequence:      5
Prev address:            199728
Next address:            199824

Global:    ^["^^d:\irisdb\sample-data\"]test(1)
New Value: "テスト(1)"

Address:                 199824
Type:                    Set
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76837 - 03/12/2025 21:20:37
Collation sequence:      5
Prev address:            199776
Next address:            199876

Global:    ^["^^d:\irisdb\sample-data\"]test(2)
New Value: "テスト(2)"

Address:                 199876
Type:                    KillNode
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76837 - 03/12/2025 21:20:37
Collation sequence:      5
Prev address:            199824
Next address:            199916

Global:    ^["^^d:\irisdb\sample-data\"]test(2)

Address:                 199916
Type:                    Set
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76837 - 03/12/2025 21:20:37
Collation sequence:      5
Prev address:            199876
Next address:            199968

Global:    ^["^^d:\irisdb\sample-data\"]test(3)
New Value: "テスト(3)"

Address:                 201520
Type:                    Set
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76837 - 03/12/2025 21:20:37
Collation sequence:      5
Prev address:            201504
Next address:            201576

Global:    ^["^^d:\irisdb\sample-data\"]test(3,1)
New Value: "テスト(3,1)"

Address:                 201576
Type:                    ZKill
In transaction:          No
Job ID:                  0x00000E78
Process ID:              3704
ECP system ID:           0
Time stamp:              67276,76837 - 03/12/2025 21:20:37
Collation sequence:      5
Prev address:            201520
Next address:            201616

Global:    ^["^^d:\irisdb\sample-data\"]test(3)

ちゃんと出力されていますね。

特定のジャーナルレコードを絞り込む

特定のレコードを絞り込んでみます。

サンプルとして、ZKILL(%operation=9)を実行したジャーナルレコードを出力してみます。
コマンドは下記になります。

zn "%SYS"
s path = [ジャーナルファイルのフルパス]
d SELECT^JRNDUMP(path, "", "", "^sample", 1, 9)

実行結果が下記になります。
グローバル「^sample」でZKILLを行ったのは1回のみなので、1レコードが出力されています。

Address:                 205024
Type:                    ZKill
In transaction:          No
Job ID:                  0x00000730
Process ID:              1840
ECP system ID:           0
Time stamp:              67276,76837 - 03/12/2025 21:20:37
Collation sequence:      5
Prev address:            204968
Next address:            205068

Global:    ^["^^d:\iris\mgr\user\"]sample(3)

他サンプルです。
各引数を組み合わせて、対象のジャーナル・レコードを出力する事が可能です。

// ^sample(3)のSET文、ZKILL文が出力
d SELECT^JRNDUMP(path, "", "", "^sample(3)")

// USERのDATに関連するジャーナル・レコードが全て出力される
d SELECT^JRNDUMP(path, "", "D:\IRIS\mgr\user")

// ECPシステムID = 1のジャーナル・レコードが全て出力
d SELECT^JRNDUMP(path, "", "", "",,,1)

%SYS.Journal.Record

ジャーナルレコードAPIのクラス・クエリを利用して、ジャーナルファイルの参照を行います。

ある程度の検索も行えるので、目的のジャーナル・レコードを出力する事が可能です。
恐らく管理ポータルも、このクラス・クエリを利用していると思われます。

ジャーナルファイルからの読み込みサンプル

ClassMethod readJornalRecord(path As %String, clms As %String = "*", offset As %Integer = 0, order As %Integer = 0, match As %List = "")
{
	try {
		n $namespace
		s $namespace = "%SYS"
		
		s stmt = ##class(%SQL.Statement).%New()
		$$$ThrowOnError( stmt.%PrepareClassQuery("%SYS.Journal.Record", "List") )
 		s res = stmt.%Execute(path, clms, offset, order, match)
 		while (res.%Next(.sts)) {
			w:(res.%Get("Address")'="") "Address:",?25,res.%Get("Address"),!
			w:(res.%Get("Type")'="") "Type:",?25,res.%Get("Type"),!
			w:(res.%Get("TypeName")'="") "TypeName:",?25,res.%Get("TypeName"),!
			w:(res.%Get("PrevAddress")'="") "PrevAddress:",?25,res.%Get("PrevAddress"),!
			w:(res.%Get("NextAddress")'="") "NextAddress:",?25,res.%Get("NextAddress"),!
			w:(res.%Get("InTransaction")'="") "InTransaction:",?25,res.%Get("InTransaction"),!
			w:(res.%Get("TimeStamp")'="") "TimeStamp:",?25,res.%Get("TimeStamp"),!
			w:(res.%Get("ProcessID")'="") "ProcessID:",?25,res.%Get("ProcessID"),!
			w:(res.%Get("RemoteSystemID")'="") "RemoteSystemID:",?25,res.%Get("RemoteSystemID"),!
			w:(res.%Get("ClusterSequence")'="") "ClusterSequence:",?25,res.%Get("ClusterSequence"),!
			w:(res.%Get("DatabaseName")'="") "DatabaseName:",?25,res.%Get("DatabaseName"),!
			w:(res.%Get("GlobalReference")'="") "GlobalReference:",?25,res.%Get("GlobalReference"),!
			w:(res.%Get("GlobalNode")'="") "GlobalNode:",?25,res.%Get("GlobalNode"),!
			w:(res.%Get("NumberOfValues")'="") "NumberOfValues:",?25,res.%Get("NumberOfValues"),!
			w:(res.%Get("NewValue")'="") "NewValue:",?25,res.%Get("NewValue"),!
			w:(res.%Get("OldValue")'="") "OldValue:",?25,res.%Get("OldValue"),!
			w:(res.%Get("Collation")'="") "Collation:",?25,res.%Get("Collation"),!
			w:(res.%Get("Bit Position")'="") "Bit Position:",?25,res.%Get("Bit Position"),!
			w:(res.%Get("Bit OldLength")'="") "Bit OldLength:",?25,res.%Get("Bit OldLength"),!
			w:(res.%Get("Marker MID")'="") "Marker MID:",?25,res.%Get("Marker MID"),!
			w:(res.%Get("Marker Sequence")'="") "Marker Sequence:",?25,res.%Get("Marker Sequence"),!
			w:(res.%Get("Marker Text")'="") "Marker Text:",?25,res.%Get("Marker Text"),!
			w:(res.%Get("MirrorDatabaseName")'="") "MirrorDatabaseName:",?25,res.%Get("MirrorDatabaseName"),!
			
			w !
 		}
	} catch e {
		w !,$system.Status.DisplayError(e.AsStatus())
	}
}

引数説明

引数説明
pathジャーナルファイルのフルパス
clmsカラムの指定
“*”を指定すると、全てのカラムを取得する
特定のカラムを出力したい場合は、カンマで接続する
例)”Address,TypeName” 等
offset指定したオフセット値(Address)の値以降を対象とする
order0 : オフセット値(Address)の昇順から出力する(default値)
1 : オフセット値(Address)の降順から出力する
matchジャーナル・レコードの検索を行う
$lb([カラム名], [演算子], [検索対象])

例)^SYS.Historyグローバルを除外する
$lb(“GlobalNode”, “‘[“, “^SYS.History”)

演算子:=, ‘=, [, ‘[, <, <=, >…etc…

テキスト等に出力する場合は、下記を参考にしてみてください。

%SYS.Journal.File

ジャーナルファイルのAPIを利用する方法になります。

下記サンプルは、ジャーナルファイルを引数として読み込み、各ジャーナル・レコードを取得してプロパティの値を表示しています。

後は、煮るなり焼くなり、自作で絞り込むなりファイルに出力するなり。。。

かなり手間ですが、知っておくとジャーナルの構造が理解できます。
また、完全自作なので、思うがままに検索を行うことができるのもメリットです。

ジャーナルファイルからの読み込みサンプル

ClassMethod readJornal(path As %String)
{
	try {
		n $namespace
		s $namespace = "%SYS"
		
		s jrnforef = ##class(%SYS.Journal.File).%OpenId(path)

		s (cnt,address) = 0
		f {
			s recObj = jrnforef.GetRecordAfter(address)
			q:'$IsObject(recObj)
			s address =recObj.Address
			
			s clsNm = $p($className(recObj),".",*)
			
			
			w "classname:",?25,clsNm,!
			w "Address:",?25,address,!
			w "Type:",?25,recObj.Type,!
			w "TypeName:",?25,recObj.TypeName,!
			w "In transaction:",?25,recObj.InTransaction,!
			w "JobID",?25,recObj.JobID,!			
			w "ProcessID:",?25,recObj.ProcessID,!
			w "ECP system ID:",?25,recObj.ECPSystemIDGet(),!
			w "Timie stamp:",?25,recObj.TimeStamp,!
			
			i (clsNm="Record"){
			}elseif (clsNm="SetKillRecord")||(clsNm="BitSetRecord"){
				w "Collation sequence:",?25,recObj.Collation,!
				w "DatabaseName:",?25,recObj.DatabaseName,!
				w "GlobalNode:",?25,recObj.GlobalNode,!
				w "OldValue:",?25,recObj.OldValue,!
				w "NewValue:",?25,recObj.NewValue,!
				w "GlobalReference:",?25,recObj.GlobalReference,!
				
				i (clsNm="BitSetRecord"){
					w "OldLength:",?25,recObj.OldLength,!
					w "Position:",?25,recObj.Position,!
				}
			}elseif (clsNm="Marker"){
				w "Sequence:",?25,recObj.Sequence,!
				w "MID:",?25,recObj.MID,!
				w "Text:",?25,recObj.Text,!
			}
			w "Prev address:",?25,recObj.PrevAddress,!
			w "Next address:",?25,recObj.NextAddress,!
			
			w !
			s cnt = cnt + 1
		}
		w !,"レコード件数: ",cnt,"件",!
		
	} catch e {
		w !,$system.Status.DisplayError(e.AsStatus())
	}
}

管理ポータルを利用する

「結局管理ポータルに戻るんかーい」
と突っ込む方もいらっしゃるかと思いますが、最後に管理ポータルを利用したジャーナルファイルの確認方法をご紹介して〆たいと思います。

管理ポータルを起動し[ システムオペレーション ] > [ ジャーナル ] でジャーナル画面に遷移し、どのジャーナルファイルでも構わないので、「参照」をクリックし、「ジャーナル表示」画面へ進みます。

URLの「?$ID1=」以降を削除し、参照したいジャーナルファイルのフルパスを指定します。

見事管理ポータルから、指定のジャーナル・ファイルを参照する事が出来ました。

うん。
これが一番楽かな・・・

おわりに

いかがだったでしょうか。

管理ポータルのGUIを活用する方法から、コマンドラインで詳細な情報を取得する方法まで、用途に応じたアプローチを理解しておくと、トラブルシューティングがスムーズに進みます。

特に、システムの障害発生時やデータの不整合が疑われる場合には、ジャーナルファイルを素早く確認し、問題の原因を特定できることが求められます。

日常的な運用ではジャーナルファイルを意識する機会は少ないかもしれませんが、いざという時のために、その仕組みと操作方法を理解しておくことが大切だと思います。

本記事の紹介が、適切なトラブルシューティングを行うための一助となれば幸いです。