数据库在磁盘上的存储布局HeapFile
----《大规模分布式存储系统:原理解析与架构实战》读书笔记 这篇依然是学习《大规模分布式存储系统:原理解析与架构实战》一书之外的一个话题。通过学习本书,知道了分布式键值系统,通常使用SSTable(一个无序的键值对集合容器)作为其磁盘上的布局。这不禁
----《大规模分布式存储系统:原理解析与架构实战》读书笔记这篇依然是学习《大规模分布式存储系统:原理解析与架构实战》一书之外的一个话题。通过学习本书,知道了分布式键值系统,通常使用SSTable(一个无序的键值对集合容器)作为其磁盘上的布局。这不禁让人产生联想,传统数据库使用的是什么存储布局来存储数据呢?这就是今天要探讨的主题----HeapFile.
HeapFile是什么?
HeapFile是一种保存Page数据的数据结构,类似于链表,HeapFile也是一种无序容器。HeapFile和SSTable其实都是具有特殊结构的文件。既然都是保存数据,为什么不直接使用文件呢?因为系统文件并不区分文件的内容。处理起来粒度大。而HeapFile和SSTable都能够提供记录级别的管理,从这一点上来说,二者的功能都是相同的,都是为系统提供更细粒度的存储管理。
基本上,Oracle,MySql,PostgreSql,SQLServer等传统数据库都使用HeapFile作为其存储布局管理。如同SSTable一样,HeapFile的结构实际很简单,但是你需要时刻知道,数据库中存储使用的是HeapFile。
我们都知道,数据库通常使用B+树作为索引,但是国内很少有人提到数据库使用的是HeapFile来管理记录的存储。国外的一些大学在“数据库系统实现”这门课上通常会让学生实现一个简单的数据库,因此有不少HeapFile的资料。
基于Page的HeapFile
采用链表形式的是HeapFile如下:
Heap file和链表结构类似的地方:
支持增加(append)功能
支持大规模顺序扫描
不支持随机访问
这种方式的HeapFile在寻找具有合适空间的半空Page时需要遍历多个页,I/O开销大。因此一般常用的是采用基于索引的HeaFile.在HeapFile中使用一部分空间来存储Page作为索引,并记录对应Page的剩余量。如下:
像上图那样,索引单独存在一个page上。数据记录存在其他page上,如果有多个索引的page,则可以表示为:
下面是Heap file自有的一些特性:
数据保存在二级存储体(disk)中:Heapfile主要被设计用来高效存储大数据量,数据量的大小只受存储体容量限制;
Heapfile可以跨越多个磁盘空间或机器:heapfile可以用大地址结构去标识多个磁盘,甚至于多个网络;
数据被组织成页;
页可以部分为空(并不要求每个page必须装满);
页面可以被分割在某个存储体的不同的物理区域,也可以分布在不同的存储体上,甚至是不同的网络节点中。我们可以简单假设每一个page都有一个唯一的地址标识符PageAddress,并且操作系统可以根据PageAddress为我们定位该Page。
一般情况下,使用page在其所在文件中的偏移量就可以表示了。
一种简单的布局实现方案
File的布局
在实现数据在文件中的布局的时候,为了实现更简单,我先做了一个简单的约定:一个文件表示一个关系。
这意味着一个关系的记录的条数受到文件系统的限制,如果是FAT32位系统,一个文件最大只能是4G,如果是普通的etx3,单个文件则是2TB。
同样为了实现简单,采用了数组的方式来组织页。
HeapFile的组织如下:
其中N和P为文件的最开始的16(或32)个字节。即N和P实际保存的是两个long型的值。N表示文件中页的数目,P表示每页的大小。则:
文件的总大小 FileSize = N * P + 2 * sizoeof(long).任意一页的页首地址 Page(k) = P * ( k - 1 ) +2 * sizeof(long) (k = 1,2,...,N)
Page的布局
页中可以包含多条记录。如果每天记录的长度都相同,则称为定长记录,如果每条记录的长度有不相同,则称为变长记录。定长记录可以采用数组的方式记录,但是变长记录不行。因此采用偏移量的方式来记录。page的布局如下:
从页首开始一条条记录。页尾用一个int整形记录剩余空间的偏移量,再用一个Int整形该页已存储的记录数,每一条记录在页中的偏移量和是否被删除的标记。
其中,
FreeSpace表示该页空间剩余量的首地址,也是最后一条记录的尾地址+1;N表示该页中已经存在的记录的条数,包括哪些被标记为删除的记录;
尾部的R1,R2,..表示其对应记录在页内的偏移地址,同时还会分出1个bit位标记这条记录是否被删除。如果要支持记录跨页存储的话,还需要再分出2bit来标记其是否是跨页的记录。
尾部的R1,R2等可以定义为如下结构体:
struct IndexRecord { unsigned int pos:29; <em>//记录在页内的偏移地址</em> unsigned int isdelete:1; <em>//是否删除的标记</em> unsigned int spanned:2; <em>//是否跨页存储</em> };
则:
任意一条记录的IndexRecord首地址为 R(k) = P-(2+k)*sizeof(int); (k=1,2,..,N)
计算一个页还能容纳的长度为 FreeLength = P-(2+N)*sizeof(int)
判断一个页是否装满的条件为 FreeLength > 0
一个Page通常的大小为2K,4K,8K,16K等。
这里还要再提下空隙的问题,同时删除记录时直接采用标记法,但是当更新记录的时候,由于是变长记录。存在以下3种情况:
新记录和原记录一样长:原处更新记录即可新纪录比原记录长:原记录标记删除,并新增一条记录,如果有索引,更新索引文件。
新纪录变原记录短:原处更新记录,无需更新索引文件,但是出现了记录的空隙。
当空间紧张时,可以尝试压缩页,剔除其中的空隙。
记录的布局
定长记录的布局可以比较简单,此处不提。本节主要讨论变长记录的布局,也叫记录的序列化。
一个常见的例子为给定表Person的定义,使name可以是不超过1024个字符。Schema如下:
CREATE TABLE Person ( name VARCHAR(1024) NOT NULL, age INTEGER NOT NULL, birthdate DATETIME )
上面表的记录是变长的原因为:
name字段是一个变长的字符串;birthdate可以为NULL;
变长record的序列化的关键是字段边界的界定。一种比较流行的方法是在record的首部保存字段边界的offset。
Person的record的编排方式如下:
Note:我们在首部设置4个整型去存储三个字段的四个边界offset。
上面的编排方式很自然的提供一种NULL字段的编排方式--可以标识该字段的值为NULL,如下图:
第三个offset和第四个offset指向同一个位置,那么就表明第三个字段的大小是零,即是一个NULL值。
可以看到,使用偏移量无论是Page的布局,还是记录的序列化,都是非常方便的。
根据以上介绍, 可以有以下推断:
记录的总长度 RecordLength = R[k] k为字段数每个字段的长度为 ColnumLength(k) = R[k] - R[k-1] , (k=1,2,3,...)
判断一个字段是否为NULL ColnumLength[k] = 0 ,(k=1,2,3,...)
最后我们在来看一遍关系Person的HeapFile文件的整体布局图
参考
这里有一篇关于HeapFile的翻译 关系型数据在磁盘上的存储布局
原文来自http://dblab.cs.toronto.edu/courses/443/tas/
欢迎光临我的网站----蝴蝶忽然的博客园----人既无名的专栏。
如果阅读本文过程中有任何问题,请联系作者,转载请注明出处!

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











Windows 11 システムを初めて使用するユーザーの場合、通常、ファイルのダウンロード パスはシステムのデフォルト設定に従い、ファイルはシステム ディスクに保存されることが多く、長期的には占有してコンピューターの全体的なパフォーマンスに影響を与える可能性があります。システムディスク容量が多すぎます。記憶域スペースをより合理的に管理および割り当てるためには、Windows 11 システムでダウンロードしたファイルの保存ディスクをカスタマイズする方法を学ぶことが重要です。この記事では、ユーザーのパーソナライズされたストレージのニーズを満たすために、Windows 11 でダウンロードされたファイルのターゲット ディスクを柔軟に設定する方法について詳しく説明します。これが必要な場合は、読み続けてください。この実用的な機能の詳細な手順を紹介します。操作方法 1. デスクトップ上の「このPC」をダブルクリックして開きます。 2. 次に、左側の列で新しいインターフェイスを見つけます。

Apple の最新リリースの iOS18、iPadOS18、および macOS Sequoia システムでは、さまざまな理由で紛失または破損した写真やビデオをユーザーが簡単に回復できるように設計された重要な機能が写真アプリケーションに追加されました。この新機能では、写真アプリのツール セクションに「Recovered」というアルバムが導入され、ユーザーがデバイス上に写真ライブラリに含まれていない写真やビデオがある場合に自動的に表示されます。 「Recovered」アルバムの登場により、データベースの破損、カメラ アプリケーションが写真ライブラリに正しく保存されない、または写真ライブラリを管理するサードパーティ アプリケーションによって失われた写真やビデオに対する解決策が提供されます。ユーザーはいくつかの簡単な手順を実行するだけで済みます

Hibernate ポリモーフィック マッピングは、継承されたクラスをデータベースにマップでき、次のマッピング タイプを提供します。 join-subclass: 親クラスのすべての列を含む、サブクラス用の別個のテーブルを作成します。 table-per-class: サブクラス固有の列のみを含む、サブクラス用の別個のテーブルを作成します。 Union-subclass: join-subclass と似ていますが、親クラス テーブルがすべてのサブクラス列を結合します。

MySQLi を使用して PHP でデータベース接続を確立する方法: MySQLi 拡張機能を含める (require_once) 接続関数を作成する (functionconnect_to_db) 接続関数を呼び出す ($conn=connect_to_db()) クエリを実行する ($result=$conn->query()) 閉じる接続 ( $conn->close())

PHP でデータベース接続エラーを処理するには、次の手順を使用できます。 mysqli_connect_errno() を使用してエラー コードを取得します。 mysqli_connect_error() を使用してエラー メッセージを取得します。これらのエラー メッセージをキャプチャしてログに記録することで、データベース接続の問題を簡単に特定して解決でき、アプリケーションをスムーズに実行できるようになります。

HTML はデータベースを直接読み取ることはできませんが、JavaScript と AJAX を通じて実現できます。この手順には、データベース接続の確立、クエリの送信、応答の処理、ページの更新が含まれます。この記事では、JavaScript、AJAX、および PHP を使用して MySQL データベースからデータを読み取る実践的な例を示し、クエリ結果を HTML ページに動的に表示する方法を示します。この例では、XMLHttpRequest を使用してデータベース接続を確立し、クエリを送信して応答を処理することで、ページ要素にデータを埋め込み、データベースを読み取る HTML の機能を実現します。

Go 標準ライブラリのデータベース/SQL パッケージを通じて、MySQL、PostgreSQL、SQLite などのリモート データベースに接続できます。データベース接続情報を含む接続文字列を作成します。 sql.Open() 関数を使用してデータベース接続を開きます。 SQL クエリや挿入操作などのデータベース操作を実行します。 defer を使用してデータベース接続を閉じ、リソースを解放します。

Golang でデータベース コールバック関数を使用すると、次のことを実現できます。 指定されたデータベース操作が完了した後にカスタム コードを実行します。追加のコードを記述せずに、個別の関数を通じてカスタム動作を追加します。コールバック関数は、挿入、更新、削除、クエリ操作に使用できます。コールバック関数を使用するには、sql.Exec、sql.QueryRow、または sql.Query 関数を使用する必要があります。
