/// <summary> /// 申请一个空白页或新页。 /// </summary> /// <param name="db"></param> /// <returns></returns> private static PageHeaderBlock AllocEmptyPageOrNewPage(this FileDBContext db) { PageHeaderBlock page; FileStream fs = db.fileStream; DBHeaderBlock dbHeader = db.headerBlock; long emptyPagePos = dbHeader.FirstEmptyPagePos; if (emptyPagePos != 0)// 存在空白页,则使用此空白页。 { PageHeaderBlock emptyPage = fs.ReadBlock <PageHeaderBlock>(emptyPagePos); // 从链表中去掉此空白页。 dbHeader.FirstEmptyPagePos = emptyPage.NextPagePos; emptyPage.NextPagePos = 0; page = emptyPage; } else// 没有空白页,则拓展数据库文件,增加一个页的长度。 { PageHeaderBlock newPage = new PageHeaderBlock(); newPage.ThisPos = fs.Length; newPage.OccupiedBytes = (Int16)(Consts.pageSize - Consts.maxAvailableSpaceInPage); newPage.AvailableBytes = Consts.maxAvailableSpaceInPage; fs.SetLength(fs.Length + Consts.pageSize); page = newPage; } return(page); }
public static long GetPosOfFirstPage(this DBHeaderBlock dbHeaderBlock, AllocPageTypes type) { long position; switch (type) { case AllocPageTypes.Table: position = dbHeaderBlock.FirstTablePagePos; break; case AllocPageTypes.Index: position = dbHeaderBlock.FirstIndexPagePos; break; case AllocPageTypes.SkipListNode: position = dbHeaderBlock.FirstSkipListNodePagePos; break; case AllocPageTypes.Data: position = dbHeaderBlock.FirstDataPagePos; break; default: throw new NotImplementedException(); } return(position); }
//static readonly Brush dbHeaderBrush = new LinearGradientBrush(new Rectangle(0, 0, Consts.dbHeaderBlockLength, bmpHeight), Color.Silver, Color.GreenYellow, 270f); private static void DrawDBHeader(FileDBContext db, Bitmap[] bmps) { DBHeaderBlock dbHeader = db.GetDBHeaderBlock(); long index = 0; Graphics g = Graphics.FromImage(bmps[index]); int startPos = (int)(dbHeader.ThisPos - Consts.pageSize * index); int length = dbHeader.ToBytes().Length; Brush brush = new LinearGradientBrush(new Point(startPos, 0), new Point(startPos + length, 0), Color.Purple, Color.Aqua); g.FillRectangle(brush, startPos, 1, length, bmpHeight - 1); g.DrawRectangle(boundPen, startPos, 1, length, bmpHeight - 1); g.Dispose(); }
private static PageHeaderBlock PickPage(this FileDBContext db, Int16 allocatingLength, AllocPageTypes type) { PageHeaderBlock page; FileStream fs = db.fileStream; Transaction ts = db.transaction; DBHeaderBlock dbHeader = db.headerBlock; // 找到第一个给定类型的页的位置。 long pagePos = dbHeader.GetPosOfFirstPage(type); if (pagePos == 0)// 尚无给定类型的页。 { page = db.AllocEmptyPageOrNewPage(); page.AvailableBytes -= allocatingLength; page.OccupiedBytes += allocatingLength; dbHeader.SetPosOfFirstPage(type, page.ThisPos); } else { // 最前面的table页的可用空间是最大的(这需要在后续操作中进行排序) PageHeaderBlock firstTablePage = GetTablePage(fs, ts, pagePos); if (firstTablePage.AvailableBytes >= allocatingLength)// 此页的空间足够用。 { // 把此页从Page链表中移除。 dbHeader.SetPosOfFirstPage(type, firstTablePage.NextPagePos); firstTablePage.NextPagePos = 0; page = firstTablePage; } else// 此页的空间不够用,还是要申请一个新页。 { page = db.AllocEmptyPageOrNewPage(); } page.AvailableBytes -= allocatingLength; page.OccupiedBytes += allocatingLength; // 对申请的类型的页的链表进行排序。呼应上面的排序需求。 SortPage(type, page, fs, ts, dbHeader); } return(page); }
/// <summary> /// 创建初始状态的数据库文件。 /// </summary> /// <param name="fullname">数据库文件据对路径。</param> /// <param name="config">数据库配置信息。</param> private void CreateDB(string fullname, DBConfig config) { FileInfo fileInfo = new FileInfo(fullname); Directory.CreateDirectory(fileInfo.DirectoryName); using (FileStream fs = new FileStream(fullname, FileMode.CreateNew, FileAccess.Write, FileShare.None, Consts.pageSize)) { PageHeaderBlock page = new PageHeaderBlock() { OccupiedBytes = Consts.pageSize, AvailableBytes = 0, }; fs.WriteBlock(page); DBHeaderBlock dbHeader = new DBHeaderBlock() { MaxLevelOfSkipList = config.MaxLevelOfSkipList, ProbabilityOfSkipList = config.ProbabilityOfSkipList, MaxSunkCountInMemory = config.MaxSunkCountInMemory, LockTimeout = config.LockTimeout, ThisPos = fs.Position }; fs.WriteBlock(dbHeader); TableBlock tableHead = new TableBlock() { ThisPos = fs.Position, }; fs.WriteBlock(tableHead); byte[] leftSpace = new byte[Consts.pageSize - fs.Length]; fs.Write(leftSpace, 0, leftSpace.Length); BlockCache.TryRemoveFloatingBlock(page); BlockCache.TryRemoveFloatingBlock(dbHeader); BlockCache.TryRemoveFloatingBlock(tableHead); BlockCache.TryRemoveSunkBlock(page); BlockCache.TryRemoveSunkBlock(dbHeader); BlockCache.TryRemoveSunkBlock(tableHead); } }
public static void SetPosOfFirstPage(this DBHeaderBlock dbHeaderBlock, AllocPageTypes type, long value) { switch (type) { case AllocPageTypes.Table: dbHeaderBlock.FirstTablePagePos = value; break; case AllocPageTypes.Index: dbHeaderBlock.FirstIndexPagePos = value; break; case AllocPageTypes.SkipListNode: dbHeaderBlock.FirstSkipListNodePagePos = value; break; case AllocPageTypes.Data: dbHeaderBlock.FirstDataPagePos = value; break; default: throw new NotImplementedException(); } }
/// <summary> /// 打印数据库文件里所有表的所有主键的skip list和所有类型的页链表。 /// </summary> /// <param name="db"></param> /// <returns></returns> public static string Print(this FileDBContext db) { StringBuilder builder = new StringBuilder(); builder.AppendLine(db.Fullname); FileStream fs = db.GetFileStream(); //PageHeaderBlock firstPage = fs.ReadBlock<PageHeaderBlock>(0); DBHeaderBlock dbHeader = db.GetDBHeaderBlock();// fs.ReadBlock<DBHeaderBlock>(fs.Position); PrintSkipLists(db, builder, fs); builder.AppendLine(); builder.AppendLine("Page Info.:"); for (long i = 0; i < fs.Length; i += Consts.pageSize) { PageHeaderBlock pageInfo = fs.ReadBlock <PageHeaderBlock>(i); string str = Print(pageInfo); builder.AppendLine(str); } builder.AppendLine(); builder.AppendLine(); builder.AppendLine("table pages:"); long tablePos = dbHeader.FirstTablePagePos; while (tablePos != 0) { PageHeaderBlock pageInfo = fs.ReadBlock <PageHeaderBlock>(tablePos); builder.Append(string.Format(" {0}[{1}] ->", pageInfo.ThisPos, pageInfo.ThisPos / Consts.pageSize)); tablePos = pageInfo.NextPagePos; } builder.AppendLine(); builder.AppendLine(); builder.AppendLine("index pages:"); long indexPos = dbHeader.FirstIndexPagePos; while (indexPos != 0) { PageHeaderBlock pageInfo = fs.ReadBlock <PageHeaderBlock>(indexPos); builder.Append(string.Format(" {0}[{1}] ->", pageInfo.ThisPos, pageInfo.ThisPos / Consts.pageSize)); indexPos = pageInfo.NextPagePos; } builder.AppendLine(); builder.AppendLine(); builder.AppendLine("skip list node pages:"); long skiplistnodePos = dbHeader.FirstSkipListNodePagePos; while (skiplistnodePos != 0) { PageHeaderBlock pageInfo = fs.ReadBlock <PageHeaderBlock>(skiplistnodePos); builder.Append(string.Format(" {0}[{1}] ->", pageInfo.ThisPos, pageInfo.ThisPos / Consts.pageSize)); skiplistnodePos = pageInfo.NextPagePos; } builder.AppendLine(); builder.AppendLine(); builder.AppendLine("data block pages:"); long dataBlockPos = dbHeader.FirstDataPagePos; while (dataBlockPos != 0) { PageHeaderBlock pageInfo = fs.ReadBlock <PageHeaderBlock>(dataBlockPos); builder.Append(string.Format(" {0}[{1}] ->", pageInfo.ThisPos, pageInfo.ThisPos / Consts.pageSize)); dataBlockPos = pageInfo.NextPagePos; } builder.AppendLine(); builder.AppendLine(); builder.AppendLine("empty pages:"); long emptyPos = dbHeader.FirstEmptyPagePos; while (emptyPos != 0) { PageHeaderBlock pageInfo = fs.ReadBlock <PageHeaderBlock>(emptyPos); builder.Append(string.Format(" {0}[{1}] ->", pageInfo.ThisPos, pageInfo.ThisPos / Consts.pageSize)); emptyPos = pageInfo.NextPagePos; } builder.AppendLine(); return(builder.ToString()); }
/// <summary> /// 根据数据库文件初始化<see cref="FileDBContext"/>。 /// </summary> /// <param name="fullname">数据库文件据对路径。</param> /// <param name="read">只读方式打开。</param> private void InitializeDB(string fullname, bool read) { // 尝试恢复数据库文件。 string journalFilename = this.JournalFileName; if (File.Exists(journalFilename)) { using (FileStream journal = new FileStream(journalFilename, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { using (FileStream fs = new FileStream(fullname, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { while (journal.Position < journal.Length) { object obj = Consts.formatter.Deserialize(journal); Block block = obj as Block; fs.WriteBlock(block); } } } File.Delete(journalFilename); } // 准备各项工作。 // 准备数据库文件流。 FileStream fileStream = null; if (read) { fileStream = new FileStream(fullname, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); this.fileStream = fileStream; } else { fileStream = new FileStream(fullname, FileMode.Open, FileAccess.ReadWrite, FileShare.Read); this.fileStream = fileStream; } // 准备数据库头部块。 //PageHeaderBlock pageHeaderBlock = fileStream.ReadBlock<PageHeaderBlock>(0); DBHeaderBlock headerBlock = fileStream.ReadBlock <DBHeaderBlock>(Consts.pageHeaderBlockLength); #if DEBUG Block.IDCounter = headerBlock.BlockCount; #endif BlockCache.MaxSunkCountInMemory = headerBlock.MaxSunkCountInMemory; this.headerBlock = headerBlock; // 准备数据库表块,保存到字典。 TableBlock currentTableBlock = fileStream.ReadBlock <TableBlock>(Consts.pageHeaderBlockLength + Consts.dbHeaderBlockLength); this.tableBlockHead = currentTableBlock; while (currentTableBlock.NextPos != 0) { TableBlock tableBlock = fileStream.ReadBlock <TableBlock>(currentTableBlock.NextPos); currentTableBlock.NextObj = tableBlock; Dictionary <string, IndexBlock> indexDict = GetIndexDict(fileStream, tableBlock); this.tableIndexBlockDict.Add(tableBlock.TableType, indexDict); this.tableBlockDict.Add(tableBlock.TableType, tableBlock); currentTableBlock = tableBlock; } }
/// <summary> /// 系统启动时初始化各项常量。 /// </summary> static Consts() { { PropertyInfo[] properties = typeof(Table).GetProperties(); foreach (var property in properties) { TableIndexAttribute attr = property.GetCustomAttribute <TableIndexAttribute>(); if (attr != null && property.PropertyType == typeof(ObjectId)) { Consts.TableIdString = property.Name; break; } } } { PageHeaderBlock page = new PageHeaderBlock(); using (MemoryStream ms = new MemoryStream()) { formatter.Serialize(ms, page); if (ms.Length > Consts.pageSize / 10) { throw new Exception("Page header block takes too much space!"); } Consts.pageHeaderBlockLength = (Int16)ms.Length; Consts.maxAvailableSpaceInPage = (Int16)(Consts.pageSize - ms.Length); Consts.minOccupiedBytes = (Int16)(Consts.pageSize - Consts.maxAvailableSpaceInPage); } BlockCache.TryRemoveFloatingBlock(page); } { PageHeaderBlock page = new PageHeaderBlock(); DBHeaderBlock dbHeader = new DBHeaderBlock(); TableBlock tableHead = new TableBlock(); Int16 usedSpaceInFirstPage; using (MemoryStream ms = new MemoryStream()) { formatter.Serialize(ms, page); dbHeader.ThisPos = ms.Length; formatter.Serialize(ms, dbHeader); tableHead.ThisPos = ms.Length; Consts.dbHeaderBlockLength = (Int16)(ms.Length - Consts.pageHeaderBlockLength); formatter.Serialize(ms, tableHead); Consts.tableBlockLength = (Int16)(ms.Length - tableHead.ThisPos); usedSpaceInFirstPage = (Int16)ms.Length; } if (usedSpaceInFirstPage > Consts.pageSize) { throw new Exception("First page is full!"); } BlockCache.TryRemoveFloatingBlock(page); BlockCache.TryRemoveFloatingBlock(dbHeader); BlockCache.TryRemoveFloatingBlock(tableHead); } { IndexBlock block = new IndexBlock(); int length = block.ToBytes().Length; if (length > Consts.pageSize / 10) { throw new Exception("index block takes too much space!"); } Consts.indexBlockLength = (Int16)length; BlockCache.TryRemoveFloatingBlock(block); } { SkipListNodeBlock block = new SkipListNodeBlock(); int length = block.ToBytes().Length; if (length > Consts.pageSize / 10) { throw new Exception("index block takes too much space!"); } Consts.skipListNodeBlockLength = (Int16)length; BlockCache.TryRemoveFloatingBlock(block); } { PageHeaderBlock page = new PageHeaderBlock(); DataBlock dataBlock = new DataBlock(); dataBlock.Data = new byte[0]; Int16 minValue; using (MemoryStream ms = new MemoryStream()) { formatter.Serialize(ms, page); long pos = ms.Length; formatter.Serialize(ms, dataBlock); Consts.dataBlockLength = (Int16)(ms.Length - pos); if (ms.Length > Consts.pageSize / 10) { throw new Exception("data block's metadata takes too much space!"); } minValue = (Int16)ms.Length; } Consts.maxDataBytes = (Int16)(Consts.pageSize - minValue); BlockCache.TryRemoveFloatingBlock(page); BlockCache.TryRemoveFloatingBlock(dataBlock); } }
private void DoCommit() { DBHeaderBlock dbHeaderBlock = this.fileDBContext.headerBlock; FileStream fs = this.fileDBContext.fileStream; // 给所有的块安排数据库文件中的位置。 List <Block> arrangedBlocks = new List <Block>(); while (arrangedBlocks.Count < this.addlingBlockList.Count) { for (int i = this.addlingBlockList.Count - 1; i >= 0; i--)// 后加入列表的先处理。 { AllocBlock block = this.addlingBlockList[i]; if (arrangedBlocks.Contains(block)) { continue; } bool done = block.ArrangePos(); if (done) { if (block.ThisPos == 0) { byte[] bytes = block.ToBytes(); if (bytes.Length > Consts.maxAvailableSpaceInPage) { throw new Exception("Block size is toooo large!"); } AllocPageTypes pageType = block.BelongedPageType(); IList <AllocatedSpace> spaces = this.fileDBContext.Alloc(bytes.LongLength, pageType); block.ThisPos = spaces[0].position; } arrangedBlocks.Add(block); } } } // 根据要删除的块,更新文件头。 foreach (var block in this.deletingBlockList) { long pagePos = block.ThisPos.PagePos(); PageHeaderBlock page; if (!this.affectedPages.TryGetValue(pagePos, out page)) { page = fs.ReadBlock <PageHeaderBlock>(pagePos); this.affectedPages.Add(pagePos, page); } byte[] bytes = block.ToBytes(); Int16 length = (Int16)bytes.Length; if (length > Consts.maxAvailableSpaceInPage) { throw new Exception(string.Format("Block [{0}]'s serialized bytes is more than {1}", block, Consts.maxAvailableSpaceInPage)); } page.OccupiedBytes -= length; if (page.OccupiedBytes < Consts.minOccupiedBytes) { throw new Exception(string.Format("DB Error: {0}'s Occupied bytes is less than {1}", page, Consts.minOccupiedBytes)); } if (page.OccupiedBytes == Consts.minOccupiedBytes)// 此页已成为新的空白页。 { // page 加入空白页链表。 //DBHeaderBlock dbHeader = this.fileDBContext.headerBlock; page.NextPagePos = dbHeaderBlock.FirstEmptyPagePos; dbHeaderBlock.FirstEmptyPagePos = page.ThisPos; } } // 准备恢复文件。 string journalFilename = this.fileDBContext.JournalFileName; FileStream journalFile = new FileStream(journalFilename, FileMode.Create, FileAccess.ReadWrite, FileShare.None, Consts.pageSize); foreach (Block block in this.addlingBlockList) { Consts.formatter.Serialize(journalFile, block); } foreach (PageHeaderBlock block in this.affectedPages.Values) { if (block.IsDirty) { Consts.formatter.Serialize(journalFile, block); } } if (dbHeaderBlock.IsDirty) { Consts.formatter.Serialize(journalFile, dbHeaderBlock); } journalFile.Flush(); // 写入所有的更改。 foreach (Block block in this.addlingBlockList) { fs.WriteBlock(block); } foreach (PageHeaderBlock block in this.affectedPages.Values) { if (block.IsDirty) { fs.WriteBlock(block); block.IsDirty = false; } } if (dbHeaderBlock.IsDirty) { fs.WriteBlock(dbHeaderBlock); dbHeaderBlock.IsDirty = false; } fs.Flush(); // 删除恢复文件。 journalFile.Close(); journalFile.Dispose(); File.Delete(journalFilename); // 恢复Transaction最初的状态。 this.addlingBlockList.Clear(); this.oldAddingBlockPositions.Clear(); this.deletingBlockList.Clear(); this.oldDeletingBlockPositions.Clear(); this.affectedPages.Clear(); //if (actUnlockDB == null) { actUnlockDB = new Action(UnlockDB); } UnlockDB(); this.level = 0; }