1.RecordFile
從RecordFile的commit方法部分代碼:
if (transactionsDisabled) {
long offset = node.getBlockId() * BLOCK_SIZE;
file.seek(offset);
file.write(node.getData());
node.setClean();
free.add(node);
}
可以斷定,F(xiàn)ile被均勻(大小都為BLOCK_SIZE=8192)的分成N個BlockIo。每一個
BlockIo都有一個BlockId,BlockId是從0開始順序遞增的,這樣只要知道這個BlockId
也就可以知道BlockIo的在RecordFile的位置了。
-----------------------------------------------------------------
|0 BlockIo |8192
-----------------------------------------------------------------
|1 BlockIo |8192
-----------------------------------------------------------------
|2 BlockIo |8192
-----------------------------------------------------------------
.
.
.
-----------------------------------------------------------------
|n BlockIo |8192
-----------------------------------------------------------------
從getNewNode方法可以知道,BlockIo不是從RecordFile里面生成出來的,而是
直接new一個出來。因為RecordFile持有的是RandomAccessFile,即如果你要往里面
寫數(shù)據(jù)你就直接寫好了。先New一個BlockIo,往BlockIo里面填值以后,你要close或者
commit,那么就把BlockIo里面的數(shù)據(jù)寫到這個RandomAccessFile里面就是了。
private BlockIo getNewNode(long blockid)
throws IOException
{
BlockIo retval = null;
if (!free.isEmpty()) {
retval = (BlockIo) free.removeFirst();
}
if (retval == null)
retval = new BlockIo(0, new byte[BLOCK_SIZE]);
retval.setBlockId(blockid);
retval.setView(null);
return retval;
}
2.PageHeader
Pageheader對每一個(應該是除了第0個,第0個被FileHeader封裝)BlockIo封裝,PageHeader還維護
著前一個和后一個BlockIo,即通過PageHeader可以將所有的BlockIo串起來。被PageHeader封裝的BlockIo
也就被稱為一個Page
3.PageManager
PageManager實現(xiàn)了對Page的管理,包括對Page的allocate和free,獲取第一個和最后一個Page,一個特定Page的前后Page,
以及RecordFile的commit和rollback。
4.PhysicalRow
PhysicalRowId,F(xiàn)reePhysicalRowId,F(xiàn)reePhysicalRowIdPage和FreePhysicalRowIdPageManager對BLockIo進行更細粒度的管理。每一個BlockIo被分成一個個PhysicalRowId(Id這個取名有歧義,其實就是BlockIo的一部分),
當然一個FreePhysicalRowId也可能跨越幾個BlockIo。PhysicalRowId包含一個
Block Number和一個Offset,F(xiàn)reePhysicalRowId還有一個size來表示這塊PhysicalRowId的size是多少。
FreePhysicalRowIdPage繼承PageHeader,持有ELEMS_PER_PAGE(583)個FreePhysicalRowId供分配或者釋放管理:
// slots we returned.
FreePhysicalRowId[] slots = new FreePhysicalRowId[ELEMS_PER_PAGE];
為了記錄分配的個數(shù),有一個setCount方法。和PageHeader將BlockIo封裝起來并且維護前后的BlockIo不同,
FreePhysicalRowIdPage維護free的PhysicalRowId,這些Id是以一個數(shù)組組織起來的。相同的是都對BlockIo
進行封裝:
BlockIo curBlock = _file.get(freePage);
FreePhysicalRowIdPage fp = FreePhysicalRowIdPage
.getFreePhysicalRowIdPageView(curBlock);
int slot = fp.getFirstFree();
if (slot != -1) {
free = fp.alloc(slot); //這里調(diào)用了FreePhysicalRowIdPage的setCount操作,
//這個count會寫到BlockIo里面進行記錄
break;
}
_file.release(curBlock);
從這里可以看到:BlockIo除了被PageHeader封裝外,還會被FreePhysicalRowIdPage封裝,只是FreePhysicalRowIdPage
可能不會封裝每一個BlockIo,只是free的這塊而已。
這些FreePhysicalRowId擁有同樣的BlockIo,offset具有一定規(guī)律:
/** Returns the value of the indicated slot */
FreePhysicalRowId get(int slot) {
if (slots[slot] == null)
slots[slot] = new FreePhysicalRowId(block, slotToOffset(slot)); //這時的offset只是區(qū)分,沒有實際意思
return slots[slot];
}
/** Converts slot to offset */
short slotToOffset(int slot) {
return (short) (O_FREE +
(slot * FreePhysicalRowId.SIZE)); //即相差FreePhysicalRowId.SIZE,14個byte
}
FreePhysicalRowId的使用大致是這樣的:
byte[] data = TestUtil.makeRecord(10000, (byte) 1);
Location loc = physMgr.insert( data, 0, data.length );
data = TestUtil.makeRecord(20000, (byte) 2);
Location loc2 = physMgr.update(loc, data, 0, data.length );
當更新的時候,數(shù)據(jù)量變大了,那么前面的10000byte容量放不下20000個,那么就釋放掉10000這塊容量:
free( loc )具體代碼如下:
free( Location id )
throws IOException
{
// get the rowid, and write a zero current size into it.
BlockIo curBlock = file.get( id.getBlock() );
DataPage curPage = DataPage.getDataPageView( curBlock );
RecordHeader hdr = new RecordHeader( curBlock, id.getOffset() );
hdr.setCurrentSize( 0 );
file.release( id.getBlock(), true );
// write the rowid to the free list
freeman.put( id, hdr.getAvailableSize() );
}
最后調(diào)用的是FreePhysicalRowIdPageManager的put方法:
/**
* Puts the indicated rowid on the free list
*/
void put(Location rowid, int size) throws IOException {
FreePhysicalRowId free = null;
PageCursor curs = new PageCursor(_pageman, Magic.FREEPHYSIDS_PAGE);
long freePage = 0;
while (curs.next() != 0) {
freePage = curs.getCurrent();
BlockIo curBlock = _file.get(freePage);
FreePhysicalRowIdPage fp = FreePhysicalRowIdPage
.getFreePhysicalRowIdPageView(curBlock);
int slot = fp.getFirstFree();
if (slot != -1) {
free = fp.alloc(slot);
break;
}
_file.release(curBlock);
}
if (free == null) {
// No more space on the free list, add a page.
freePage = _pageman.allocate(Magic.FREEPHYSIDS_PAGE);
BlockIo curBlock = _file.get(freePage);
FreePhysicalRowIdPage fp = FreePhysicalRowIdPage
.getFreePhysicalRowIdPageView(curBlock);
free = fp.alloc(0);
}
free.setBlock(rowid.getBlock());
free.setOffset(rowid.getOffset());
free.setSize(size);
_file.release(freePage, true);
}
最后幾行代碼就是對FreePhysicalRowId對象進行了設(shè)置,以供后面使用,見PhysicalRowIdManager的alloc方法:
alloc( int size )
throws IOException
{
Location retval = freeman.get( size );
if ( retval == null ) {
retval = allocNew( size, pageman.getLast( Magic.USED_PAGE ) );
}
return retval;
}
FreePhysicalRowIdPageManager的get( size )方法有如下代碼:
int slot = fp.getFirstLargerThan(size)
retval = new Location(fp.get(slot)); //fp.get(slot)返回一個FreePhysicalRowId,Location根據(jù)FreePhysicalRowId進行設(shè)置
return retval;
FreePhysicalRowIdPage的getFirstLargerThan(size):
/**
* Returns first slot with available size >= indicated size, or -1 if no
* slots are available.
**/
int getFirstLargerThan(int size) {
for (int i = 0; i < ELEMS_PER_PAGE; i++) {
if (isAllocated(i) && get(i).getSize() >= size) //getSize會取到前面的setSize的值
return i;
}
return -1;
}
Location的構(gòu)造方法如下:
/**
* Creates a location based on the data of the physical rowid.
*/
Location(PhysicalRowId src) {
block = src.getBlock();
offset = src.getOffset();
}
FreePhysicalRowIdPageManager只有兩個方法,是對FreePhysicalRowIdPage進行管理的。類似于
/**
* Returns a free physical rowid of the indicated size, or null if nothing
* was found.
*/
Location get(int size)
/**
* Puts the indicated rowid on the free list
*/
void put(Location rowid, int size)