1.RecordFile
从RecordFile的commit方法部分代码:
if (transactionsDisabled) {
long offset = node.getBlockId() * BLOCK_SIZE;
file.seek(offset);
file.write(node.getData());
node.setClean();
free.add(node);
}
可以断定,File被均匀(大小都为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,即如果你要往里面
写数据你就直接写好了。先New一个BlockIo,往BlockIo里面填值以后,你要close或者
commit,那么就把BlockIo里面的数据写到这个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实现了对Page的管理,包括对Page的allocate和free,获取第一个和最后一个Page,一个特定Page的前后Page,
以及RecordFile的commit和rollback。
4.PhysicalRow
PhysicalRowId,FreePhysicalRowId,FreePhysicalRowIdPage和FreePhysicalRowIdPageManager对BLockIo进行更细粒度的管理。每一个BlockIo被分成一个个PhysicalRowId(Id这个取名有歧义,其实就是BlockIo的一部分),
当然一个FreePhysicalRowId也可能跨越几个BlockIo。PhysicalRowId包含一个
Block Number和一个Offset,FreePhysicalRowId还有一个size来表示这块PhysicalRowId的size是多少。
FreePhysicalRowIdPage继承PageHeader,持有ELEMS_PER_PAGE(583)个FreePhysicalRowId供分配或者释放管理:
// slots we returned.
FreePhysicalRowId[] slots = new FreePhysicalRowId[ELEMS_PER_PAGE];
为了记录分配的个数,有一个setCount方法。和PageHeader将BlockIo封装起来并且维护前后的BlockIo不同,
FreePhysicalRowIdPage维护free的PhysicalRowId,这些Id是以一个数组组织起来的。相同的是都对BlockIo
进行封装:
BlockIo curBlock = _file.get(freePage);
FreePhysicalRowIdPage fp = FreePhysicalRowIdPage
.getFreePhysicalRowIdPageView(curBlock);
int slot = fp.getFirstFree();
if (slot != -1) {
free = fp.alloc(slot); //这里调用了FreePhysicalRowIdPage的setCount操作,
//这个count会写到BlockIo里面进行记录
break;
}
_file.release(curBlock);
从这里可以看到:BlockIo除了被PageHeader封装外,还会被FreePhysicalRowIdPage封装,只是FreePhysicalRowIdPage
可能不会封装每一个BlockIo,只是free的这块而已。
这些FreePhysicalRowId拥有同样的BlockIo,offset具有一定规律:
/** Returns the value of the indicated slot */
FreePhysicalRowId get(int slot) {
if (slots[slot] == null)
slots[slot] = new FreePhysicalRowId(block, slotToOffset(slot)); //这时的offset只是区分,没有实际意思
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 );
当更新的时候,数据量变大了,那么前面的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() );
}
最后调用的是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对象进行了设置,以供后面使用,见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根据FreePhysicalRowId进行设置
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的构造方法如下:
/**
* 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)