database
組員: 109062274, 109062314, 109062315
BufferPoolMgr
解答中實作了 lock striping, 分別對 Block 以及 File 上 lock。但我們實作中都沒考慮到。
有可能 2 個以上的 transaction 想對同一個 block 做 pin ,但是 bufferpool 中沒有相對應的 block,所以 buffer replacement strategy 會各分派一個不同的 candidate buffer 給各個 transaction 做 swapping。但這樣的結果是 bufferpool 中有不同的 Buffer 內容包著相同的 BlockId。這樣有可能會有consistency issue。這樣也違反了 pinning Buffer 原本的用意,原本用意是每個 Buffer cache 不同的 Block, 並依據 pin count 來決定 buffer replacement strategy。而且如果相同的 Block 佔據不同的 Buffer,而且這個 Block 被頻繁使用,bufferpool 能 swap out 的 Buffer 會相對減少。
我覺得解答的寫法沒有必要將同一個 filename 上 lock,這是因為假如 2 個以上的 transactions 同時執行
BufferPoolMgr::pinNew
,在 clock replacement 已經有 swapLock 保護同一個 Buffer 免於在做 swapping 還被選作 candidate buffer,所以 swapping 是一次做完沒有停頓 (即使 transactions pinNew 同一個 file)。而且 swapping 完成時,Buffer 的 BlockId 一定是不同的,因為 pinNew 代表說我們要 append file , append file 一定是 return 新的 BlockId 給 Buffer。所以也不會有不同的 Buffer 內容包著相同的 BlockId 的問題。如果是同個 file 要做 append 分配到不同 Buffer 做 swapping,其實最底層的JavaNioFileChannel::append()
也有上一個 readwrite lock,所以也沒有race condition 的問題。
故我們覺得 Block Lock Stripping 技術解決了不同 buffer 可能會有同樣的 Block 的問題。但 File Lock Stripping 有點沒必要。
BufferPoolMgr::pin
解答中的寫法跟我們在 phase 1 report 中講的方法基本上大相逕庭,但是我們的實作發現一次跑多個 testcase 會過單獨跑某個 testcase 不會過,因此把 pin
改回原來的版本(synchronized method)。這對於 throughput 是個很大的 bottleneck,因為 synchronized method 只會允許 1 個 Thread 執行 pin
這個程式片段,但是如果多個 transaction pin 的 Buffer 不同, Block 也不同,這其實是可以併行執行的,也不會相互影響,這也許就是我們在 Phase1 實驗中得不到更好的 throughput 的原因。
BufferMgr
這部分的優化,助教與我們的作法皆是減少 synchronized(bufferPool)
所包住的範圍,而差異在於助教包得更精準,且根據何時要呼叫 bufferPool.notifyAll;
有更好的判斷。而在實作 pin
與 pinNew
實作FIFO的部分上,我們改為允許由 WAKE_RATIO
外部參數決定之區間大小皆可繼續執行的邏輯,期望有更好的 performance。
Buffer
解答實作做了 contentLock,對 Page 的操作上 Lock,但我們沒有實作這功能。而 pins
計數器以 AtomicInteger
來實現這點不謀而合。
從解答中發現 contentLock 主要是對以下方法上 ReadWriteLock
:
Buffer::getval
Buffer::setval
Buffer::lastLsn
Buffer::close
Buffer::flush
Buffer::isModified
// 原本沒有的方法但是其實有些 method 可以拿掉 ReadWrite Lock,像是 Buffer::close
底層的 JavaNioByteBuffer::close
不會做任何事,還有在 Buffer::getval
中呼叫的 Page::getval
以及 Buffer::setval
中呼叫 Page::setval
。
另外 Page
的 setval
以及 getval
都還保留 synchronized method。由於有 contentLock 就能保證 Page::getval
以及 Page::setval
是 thread-safe,因此應該可以拿掉 synchronized。
FileMgr & Page
Page
methods 的 synchronized,使讀寫檔案、IoBuffer 為 thread-safe,因此移除了 FileMgr
的 read
、write
、append
synchronized。而在 FileMgr
中,getFileChannel
與 delete
則根據 filename 使用 lock stripping 技術,畢竟不能同時有多個 thread 新增或刪除同一個檔案FileMgr
對檔案操作之 methods 加上 StampedLock,Read
用 pessimistic read、Size
用 very-optimistic read、Write
& delete
則採用 write lock。而在 Page
中,由於對檔案讀寫已經是 thread-safe,因此將 read
、write
、append
的 synchronized 移除,然後將對 IoBuffer 操作之 getVal
與 setVal
加上 ReentrantLockgetFileChannel
(相當於我們的 getLockableFile
) 的 thread-safe,雖然有使用 try-catch 避免出錯,但邏輯上還是助教的寫法比較好 (根據 filename 加上 lock)。而其他部分,相比助教直接使用 synchronized 達成 thread-safe,我們會根據不同狀況使用 StampedLock、ReentrantLock,使得有較好的並行性。BlockId
與助教類似,hashCode
及 toString
方法能直接取出恆定的計算結果。