SQLite中的B-Tree实现细节分析

SQLite在存储在外部的数据库是以B-Tree来组织的。关于B-tree的细节,参考
**
** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
** "Sorting And Searching", pages 473-480. Addison-Wesley
** Publishing Company, Reading, Massachusetts.
**
基本思想是文件包含的每一页都包括N个数据库入口和N+1个指向子页的指针。文件分成很多页存储。为什么这么干,因为内存分页管理机制闹得。外存中每个页就是B树的一个节点。
----------------------------------------------------------------
| Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) |
----------------------------------------------------------------
Ptr(0)指向的页上的所有的key的值都小于Key(0)。所有Ptr(1)指向的页和子页的所有的key的值都大于Key(0),小于Key(1)。所有Ptr(N)指向的页和子页的key的值都大于Key(N-1),等等。

为了知道一个特定的key,需要从磁盘上以O(long(M))来读取,其中M是树的阶数。内存中找不到了,就发生缺页中断。
主要是解决内存中找不到的问题。一方面换出来一些。一方面换进去一些。换进去的时候要找到他们再硬盘的哪个页面上啊。
(B树的优点就是适合于用块儿存储的存储设备上。)利用所以,可以知道他们们在哪个页面上。

在SQLite的实现中,一个文件可以含有1个或的过独立的BTree。每一个BTree由它的根页的索引来标识。所有入口的key和数据组成了有效负荷(payload)。数据库的一页有一个固定的有效负荷总量。如果负荷大于了预先设定的值,那么剩余的字节就会被存储在溢出页上。一个入口的有效负荷再加上前向指针(the preceding pointer)构成了一格(cell)。每一页都有一个小头部,包含了Ptr(N)指针和其它一些信息,例如key和数据的大小。

格式细节
一个文件分成了多个页。第一页叫做页1,第二页叫做页2,一次类推。页的个数为0表示没有页。页的大小可以从512 到 65536。每一页或者是一个btree页,或者是一个freelist页,或者是一个溢出页。
第一页一定是一个btree页。第一页的前面100个字节包含了一个特殊的首部(文件头),它是这个文件的描述。
文件头的个数如下:
** OFFSET SIZE DESCRIPTION
** 0 16 Header string(首部字符串): "SQLite format 3\000"
** 16 2 Page size in bytes(页的字节数).
** 18 1 File format write version(文件写操作的版本)
** 19 1 File format read version (文件读操作的版本)
** 20 1 Bytes of unused space at the end of each page(每一页结尾未使用的字节)
** 21 1 Max embedded payload fraction(最大的嵌入有效负荷分片)
** 22 1 Min embedded payload fraction(最小的嵌入有效负荷分片)
** 23 1 Min leaf payload fraction(最小的页有效负荷分片)
** 24 4 File change counter (文件变化计数器)
** 28 4 Reserved for future use (保留字节)
** 32 4 First freelist page (第一个freelist页)
** 36 4 Number of freelist pages in the file (本文件中freelist页的个数)
** 40 60 15 4-byte meta values passed to higher layers()
**
所有的整数都是大端的。

每次修改文件时,文件变化计数器都会增加。这个计数器可以让其他进程知道何时文件被修改了,他们的cache是否需要清理。

最大嵌入有效负荷分片是一页的所有可用空间,被标准B-tree(非叶数据)表的单独的一个所能使用的总量。值255代表100%。默认情况下,一格(cell)的最大量被限制为,至少有4格才能填满一页。因此,默认的最大嵌入负荷分片是64。

如果一页的有效负荷大于了最大有效负荷,那么剩下的数据就要被存储到溢出页。一旦分配了一个溢出页,有可能会有许多数据也被转移到这个溢出页,但是不会让格cell的大小小于最小嵌入有效负荷分片的。

最小页有效负荷分片与最小嵌入有效负荷分片类似,但是它是应用于LEAFDATA tree中的叶节点。一个LEAFDATA的最大有效负荷分片为100%(或者是值255),它不用再首部指定。

BTree的每一页被分为三部分:首部,格(cell)指针数组,和格cell的内容。页1还会在页首部有100字节的文件头。
**
** |----------------|
** | file header | 100 bytes. Page 1 only.
** |----------------|
** | page header | 8 bytes for leaves. 12 bytes for interior nodes
** |----------------|
** | cell pointer | | 2 bytes per cell. Sorted order.
** | array | | Grows downward
** | | v
** |----------------|
** | unallocated |
** | space |
** |----------------| ^ Grows upwards
** | cell content | | Arbitrary order interspersed with freeblocks.
** | area | | and free space fragments.
** |----------------|
**
页首部如下图所示:
**
** OFFSET SIZE DESCRIPTION
** 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf
** 1 2 byte offset to the first freeblock
** 3 2 number of cells on this page
** 5 2 first byte of the cell content area
** 7 1 number of fragmented free bytes
** 8 4 Right child (the Ptr(N) value). Omitted on leaves.
**
标志位定义了这个BTree页的格式。叶leaf标志意味着这一页没有孩子children。zerodata0数据表示这一页只含有key,没有数据;intkey标志意味着key是一个整数,而且是被存储在格cell首部的key大小处,而不是在有效负荷区域。

格cell指针数组从页首部开始。格cell指针数组包含0个或多余2个字节的数字,这个数字代表格cell内容区域中的格cell内容从文件起始位置的偏移量。格cell指针式有序的。系统尽力保证空闲空间位于最后一个格cell指针之后,这样可以保证新的格cell可以很快的添加,而不用重新整理(defragment)这一页。

格cell内容存储在页的末尾,且是向文件的起始方向增长。

在格cell内容区域中的未使用的空间被收集到链表freeblocks上。每一个freeblock至少有4个字节。第一个freeblock的偏移在页首部给出了。Freeblock是增序的。因为一个freeblock至少有4个字节,所有在格cell内容区域的3个或是哦啊与3个的未用空间不能存在于freeblock链表上。这些3个或少于3个的空闲空间被称为碎片。所有碎片的总个数被记录下来,存储于页首部的偏移7的位置。

** SIZE DESCRIPTION
** 2 Byte offset of the next freeblock
** 2 Bytes in this freeblock
**

格cell是可变长度的。格cell被存储于页的末尾格cell内容区域。指向格cell的cell指针数组紧跟在页首部的后面。格cell不必是连续或者有序的,但是格cell指针是连续和有序的。

格cell内容充分利用了可变长度整数。可变长度整数是从1到9个字节,每个字节的低7位被使用。整个整数由8位的字节组成,其中第一个字节的第8位被清零。整数最重要的字节出现在第一个。可变长度整数一般不多于9个字节。作为一种特殊情况,第九个字节的所有8个字节都会被认为是数据。这就允许了64位整数变编码为9个字节。
** 0x00 becomes 0x00000000
** 0x7f becomes 0x0000007f
** 0x81 0x00 becomes 0x00000080
** 0x82 0x00 becomes 0x00000100
** 0x80 0x7f becomes 0x0000007f
** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678
** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081
本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2012-11/75009.htm

(0)

相关推荐

  • Android开发之SQLite的使用方法

    前言 SQLite是一种轻量级的小型数据库,虽然比较小,但是功能相对比较完善,一些常见的数据库基本功能也具有,在现在的嵌入式系统中使用该数据库的比较多,因为它占用系统资源很少.Android系统中也不例外,也是采用SQLite,本节中就学习下在andorid中怎样使用该数据库来存放数据,并且对SQLite完成简单的新建,更新,查询,删除等操作. 实验说明: Android中使用SQLite数据库时,需要用adb来辅助调试,如果想在windows下的cmd命令行中使用adb,必须先配置环境变量,我

  • SQLite优化方法

    例如:向数据库中插入100万条数据,在默认的情况下如果仅仅是执行 sqlite3_exec(db, "insert into name values 'lxkxf', '24'; ", 0, 0, &zErrMsg); 将会重复的打开关闭数据库文件100万次,所以速度当然会很慢.因此对于这种情况我们应该使用"事务". 具体方法如下:在执行SQL语句之前和SQL语句执行完毕之后加上 rc = sqlite3_exec(db, "BEGIN;"

  • SQLite 错误码整理

    复制代码 代码如下: #define SQLITE_OK           0   /* 成功 | Successful result *//* 错误码开始 */#define SQLITE_ERROR        1   /* SQL错误 或 丢失数据库 | SQL error or missing database */#define SQLITE_INTERNAL     2   /* SQLite 内部逻辑错误 | Internal logic error in SQLite */#

  • sQlite常用语句以及sQlite developer的使用与注册

    前言 sQlite是开发中比较常用的轻量级数据库.通常只占据几百k的内存空间,所以在ios开发中,苹果将sQlite作为数据库应用在苹果开发中,当然,fmdb就另当别论了.这里主要是为了区分sQlite语句,以及mySql语句,以及Oracle之间的区别. sQlite的常用语句归纳 1.创建表语句 create table create table student( id integer primary key autoincrement, name varchar(20) not null,

  • SQLite3中的日期时间函数使用小结

    复制代码 代码如下: import sqlite3conn = sqlite3.connect('/tmp/sqlite.db')cur = conn.cursor() 接下来干嘛呢?建一张表吧.这里需要注意的是,SQLite不支持在创建表的同时创建索引,所以要分两步走,先创建表然后再创建索引 复制代码 代码如下: create_table_stmt = '''CREATE TABLE IF NOT EXISTS test_table ( id INTEGER PRIMARY KEY AUTOI

  • sqlite中文乱码问题原因分析及解决

    在VC++中通过sqlite3.dll接口对sqlite数据库进行操作,包括打开数据库,插入,查询数据库等,如果操作接口输入参数包含中文字符,会导致操作异常.例如调用sqlite3_open打开数据库文件,如果文件路径出现中文,就会导致打开失败.sqlite3_exec执行sql语句,如果包含中文对应字符就会变成乱码. 这是由于sqlite数据库使用的是UTF-8编码方式,而传入的字符串是ASCII编码或Unicode编码,导致字符串格式错误.解决方案是在调用sqlite接口之前,先将字符串转换

  • Sqlite 常用函数 推荐

    1 .打开数据库: 说明:打开一个数据库,文件名不一定要存在,如果此文件不存在, sqlite 会自动创建.第一个参数指文件名,第二个参数则是定义的 sqlite3 ** 结构体指针(关键数据结构),这个结构底层细节如何,您不用管它. int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); 返回值:表示操所是否正确 (

  • SQLite 中文指南之FAQ第1/6页

    1. 如何创建自增字段? 2. SQLite 支持哪些数据类型? 3. 为什么能向 SQLite 数据库的整型字段中插入字符串? 4. 为什么 SQLite 认为表达式 '0'=='00' 为真? 5. 为什么 SQLite 不允许在同一张表里使用 '0' 和 '0.0' 作为两个不同的行的主键? 6. 为什么不能在 Linux box 中读取在 SparcStation 中创建的 SQLite 数据库? 7. 多个应用程序或者同一个应用程序的多个例程能同时存取同一个数据库文件吗? 8. SQL

  • sqlite3 top的查询及limit语法介绍

    其实,在sqlite3中没有top的语法结构,但在sqlite3中有相关的语法能实现跟top语法相同的功能,sqlite3 sql是用limit这样的语法来实现的: 如: 复制代码 代码如下: select * from table where name='_安静ゝ' order by id limit 0,10; 这个效果就相当于select top 10 * from table where name='_安静ゝ': 如果还有更精确的: 复制代码 代码如下: select * from ta

  • Java中字符串拼接的一些细节分析

    工作日忙于项目的逻辑实现,周六有点时间,从书柜里拿出厚厚的英文版Thinking In Java,读到了字符串对象的拼接.参考着这本书做个翻译,加上自己思考的东西,写上这篇文章记录一下. 不可变的String对象 在Java中,String对象是不可变的(Immutable).在代码中,可以创建多个某一个String对象的别名.但是这些别名都是的引用是相同的. 比如s1和s2都是"droidyue.com"对象的别名,别名保存着到真实对象的引用.所以s1 = s2 复制代码 代码如下:

  • 详解SQLite中的查询规划器

     1.0 介绍 查询规划器的任务是找到最好的算法或者说"查询计划"来完成一条SQL语句.早在SQLite 3.8.0版本,查询规划器的组成部分已经被重写使它可以运行更快并且生成更好的查询计划.这种重写被称作"下一代查询规划器"或者"NGQP". 这篇文章重新概括了查询规划的重要性,提出来一些查询规划固有的问题,并且概括了NGQP是如何解决这些问题. 我们知道的是,NGQP(下一代查询规划器)几乎总是比旧版本的查询规划器好.然而,也许有的应用程序在

  • 浅谈SQLite时间函数的使用说明与总结分析

    本文主要讲解SQLite中时间函数进行分析与总结并给出使用案例.本文给出的例子都是经过测试.SQLite时间/日期函数种类:1.datetime():产生日期和时间2.date():产生日期3:.time():产生时间4.strftime():对以上三个函数产生的日期和时间进行格式化 SQLite时间/日期函数用法:1.datetime()的用法是:datetime(日期/时间,修正符,修正符...)2.date()和time()的语法与datetime()相同.3.strftime()函数可以

  • 浅谈SpringMVC中的session用法及细节记录

    前言 初学SpringMVC,最近在给公司做的系统做登录方面,需要用到session. 在网上找了不少资料,大致提了2点session保存方式: 1.javaWeb工程通用的HttpSession 2.SpringMVC特有的@SessionAttributes 我个人比较关注@SessionAttributes的用法,毕竟现在是在用SpringMVC嘛.但是我看网上那些文章,基本都是只说明了基础用法,详细的使用和细节却基本没有,我想这是不够的,所以我自己做了一些测试,然后整理了下代码做了个de

  • Python实现将SQLite中的数据直接输出为CVS的方法示例

    本文实例讲述了Python实现将SQLite中的数据直接输出为CVS的方法.分享给大家供大家参考,具体如下: 对于SQLite来说,目前查看还是比较麻烦,所以就像把SQLite中的数据直接转成Excel中能查看的数据,这样也好在Excel中做进一步分数据处理或分析,如前面文章中介绍的<使用Python程序抓取新浪在国内的所有IP>.从网上找到了一个将SQLite转成CVS的方法,贴在这里,供需要的朋友使用: import sqlite3 import csv, codecs, cStringI

  • MySQL 权限控制细节分析

    今天周天,早上懒了一会儿,起的有点儿晚,中午没事儿干,重新看了看MySQL里面的权限控制模块,再次回头看,还是有很多收获的细节,这里记录一下,方便自己后续查看.     关于权限部分的内容,之前3月11号的文章中有写过一些,今天的内容,我们使用一个一个的细节知识点来撰写(本文中所使用的MySQL版本是5.7.16),在写这些知识点之前,我们首先介绍一下MySQL的权限控制粒度.然后了解一下MySQL中客户端发起请求的时候,服务端所做的核实工作,先来看权限控制粒度: 1.全局层级 全局权限使用于给

  • Postgres中UPDATE更新语句源码分析

    目录 PG中UPDATE源码分析 整体流程分析 解析部分——生成语法解析树UpdateStmt 解析部分——生成查询树Query 优化器——生成执行计划 执行器 事务 总结 PG中UPDATE源码分析 本文主要描述SQL中UPDATE语句的源码分析,代码为PG13.3版本. 整体流程分析 以update dtea set id = 1;这条最简单的Update语句进行源码分析(dtea不是分区表,不考虑并行等,没有建立任何索引),帮助我们理解update的大致流程. SQL流程如下: parse

  • SQLite中重置自动编号列的方法

    目前流行的数据库都提供了自动编号类型,SQLite也不例外.当数据库中包含自动编号的字段时,SQLite会自动建立一个名为 sqlite_sequence 的表.这个表包含两个字段:name 和 seq .name字段记录了自动编号字段所在的表,seq字段记录了当前用到的序号(下一条记录的编号就是当前序号加1). 在开发过程中,我们经常要把表重置.也就是说把表中的记录全部清空,并把自动编号归0.在SQLite中,只需要修改 sqlite_sequence 表就可以了: 复制代码 代码如下: UP

  • jQuery中.attr()和.data()的区别分析

    $.attr()和$.data()本质上属于 DOM属性 和 Jquery对象属性 的区别. Jquery对象属性和DOM属性 一个简单的例子 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Jquery中.attr和.data的区别</title> </head> <body> <p id="app&q

  • 在laravel中使用Symfony的Crawler组件分析HTML

    Crawler全名是DomCrawler,是Symfony框架的组件.令人发指的是DomCrawler的没有中文文档,Symfony也没有翻译该部分,所以使用DomCrawler开发只能一点一点摸索,现将使用过程中的经验总结. 首先是安装 composer require symfony/dom-crawler composer require symfony/css-selector css-seelctor 是 css选择器,用css选择节点时一些函数会用到 手册里面使用的例子是 use S

随机推荐