现在还在项目一所以多线程加锁等待这一块没写,一开始没看到写了锁写了很麻烦,才发现不用写,也没测试,所以就没写,等以后做到再加上去。
写BufferPool是用map来缓存数据,但不知道TransactionId在这个函数有什么用,。没用到这个参数,代码如下,其实就两个函数
public class BufferPool {
/**
* Bytes per page, including header.
*/
public static final int PAGE_SIZE = 4096;
/**
* 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 int maximum;
private DbFile dbFile;
Catalog catalog = new Catalog();
Map<PageId,Page> pageMap = new HashMap<>();
/**
* Creates a BufferPool that caches up to numPages pages.
*
* @param numPages maximum number of pages in this buffer pool.
*/
public BufferPool(int numPages) {
// some code goes hered
maximum = numPages;
pageSet = new HashSet<>(maximum);
pageMap = new HashMap<>(maximum);
}
/**
* Retrieve the specified page with the associated permissions.
* Will acquire a lock and may block if that lock is held by another
* transaction.
* <p>
* The retrieved page should be looked up in the buffer pool. If it
* is present, it should be returned. If it is not present, it should
* be added to the buffer pool and returned. If there is insufficient
* space in the buffer pool, an page should be evicted and the new page
* should be added in its place.
*
* @param tid the ID of the transaction requesting the page
* @param pid the ID of the requested page
* @param perm the requested permissions on the page
*/
public Page getPage(TransactionId tid, PageId pid, Permissions perm)
throws TransactionAbortedException, DbException {
// some code goes here
if (pageMap.containsKey(pid)){
return pageMap.get(pid);
}else {
if (pageMap.size()<maximum){
DbFile dbFile = catalog.getDbFile(pid.getTableId());
Page page= dbFile.readPage(pid);
pageMap.put(pid,page);
return page;
}else {
DbFile dbFile = catalog.getDbFile(pid.getTableId());
Page page= dbFile.readPage(pid);
Random random = new Random();
int i=random.nextInt(maximum-1);
int j=0;
for(PageId pid1:pageMap.keySet()){
if (j==i){
pageMap.remove(pid1);
pageMap.put(pid,page);
}
j++;
}
}
}
return null;
}
/**
* Releases the lock on a page.
* Calling this is very risky, and may result in wrong behavior. Think hard
* about who needs to call this and why, and why they can run the risk of
* calling it.
*
* @param tid the ID of the transaction requesting the unlock
* @param pid the ID of the page to unlock
*/
public void releasePage(TransactionId tid, PageId pid) {
// some code goes here
// not necessary for proj1
}
/**
* Release all locks associated with a given transaction.
*
* @param tid the ID of the transaction requesting the unlock
*/
public void transactionComplete(TransactionId tid) throws IOException {
// some code goes here
// not necessary for proj1
}
/**
* Return true if the specified transaction has a lock on the specified page
*/
public boolean holdsLock(TransactionId tid, PageId p) {
// some code goes here
// not necessary for proji
return true;
}
/**
* Commit or abort a given transaction; release all locks associated to
* the transaction.
*
* @param tid the ID of the transaction requesting the unlock
* @param commit a flag indicating whether we should commit or abort
*/
public void transactionComplete(TransactionId tid, boolean commit)
throws IOException {
// some code goes here
// not necessary for proj1
}
/**
* Add a tuple to the specified table behalf of transaction tid. Will
* acquire a write lock on the page the tuple is added to(Lock
* acquisition is not needed for lab2). May block if the lock cannot
* be acquired.
* <p>
* Marks any pages that were dirtied by the operation as dirty by calling
* their markDirty bit, and updates cached versions of any pages that have
* been dirtied so that future requests see up-to-date pages.
*
* @param tid the transaction adding the tuple
* @param tableId the table to add the tuple to
* @param t the tuple to add
*/
public void insertTuple(TransactionId tid, int tableId, Tuple t)
throws DbException, IOException, TransactionAbortedException {
// some code goes here
// not necessary for proj1
}
/**
* Remove the specified tuple from the buffer pool.
* Will acquire a write lock on the page the tuple is removed from. May block if
* the lock cannot be acquired.
* <p>
* Marks any pages that were dirtied by the operation as dirty by calling
* their markDirty bit. Does not need to update cached versions of any pages that have
* been dirtied, as it is not possible that a new page was created during the deletion
* (note difference from addTuple).
*
* @param tid the transaction adding the tuple.
* @param t the tuple to add
*/
public void deleteTuple(TransactionId tid, Tuple t)
throws DbException, TransactionAbortedException {
// some code goes here
// not necessary for proj1
}
/**
* Flush all dirty pages to disk.
* NB: Be careful using this routine -- it writes dirty data to disk so will
* break simpledb if running in NO STEAL mode.
*/
public synchronized void flushAllPages() throws IOException {
// some code goes here
// not necessary for proj1
}
/**
* Remove the specific page id from the buffer pool.
* Needed by the recovery manager to ensure that the
* buffer pool doesn't keep a rolled back page in its
* cache.
*/
public synchronized void discardPage(PageId pid) {
// some code goes here
// not necessary for proj1
}
/**
* Flushes a certain page to disk
*
* @param pid an ID indicating the page to flush
*/
private synchronized void flushPage(PageId pid) throws IOException {
// some code goes here
// not necessary for proj1
}
/**
* Write all pages of the specified transaction to disk.
*/
public synchronized void flushPages(TransactionId tid) throws IOException {
// some code goes here
// not necessary for proj1
}
/**
* Discards a page from the buffer pool.
* Flushes the page to disk to ensure dirty pages are updated on disk.
*/
private synchronized void evictPage() throws DbException {
// some code goes here
// not necessary for proj1
}
}
然后是Exercise4,总算写完了,调试了很久。
先写HeapPageId.java
public class HeapPageId implements PageId {
/**
* Constructor. Create a page id structure for a specific page of a
* specific table.
*
* @param tableId The table that is being referenced
* @param pgNo The page number in that table.
*/
private int tableId;
private int pgNo;
public HeapPageId(int tableId, int pgNo) {
// some code goes here
this.tableId = tableId;
this.pgNo = pgNo;
}
/** @return the table associated with this PageId */
public int getTableId() {
// some code goes here
return tableId;
}
/**
* @return the page number in the table getTableId() associated with
* this PageId
*/
public int pageNumber() {
// some code goes here
return pgNo;
}
/**
* @return a hash code for this page, represented by the concatenation of
* the table number and the page number (needed if a PageId is used as a
* key in a hash table in the BufferPool, for example.)
* @see BufferPool
*/
public int hashCode() {
// some code goes here
int result = 17;
result = 31*result+tableId;
result = 31*result+pgNo;
return result;
}
/**
* Compares one PageId to another.
*
* @param o The object to compare against (must be a PageId)
* @return true if the objects are equal (e.g., page numbers and table
* ids are the same)
*/
public boolean equals(Object o) {
// some code goes here
if (o==null)return false;
if (getClass()!=o.getClass()){
return false;
}
if (o==this)return true;
HeapPageId heapPageId = (HeapPageId) o;
return heapPageId.pgNo==this.pgNo&&heapPageId.tableId==this.tableId;
}
/**
* Return a representation of this object as an array of
* integers, for writing to disk. Size of returned array must contain
* number of integers that corresponds to number of args to one of the
* constructors.
*/
public int[] serialize() {
int data[] = new int[2];
data[0] = getTableId();
data[1] = pageNumber();
return data;
}
}
Record.java我在之前练习一的时候无意中写了
再放上HeapPage.java
public class HeapPage implements Page {
HeapPageId pid;
TupleDesc td;
byte header[];
Tuple tuples[];
int numSlots;
byte[] oldData;
/**
* Create a HeapPage from a set of bytes of data read from disk.
* The format of a HeapPage is a set of header bytes indicating
* the slots of the page that are in use, some number of tuple slots.
* Specifically, the number of tuples is equal to: <p>
* floor((BufferPool.PAGE_SIZE*8) / (tuple size * 8 + 1))
* <p> where tuple size is the size of tuples in this
* database table, which can be determined via {@link Catalog#getTupleDesc}.
* The number of 8-bit header words is equal to:
* <p>
* ceiling(no. tuple slots / 8)
* <p>
* @see Database#getCatalog
* @see Catalog#getTupleDesc
* @see BufferPool#PAGE_SIZE
*/
public HeapPage(HeapPageId id, byte[] data) throws IOException {
this.pid = id;
this.td = Database.getCatalog().getTupleDesc(id.getTableId());
this.numSlots = getNumTuples();
//System.out.println(getNumTuples());
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data));
// allocate and read the header slots of this page
header = new byte[getHeaderSize()];
for (int i=0; i<header.length; i++)
header[i] = dis.readByte();
try{
// allocate and read the actual records of this page
tuples = new Tuple[numSlots];//注意每个都被new了
for (int i=0; i<tuples.length; i++) {
tuples[i] = readNextTuple(dis, i);
}
}catch(NoSuchElementException e){
e.printStackTrace();
}
dis.close();
setBeforeImage();
}
/** Retrieve the number of tuples on this page.
@return the number of tuples on this page
*/
private int getNumTuples() {
// some code goes here
return (int) Math.floor((BufferPool.PAGE_SIZE*8)/(td.getSize()*8+1));
}
/**
* Computes the number of bytes in the header of a page in a HeapFile with each tuple occupying tupleSize bytes
* @return the number of bytes in the header of a page in a HeapFile with each tuple occupying tupleSize bytes
*/
private int getHeaderSize() {
return (int) Math.ceil((int) Math.floor((BufferPool.PAGE_SIZE*8)/(td.getSize()*8+1))/8);
}
/** Return a view of this page before it was modified
-- used by recovery */
public HeapPage getBeforeImage(){
try {
return new HeapPage(pid,oldData);
} catch (IOException e) {
e.printStackTrace();
//should never happen -- we parsed it OK before!
System.exit(1);
}
return null;
}
public void setBeforeImage() {
oldData = getPageData().clone();
}
/**
* @return the PageId associated with this page.
*/
public HeapPageId getId() {
return pid;
// some code goes here
}
/**
* Suck up tuples from the source file.
*/
private Tuple readNextTuple(DataInputStream dis, int slotId) throws NoSuchElementException {
// if associated bit is not set, read forward to the next tuple, and
// return null.
if (isSlotUsed(slotId)) {
for (int i=0; i<td.getSize(); i++) {
try {
int t = 0;
dis.readByte();
} catch (IOException e) {
throw new NoSuchElementException("error reading empty tuple");
}
}
return null;
}
// read fields in the tuple
Tuple t = new Tuple(td);
RecordId rid = new RecordId(pid, slotId);
t.setRecordId(rid);
try {
for (int j=0; j<td.numFields(); j++) {
Field f = td.getFieldType(j).parse(dis);
t.setField(j, f);
}
} catch (java.text.ParseException e) {
e.printStackTrace();
throw new NoSuchElementException("parsing error!");
}
return t;
}
/**
* Generates a byte array representing the contents of this page.
* Used to serialize this page to disk.
* <p>
* The invariant here is that it should be possible to pass the byte
* array generated by getPageData to the HeapPage constructor and
* have it produce an identical HeapPage object.
*
* @see #HeapPage
* @return A byte array correspond to the bytes of this page.
*/
public byte[] getPageData() {
int len = BufferPool.PAGE_SIZE;
ByteArrayOutputStream baos = new ByteArrayOutputStream(len);
DataOutputStream dos = new DataOutputStream(baos);
// create the header of the page
for (int i=0; i<header.length; i++) {
try {
dos.writeByte(header[i]);
} catch (IOException e) {
// this really shouldn't happen
e.printStackTrace();
}
}
// create the tuples
for (int i=0; i<tuples.length; i++) {
// empty slot
if (!isSlotUsed(i)) {//这里我好像改过判断。。需要注意,在下面有函数需要补全,很关键
for (int j=0; j<td.getSize(); j++) {
try {
dos.writeByte(0);
} catch (IOException e) {
e.printStackTrace();
}
}
continue;
}
// non-empty slot
for (int j=0; j<td.numFields(); j++) {
Field f = tuples[i].getField(j);
try {
f.serialize(dos);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// padding
int zerolen = BufferPool.PAGE_SIZE - (header.length + td.getSize() * tuples.length); //- numSlots * td.getSize();
byte[] zeroes = new byte[zerolen];
try {
dos.write(zeroes, 0, zerolen);
} catch (IOException e) {
e.printStackTrace();
}
try {
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
/**
* Static method to generate a byte array corresponding to an empty
* HeapPage.
* Used to add new, empty pages to the file. Passing the results of
* this method to the HeapPage constructor will create a HeapPage with
* no valid tuples in it.
*
* @return The returned ByteArray.
*/
public static byte[] createEmptyPageData() {
int len = BufferPool.PAGE_SIZE;
return new byte[len]; //all 0
}
/**
* Delete the specified tuple from the page; the tuple should be updated to reflect
* that it is no longer stored on any page.
* @throws DbException if this tuple is not on this page, or tuple slot is
* already empty.
* @param t The tuple to delete
*/
public void deleteTuple(Tuple t) throws DbException {
// some code goes here
// not necessary for lab1
}
/**
* Adds the specified tuple to the page; the tuple should be updated to reflect
* that it is now stored on this page.
* @throws DbException if the page is full (no empty slots) or tupledesc
* is mismatch.
* @param t The tuple to add.
*/
public void insertTuple(Tuple t) throws DbException {
// some code goes here
// not necessary for lab1
}
/**
* Marks this page as dirty/not dirty and record that transaction
* that did the dirtying
*/
public void markDirty(boolean dirty, TransactionId tid) {
// some code goes here
// not necessary for lab1
}
/**
* Returns the tid of the transaction that last dirtied this page, or null if the page is not dirty
*/
public TransactionId isDirty() {
// some code goes here
// Not necessary for lab1
return null;
}
/**
* Returns the number of empty slots on this page.
*/
public int getNumEmptySlots() {//判断当前tuples[i]是否装了值
// some code goes here
Tuple newT = new Tuple(td);
int count =0;
for (int i=0;i<tuples.length;i++){
if (tuples[i].tuple.equals(newT.tuple)) {
count++;
}
}
return count;
}
/**
* Returns true if associated slot on this page is filled.
*/
public boolean isSlotUsed(int i) {
// some code goes here
Tuple newT = new Tuple(td);
if (tuples[i]==null||tuples[i].tuple.equals(newT.tuple))return false;
else{return true;}
}
/**
* Abstraction to fill or clear a slot on this page.
*/
private void markSlotUsed(int i, boolean value) {
// some code goes here
// not necessary for lab1
}
/**
* @return an iterator over all tuples on this page (calling remove on this iterator throws an UnsupportedOperationException)
* (note that this iterator shouldn't return tuples in empty slots!)
*/
public Iterator<Tuple> iterator() {
// some code goes here
return new Iterator<Tuple>() {//常规写法,但注意要tuples都是被new了的,所以还需要计算一下赋了值的数量
private int index=0;
@Override
public boolean hasNext() {
Tuple newT = new Tuple(td);
int count =0;
for (int i=0;i<tuples.length;i++){
if (tuples[i].tuple.equals(newT.tuple)) {
count++;
}
}
return index<numSlots-count;
}
@Override
public Tuple next() {
int count = index;
index++;
return tuples[count];
}
};
}
}