在这个lab中,需要在SimpleDB实现简单的 locking-based transaction system,需要在代码的合适位置添加锁和解锁,也要给每个transaction授予锁,并且跟进每个拥有锁的transaction。
该文档剩下的部分会描述如何对transaction提供支持,并提供一个基础框架代码。
1. Getting started
$ cd simple-db-hw
$ git pull upstream master
2. Transactions, Locking, and Concurrency Control
开始之前,确保理解transaction是什么,并且理解two-phase locking是如何运作的。
在这节接下来的内容里,我们会简要回顾这些概念,并讨论如何应用到SimpleDB中。
2.1. Transactions
transaction是一组数据库操作的集合进行原子执行,就是说,要么这些操作全部执行完成要么一条都不执行,并且对于外部而言会把这一组操作观察为一个操作。
2.2. The ACID Properties
为了帮助理解 transaction management 是怎么在SimpleDB中运作的,简单回顾一下它是如何确保ACID属性被满足:
Atomicity: Rigorous two-phase locking以及 buffer management 确保了原子性。
Consistency:因为原子性操作,数据库是transaction一致的。
Isolation:Rigorous two-phase locking 提供了隔离性。
Durability:一种FORCE buffer management policy确保了持久性。
2.3. Recovery and Buffer Management
为了简化实现,推荐实现一个NO STEAL/FORCE buffer management policy。这意味着:
- 如果dirty (updated) pages资源正被一个没有提交的transaction加锁,不要从buffer pool淘汰dirty (updated) pages,这就是NO STEAL。
- 当transaction提交时,应该强制对磁盘写入dirty pages,这就是FORCE。
为了更加简化,可以假设SimpleDB在处理transactionComplete指令的时候不会崩溃。这三点确保在这个lab中不需要实现基于日志的恢复,因为你不需要撤销操作(you never evict dirty pages),也不用重做操作(you force updates on commit and will not crash during commit processing)。
2.4. Granting Locks
不需要在SimpleDB中实现调用来加锁或者释放锁。我们推荐在以页为粒度的资源上加锁,当然也可以以tuple为粒度加锁,但千万别实现table-level locking。接下来的文档和测试都是以页粒度加锁设计的。
需要创建一种数据结构跟踪每个transaction上了哪一个锁,并检查是否应该给请求锁的transaction授权。
需要实现shared locks 和 exclusive locks,它们的功能如下:
- 在一个transaction能够读资源前,必须获得资源的 shared lock。
- 在一个transaction能够写资源前,必须获得资源的 exclusive locks。
- 多个transaction可以同时获得资源的 shared lock。
- 同一时间,只有一个transaction可以获得资源的 exclusive locks。
- 如果transaction t 是唯一一个拥有资源 o shared lock 的transaction,t可以将它对资源o的锁更新为exclusive locks。
如果一个 transaction 获得了它本不该被授权的锁,代码会进入等待状态,等到那个锁恢复可用状态。
需要很小心在加锁解锁的时候,不要进入race conditions。
Exercise 1
Exercise 1.
Write the methods that acquire and release locks in BufferPool. Assuming you are using page-level locking, you will need to complete the following:
- Modify getPage() to block and acquire the desired lock before returning a page.
- Implement releasePage(). This method is primarily used for testing, and at the end of transactions.
- Implement holdsLock() so that logic in Exercise 2 can determine whether a page is already locked by a transaction.
You may find it helpful to define a class that is responsible for maintaining state about transactions and locks, but the design decision is up to you.
You may need to implement the next exercise before your code passes the unit tests in LockingTest.
BufferPool.java的代码如下:
package simpledb;
import javax.xml.crypto.Data;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* BufferPool manages the reading and writing of pages into memory from
* disk. Access methods call into it to retrieve pages, and it fetches
* pages from the appropriate location.
* <p>
* The BufferPool is also responsible for locking; when a transaction fetches
* a page, BufferPool checks that the transaction has the appropriate
* locks to read/write the page.
*
* @Threadsafe, all fields are final
*/
public class BufferPool {
/** Bytes per page, including header. */
private static final int DEFAULT_PAGE_SIZE = 4096;
private static int pageSize = DEFAULT_PAGE_SIZE;
/** Default number of pages passed to the constructor. This is used by
other classes. BufferPool should use the numPages argument to the
constructor instead. */
public static final int DEFAULT_PAGES = 50;
private final int numPages;
private final ConcurrentHashMap<PageId,Integer> pageAge;
private final ConcurrentHashMap<PageId,Page> pageStore;
private int age;
private PageLockManager lockManager;
private class Lock{
TransactionId tid;
int lockType; // 0 for shared lock and 1 for exclusive lock
public Lock(TransactionId tid,int lockType){
this.tid = tid;
this.lockType = lockType;
}
}
private class PageLockManager{
ConcurrentHashMap<PageId,Vector<Lock>> lockMap;
public PageLockManager(){
lockMap = new ConcurrentHashMap<PageId,Vector<Lock>>();
}
public synchronized boolean acquireLock(PageId pid,TransactionId tid,int lockType){
// if no lock held on pid
if(lockMap.get(pid) == null){
Lock lock = new Lock(tid,lockType);
Vector<Lock> locks = new Vector<>();
locks.add(lock);
lockMap.put(pid,locks);
return true;
}
// if some Tx holds lock on pid
// locks.size() won't be 0 because releaseLock will remove 0 size locks from lockMap
Vector<Lock> locks = lockMap.get(pid);
// if tid already holds lock on pid
for(Lock lock:locks){
if(lock.tid == tid){
// already hold that lock
if(lock.lock

最低0.47元/天 解锁文章
360





