第9回講義資料

データベースシステムⅠ
第9回
今日の話題
• 同時実行制御
– ロックとデッドロック
• 障害回復
– 障害の分類
– ログを用いた障害回復
デッドロック(dead lock)
• ロックを用いる同時実行制御を行う場合、複
数のトランザクションが互いのアンロックを
延々と待ち続け一切処理が進まない状態に
なってしまうことがある
– これを「デッドロック」という
• ロックを用いるデータベースシステムでは、
デッドロック状態にないかどうかをチェックした
り、デッドロック状態を解消したりする機構を
持つ必要がある
デッドロックの例
• データベースにA、Bの2つの項目が格納され
ているとする
– トランザクションT1がAにロックをかけ、
Bがアンロックされるのを待っている
– トランザクションT2がBにロックをかけ、
Aがアンロックされるのを待っている
という状態になると、デッドロックである
• 二相ロッキングプロトコルを使っていたとして
も、デッドロックは起こりうる
デッドロックが起こる
スケジュールの例
• トランザクションT1
XL1(A)
W1(A)
XL1(B)
W1(B)
UL1(A)
UL1(B)
ここでデッド
ロックが起こる
• トランザクションT2
XL2(B)
W2(B)
XL2(A)
W2(A)
UL2(A)
UL2(B)
デッドロック状態
ロック中
A
T1
アンロック待ち
アンロック待ち
T2
B
ロック中
デッドロックの検出法
• 待ちグラフ(wait-for graph)による方法
– どのトランザクションがどのトランザクションのアン
ロックを待っているかをグラフにしたもの
– トランザクションTjがロックしている項目がアンロックさ
れるのをトランザクションTiが待っているときTi→Tjの
有向エッジをひく
– 待ちグラフにサイクル(閉路)ができたらデッドロック
状態
• タイムアウトによる方法
– トランザクションが一定時間以上アンロック待ちに
なったら、「デッドロックの可能性がある」として、強制
的にアボートする
待ちグラフの例
T2
T1
T1
T3
T2
デッドロックの回避法
① 一括ロックする方法
② データを順序付ける方法
③ トランザクションを順序付ける方法
① 一括ロックする方法
• ロックする必要のある項目を、それぞれのトラ
ンザクションがその開始時に一括してロックす
る方法
• すべてのロックがとれなかったら、一切ロック
せず、しばらく待ってから再度ロックを試みる
• この方法と二相ロッキングプロトコルを組み
合わせたものを「保守的二相ロッキングプロト
コル(conservative two phase locking)」という
通常の二相ロック
トランザクション
開始
ロ
ッ
ク
の
量
コミット
成長相
縮退相
保守的二相ロック
厳格な二相ロック
開始
コミット
時間
② データを順序付ける方法
• 項目にある種の順序をつけておき、各トラン
ザクションがロックをかける順序はこの順序を
守るようにする方法
ロック中
T1
1
2
3
アンロック待ち
×
T2
③ トランザクションを
順序付ける方法
• トランザクション開始時刻の時刻印(timestamp)
を使ってトランザクションに順序を導入し、待ち関
係のサイクルができないようにする方法
– ウェイト・ダイ方式
– ワウンド・ウェイト方式
• 以下、トランザクションTiの時刻印をTS(Ti)と書く
ウェイト・ダイ(wait-die)方式
• トランザクションTiがある項目をロックしようとした
とき、それと競合するロックが別のトランザクショ
ンTjによってすでになされていたら、
– TS(Ti)<TS(Tj)であれば、
TiはTjがアンロックするのを待つ
– TS(Ti)>TS(Tj)であれば、
Tiはアボートする
• すなわち、Tiの開始が先ならアンロックされるの
を待ち(wait)、そうでなければTiはアボートする
(die)
ウェイト・ダイ方式
Ti
ロック
Ti は Tj の
アンロックを待つ
Tj
ロック
Ti
Ti はアボートする
ロック
Tj
ロック
ワウンド・ウェイト(wound-wait)方式
• トランザクションTiがある項目をロックしようとした
とき、それと競合するロックが別のトランザクショ
ンTjによってすでになされていたら、
– TS(Ti)<TS(Tj)であれば、
Tjをアボートさせる
– TS(Ti)>TS(Tj)であれば、
TiはTjがアンロックするのを待つ
• すなわち、Tiの開始が先ならロックを持っているTj
をアボートさせ(wound)、そうでなければTiはア
ンロックされるのを待つ(wait)
ワウンド・ウェイト方式
Ti
Tj をアボートさせる
ロック
Tj
ロック
Ti
ロック
Tj
ロック
Ti は Tj の
アンロックを待つ
③ トランザクションを順序付ける方法
• ウェイト・ダイ方式でも、ワウンド・ウェイト方式で
も、最小の時刻印を持つトランザクション(実行
中のトランザクションのうち、一番最初に始まっ
たトランザクション)はアボートされることがない
• したがって、アボートされたトランザクションに元
と同じ時刻印を与えて再実行すれば(いつか必
ず最小の時刻印になるので)、いずれはアボート
されることなく実行されることが保証される
アプリケーションプログラムの
デッドロック対策
• デッドロックが発生するといずれかのトランザク
ションが強制的にアボートされる
• また、デッドロックを回避するためにトランザク
ションが強制的にアボートされることもある
• したがって、アプリケーションプログラムは原則と
して、強制アボートが起こったらトランザクション
を再開するようにプログラミングされる必要があ
る
• デッドロックの発生頻度を下げるため、トランザク
ションを小さくする(こまめにコミットするようにす
る)という手法はよく使われる
障害回復
• トランザクションのACID属性を完全に実現す
るためには、データベースシステム稼働中に
何らかの障害が起こった場合の対処が必要
となる
• このような機能のことを障害回復機能という
障害の分類
• トランザクション障害
• システム障害
• メディア障害
トランザクション障害
(transaction failure)
• 個々のトランザクションが何らかの理由でコ
ミットに至る前に異常終了するケース
• 以下のような場合がある
– 何らかの理由でトランザクションが自主的にア
ボートする場合
– アプリケーションプログラムの実行時エラーにより
強制的に終了させられる場合
– デッドロックによりトランザクションの続行が不可
能な場合、など
システム障害(system failure)
• ハードウェアあるいはソフトウェア上の問題か
らシステムダウンが発生し、その時点でのす
べてのトランザクションが異常終了してしまう
ケース
• 障害発生時のすべての主記憶上のデータは
消滅するが、ディスクから障害発生直前の状
態のデータを読み出すことは可能であるので、
これを用いて復旧する
メディア障害(media failure)
• データベースを格納したディスクに障害が発
生し、データの読み出しができなくなるケース
• 復旧にはバックアップとログを用いる(後述)
トランザクションと障害回復
• トランザクション障害が発生した場合は、トランザクション
はアボートされる
– したがって、もしそのトランザクションが障害発生前にデータ
ベースに書き込みを行っていたら、それを元の値に戻す処理を
しなければならない
• システム障害が発生した場合には、リスタート処理が行わ
れる
– 障害発生時点でコミット済であったトランザクションとそれ以外
のトランザクションによる基本操作を区別し、その後始末を行う
必要がある
– コミット済のトランザクションによるデータの書き込みは確実に
データベースに反映しなければならない
– コミット済でないトランザクションは確実にアボートさせなければ
ならない
基本操作とバッファリング
• アプリケーションはデータベースシステムに対して
read/writeの基本操作を要求する
• データベースシステムは、効率よく処理するため(ディ
スクアクセスの回数を減らすため)バッファリングを行
う
– バッファの内容をディスクに書き戻す処理を「フラッシュ
(flush)」という
• したがって、アプリケーションがwriteを実行したからと
いって、必ずしもその時点でその内容がディスクに書
き込まれているとは限らない
• バッファリングされた内容がディスクに書き込まれる前
に(フラッシュされる前に)障害が起こると問題となる
基本操作とバッファリング
read / write
バッファ
(主記憶)
アプリケーション
プログラム
データベース
データベースシステム
アプリケーションのread/writeと
データベースシステムのread/writeの
タイミングは一致しない
システム障害発生時
read / write
バッファ
(主記憶)
アプリケーション
プログラム
データベース
データベースシステム
障害が発生すると、
バッファの内容がすべて
失われる
システム障害のケース
begin
read(A, x)
read(B, y)
x := x – 10000
y := y + 10000
write(A, x)
write(B, y)
commit
AからBへの送金
・ Aから出金
・ Bに入金
ケース(1)
ここで障害が起こった場合
ケース(2)
ここで障害が起こった場合
ケース(1)
write(A, x)の直後に障害
• writeは終わっているのでxの値はバッファに書き
込まれているが、障害が発生する前にそれが
ディスクに書き戻されていたかどうかが問題とな
る
• ディスクに書き戻されているとすると、Aからの出
金は終わってるのにBへの入金が終わっていな
いことになる→不整合状態
• ディスクに書き戻されていないとすると、Aからの
出金も終わっていないことになる
• いずれにせよ、システムリスタート時には、トラン
ザクション開始前の状態に戻す必要がある
ケース(2)
commitの直後に障害
• write(A, x)、write(B, y)は終わっているのでx,
yの値はバッファには書き込まれたが、障害
が発生される前にそれがディスクに書き戻さ
れていたかどうかが問題となる
• トランザクションはコミット済であるので、たと
えディスクに書き戻される前に障害が発生し
ていたとしても、システムリスタート時の処理
でデータベースがコミット後の状態になること
を保証しなければならない
ログを用いた障害回復
• 障害回復を実現するために最も一般的に用いられる
のはログ(log)を用いる手法である
– ログのことをジャーナル(journal)ということもある
• ログとは、すべてのトランザクションのすべての基本
操作の内容をディスク中に記録したものである
– ログの個々の操作の記録を「ログレコード(log record)」と
いう
• ログを見ると、いつ、どのトランザクションが、データ
ベースのどの項目に対してどういう操作を行ったか、
がわかる
• 障害が発生したら、システムリスタート時にこのログを
使ってデータベースを正しい状態に戻す処理を行う
ログレコードの内容
• トランザクションTiの開始: begin
[ B: Ti ]
• トランザクションTiによる書き込み: write(A, x)
[ W: Ti, A, Old, New ]
• Old: Aの更新前の値(ビフォアイメージ(before image))
• New: Aの更新後の値(アフターイメージ(after image))
• トランザクションTiによる読み出し: read(A, x)
[ R: Ti, A ]
• トランザクションTiのコミット: commit
[ C: Ti ]
• トランザクションTiのアボート: abort
[ A: Ti ]
• なお、それぞれのログレコードには、LSN(log sequence number)と
いう番号を順に付ける
トランザクションの実行とログ
トランザクションT1 トランザクションT2
begin
read(A, x)
begin
write(B, 150)
commit
read(B, y)
write(A, 200)
commit
この例では、
トランザクションが
始まる前のAとBの
初期値はともに「100」
としている(before image)
ログ
1:
2:
3:
4:
5:
6:
7:
8:
[ B: T1 ]
[ R: T1, A ]
[ B: T2 ]
[ W: T1, B, 100, 150 ]
[ C: T1 ]
[ R: T2, B ]
[ W: T2, A, 100, 200 ]
[ C: T2 ]
これがLSN
スケジュールとログレコード
• ログレコードの種類はB(begin)、R(read)、W(write)、
C(commit)、A(abort)の5種類
• ここで、「厳格な二相ロッキングプロトコル」などにより、
トランザクションのスケジュールが厳格であることが保
証される場合を考える
• すると、「あるトランザクションがアボートする結果とし
て同じ項目をreadしている別のトランザクションを連鎖
的にアボートさせる」ということが起きないので、障害
発生時にreadのことを考慮する必要がなくなる
• 以下ではそのような状況を考え、readは無視し、write
だけ考えることとする
ログ先書きプロトコル
(write ahead log protocol, WAL)
• 基本操作write(A, New)が行われた場合、Newの値が
バッファリングされ、やがてその内容がディスク内の
データベースに書かれる
• この操作が行われるとき、ログレコード
[ W: Ti, Old, New ]
が記録されるが、この記録もディスク上のファイル(ロ
グファイル)に書かれる
• 障害発生時にOldの値が必要になる可能性があること
を考えると、データベースにNewを書くよりもさきに、ロ
グファイルに上記のログレコードが先に書かれている
必要がある
• この方式を「ログ先書きプロトコル」という
ログファイルとバッファリング
• 通常、ログファイルへのログレコードの記録も
バッファリングされる
– 毎回ファイルに書き込むのではなく、必要な時に
まとめてフラッシュする
• データベースシステムではLSN(log sequence
number)を用いて現在どのログレコードまで
がディスクに書き込み済みかを管理する
障害回復の方式
• no-undo/redo方式
• undo/no-redo方式
• undo/redo方式
• 障害発生後の回復処理(リスタート時の処
理)で、実行中だったトランザクションの基本
操作を元に戻す処理を「undo」、やり直す処
理を「redo」という
no-undo/redo方式
• 障害回復(リスタート)時にはredoのみ行い、undoは
行わない方式
• トランザクション中でのwrite操作はログにのみ記録し、
そのトランザクションがコミットするまでデータベース本
体への更新は一切行わない
• どの段階で障害が発生しても、コミット済でないトラン
ザクションの操作はデータベースに書き込まれていな
いので、障害回復時にundoの必要がない(したがって
ビフォアイメージの記録の必要がない)
• 障害回復時には、コミット済であるのにデータベース
に書き込みが終わっていなかった処理を、redoする
no-undo/redo方式
通常時: トランザクション実行中
read / write
×
アプリケーション
プログラム
実行時には
データベースには
書き込まず、
ログにのみ
記録を残す
データ用
バッファ
[ B: Ti ]
[ W: Ti, ・・・]
データベース
×
ログ用バッファ
データベースシステム
ログファイル
no-undo/redo方式
通常時: コミット時①
read / write
アプリケーション
プログラム
コミット時にはまず、
ログの内容を
ログファイルに
記録する
(これにより、
コミット済状態となる)
データ用
バッファ
データベース
[ B: Ti ]
[ W: Ti, ・・・]
[ C: Ti ]
ログ用バッファ
データベースシステム
ログファイル
no-undo/redo方式
通常時: コミット時②
read / write
アプリケーション
プログラム
その後、
バッファを
フラッシュして
データベースを
更新する
データ用
バッファ
データベース
[ B: Ti ]
[ W: Ti, ・・・]
[ C: Ti ]
ログ用バッファ
データベースシステム
ログファイル
no-undo/redo方式
障害回復時: コミット済でない場合
read / write
アプリケーション
プログラム
コミット済でない場合
データベースには
何も書き込まれて
いないので、
回復処理で何もする
必要がない
データ用
バッファ
データベース
[ B: Ti ]
[ W: Ti, ・・・]
[ C: Ti ]
ログ用バッファ
データベースシステム
ログファイル
no-undo/redo方式
障害回復時: コミット済の場合
read / write
アプリケーション
プログラム
コミット済の場合
データベースの更新が
終わっていない可能性が
あるので、ログを使って
redoする
データ用
バッファ
データベース
[ B: Ti ]
[ W: Ti, ・・・]
[ C: Ti ]
ログ用バッファ
データベースシステム
ログファイル
undo/no-redo方式
• 障害回復(リスタート)時にはundoのみ行い、redoは
行わない方式
• no-undo/redo方式の逆
• トランザクション中でのwrite操作はすべてデータベー
スに対して更新し、そのトランザクションがコミットする
前にすべてのデータベース本体への更新を行う
• どの段階で障害が発生しても、コミット済のトランザク
ションの操作はデータベースへの更新が終わっている
ので、障害回復時にredoの必要がない(したがってア
フターイメージの記録の必要がない)
• 障害回復時には、コミット済でないトランザクションが
行ったデータベースへの更新をundoする
undo/no-redo方式
通常時: コミット時
read / write
アプリケーション
プログラム
コミット時にはデータ
ベースへの更新を行い、
ログもログファイルに
記録する(これにより、
コミット済状態となる)
データ用
バッファ
データベース
[ B: Ti ]
[ W: Ti, ・・・]
[ C: Ti ]
ログ用バッファ
データベースシステム
ログファイル
undo/no-redo方式
障害回復時: コミット済の場合
read / write
アプリケーション
プログラム
コミット済の場合
データベースの更新は
すべて終わっているので、
何もする必要がない
データ用
バッファ
データベース
[ B: Ti ]
[ W: Ti, ・・・]
[ C: Ti ]
ログ用バッファ
データベースシステム
ログファイル
undo/no-redo方式
障害回復時: コミット済でない場合
read / write
アプリケーション
プログラム
コミット済でない場合
データベースの更新が
終わってしまっている
可能性があるので、
ログを使ってundoする
データ用
バッファ
データベース
[ B: Ti ]
[ W: Ti, ・・・]
[ C: Ti ]
ログ用バッファ
データベースシステム
ログファイル
no-undo/redo方式と
undo/no-redo方式
• どちらもシンプルな方式ではあるが、「コミット
済になるまでバッファをフラッシュできない
(no-undo/redo方式の場合)」、あるいは、「コ
ミットする前には必ずすべての更新をデータ
ベースに対して行わないといけない
(undo/no-redo方式の場合)」という、制約が
ある
• そのため、実際には次に説明するundo/redo
方式を採用するのが主流となっている
undo/redo方式
• 障害回復(リスタート)時に必要に応じてundo処
理もredo処理もどちらも行う方式
時間
ログ
B1
B2
C1
障害発生
– T1は障害前にコミットしているので必要に応じて
redoする
– T2は障害前にコミットしていないので必要に応じて
undoする
undo/redo方式の詳細
• トランザクションの開始: begin
– ログレコード [B: Ti ] をログ用バッファに書き込む
• トランザクションによる書き込み: write(A, x)
– ログレコード [ W: Ti, Old, New ] をログ用バッファに書き込む
– データ用バッファで A を含むページを更新する
• このページをディスクに書き込む場合は、その前にログ用バッファを
書き込む(ログ先書きプロトコル)
• トランザクションのコミット: commit
– ログレコード [ C: Ti ] をログ用バッファに書き込んだ後、ログ用
バッファをフラッシュする
• これにより、トランザクションは「コミット済」状態となる
• トランザクションのアボート: abort
– ログレコード [ A: Ti ] をログ用バッファに書き込んだ後、ログの
内容に基づきそのトランザクションが行ったwriteのundo処理を
行う
undo/redo方式の例(1)
トランザクションの開始
アプリケーション
プログラム
ログ用バッファに
[ B: Ti ] を書き込む
Old
Old
データ用
バッファ
データベース
[ B: Ti ]
ログ用バッファ
データベースシステム
ログファイル
undo/redo方式の例(2)
トランザクションによる書き込み
write
アプリケーション
プログラム
ログ用バッファに
[ W: Ti, Old New ]
を書き込み、
新しい値 Newを
データ用バッファに
書き込む
New
Old
データ用
バッファ
データベース
[ B: Ti ]
[ W: Ti, O, N]
ログ用バッファ
データベースシステム
ログファイル
undo/redo方式の例(3)
トランザクションのコミット
write
アプリケーション
プログラム
ログ用バッファに
[ C: Ti ]を書き込み、
ログ用バッファを
フラッシュする
New
Old
データ用
バッファ
データベース
[ B: Ti ]
[ W: Ti, O, N]
[ C: Ti ]
ログ用バッファ
データベースシステム
[ B: Ti ]
[ W: Ti, O, N]
[ C: Ti ]
ログファイル
undo/redo方式の例(4)
障害回復処理
write
アプリケーション
プログラム
ログを使って
undo/redo処理する
New
Old
データ用
バッファ
データベース
[ B: Ti ]
[ W: Ti, O, N]
[ C: Ti ]
ログ用バッファ
データベースシステム
[ B: Ti ]
[ W: Ti, O, N]
[ C: Ti ]
ログファイル
障害のタイミングと
undo/redo処理の詳細
以下の例で考えてみる
トランザクションT1 トランザクションT2
begin
read(A, x)
begin
write(B, 150)
commit
ケース1:
read(B, y)
ここで障害発生
write(A, 200)
commit
ケース2:
ここで障害発生
ログ
1:
2:
3:
4:
5:
6:
7:
8:
[ B: T1 ]
[ R: T1, A ]
[ B: T2 ]
[ W: T1, B, 100, 150 ]
[ C: T1 ]
[ R: T2, B ]
[ W: T2, A, 100, 200 ]
[ C: T2 ]
ケース(1)
トランザクションT1 トランザクションT2
begin
read(A, x)
begin
write(B, 150)
ログ
1:
2:
3:
4:
[ B: T1 ]
[ R: T1, A ]
[ B: T2 ]
[ W: T1, B, 100, 150 ]
• この場合、ログから T1、T2ともにコミットしていないことがわかる
• したがって、4: [ W: T1, B, 100, 150 ] をundoする
ケース(2)
トランザクションT1 トランザクションT2
begin
read(A, x)
begin
write(B, 150)
commit
read(B, y)
write(A, 200)
ログ
1:
2:
3:
4:
5:
6:
7:
[ B: T1 ]
[ R: T1, A ]
[ B: T2 ]
[ W: T1, B, 100, 150 ]
[ C: T1 ]
[ R: T2, B ]
[ W: T2, A, 100, 200 ]
• この場合、ログから T1がコミットし、T2がにコミットしていない
ことがわかる
• したがって、4: [ W: T1, B, 100, 150 ] をredoし、
7: [ W: T2, A, 100, 200 ] をundoする
チェックポイント(checkpoint)
• undo/redo方式に基づく障害回復処理(リスター
ト時の処理)では、システム起動時から障害発生
時までのログが必要になるが、これは膨大な量
になる
– 障害回復時にこれをすべてundo/redoするのでは時
間がかかる
• そこで、一定周期でバッファの内容をディスクに
書き込み(フラッシュし)、障害回復時にはその時
点以降のみundo/redoすればよいようにするの
が普通である
– これを「チェックポイント(checkpoint)」という
単純なチェックポイント処理手順
• 以下の手順により、チェックポイントを作る
① 新たなトランザクションの開始を禁止する
② アクティブな(実行中の)トランザクションがすべてコ
ミットするかアボートするまで処理を続ける
③ アクティブなトランザクションが無くなったら、まずロ
グ用バッファをフラッシュし、続いてデータ用バッファ
をフラッシュする
④ ログ用バッファにログレコード [ CHECKPOINT ] を書
き込み、もう一度ログ用バッファをフラッシュする
• これにより、CHECKPOINT以降に発生した障害
の回復処理は、CHECKPOINT以降のログのみを
undo/redoすればよくなる
メディア障害と対策
• データベース本体や、ログファイルを保存してお
くディスク装置が故障するのがメディア障害であ
る
• メディア障害に対する対処としては以下のように
なる
– 定期的にデータベース全体のバックアップをとる
– バックアップ時以降のログを使って回復する
• どんな障害でもログが失われると回復処理が不
可能になるので、ログは異なる装置に二重・三
重に記録を取るようにする
成績評価
• 期末試験で60点以上取得し、かつ、実習課
題のレポートの本提出をすることを、単位の
条件とする
• 合格者の評点(A~C)は、期末試験の結果と、
実習課題、レポート内容から決定する
期末試験について
• 期末試験は、6月26日(木)に7C202(実習室3)で行う
• 試験時間は5限(15時15分~16時30 分、75分間)に行う
– 終わった人から提出し、退席してよい
• 試験は、教科書・資料等の持ち込み可、端末の使用可、
ネットでの検索・閲覧可とする
– 問題の分量が多いので、事前に予習をしっかりしておくこと
– メール、チャット、SNS、Yahoo!知恵袋等の利用は不可とする
• http://wombat.cc.tsukuba.ac.jp/dbsystem1/ に模
擬試験問題とその解答例・解説を載せておくの
で、参考にすること
レポートについて
• レポートの締め切りは「本提出」と「再提出」の
2回がある
– 本提出締切: 6月21日(土) 19時
– 再提出締切: 7月10日(木) 19時
• どちらも manaba で提出すること
• 本提出締切には必ず提出すること(単位の要
件の一つ)
• 再提出は必須ではないが、提出すればプラ
スに評価する
レポートの本提出について
• 以下の内容を含めること
– 実体関連図
– MySQLで作成したスキーマ(create table、create indexなど
SQL文のリスト)
– PHPのプログラムリスト
– (PHPプログラムが完成している場合には)実行したPHPプロ
グラムの主要なWeb画面のコピー(3~5画面程度)
• 以下については、これまでの実習での提出後に加筆・
修正を行った場合には、本提出のレポートに含めること
– 機能一覧、画面フロー設計、画面設計、入出力項目の洗い
出し、データベース設計
• 作業したものをそのまま束ねて提出すればよい
– 全体のつじつまがあってなかったり、完成していない部分が
あってもよい
レポートの再提出について
• 以下の内容を含めること
– (本提出より完成度を高めた)PHPのプログラムリ
スト
– 実行したPHPプログラムの主要なWeb画面のコ
ピー(3~5画面程度)
• 作業したものをそのまま束ねて提出すればよ
い
– 全体のつじつまがあってなかったり、完成してい
ない部分があってもよい