关系数据库如何存储和检索数据?

了解数据在磁盘上是如何组织的。

数据库旨在高效地存储、管理和检索数据。这一过程涉及多个关键组件和概念。

以下是数据库如何存储和检索数据的总体概述:

  1. 数据模型:
    • 数据库使用数据模型来定义其存储的数据结构。常见的数据模型包括关系型、面向文档型、图型和键值模型。
    • 在关系型数据库中,数据被组织成具有行和列的表格。每一行代表一条记录,每一列代表一个字段或属性。
  • 表和字段:
    • 在关系数据库中,数据存储在表中。每个表都有一个已定义的结构,其中包含特定字段(列)和数据类型。
    • 字段存储单个数据,每个字段都有一个数据类型(如文本、整数、日期)。
  • 主键:
    • 表通常有一个主键,即每条记录的唯一标识符。该键用于建立表之间的关系。
  • 索引:
    • 数据库使用索引来优化数据检索。索引是一种数据结构,它提供了一种基于特定列查找记录的快速方法。
    • 索引可提高查询性能,但可能会在存储和更新开销方面有所折衷。
  • SQL(结构化查询语言):
    • SQL 是用于与关系数据库交互的语言。它提供用于创建、更新和查询数据的命令。
    • 查询用于根据查询中指定的条件检索特定数据。
  •  
  • ACID 属性:
    • ACID(原子性、一致性、隔离性、持久性)属性可确保数据库事务的可靠性。事务是维护数据完整性的工作单位。
  • 数据库管理系统 (DBMS):
    • DBMS 是一种提供与数据库交互界面的软件。例如,MySQL、PostgreSQL、Oracle 和 MongoDB。
    • DBMS 处理数据存储、检索、安全和并发控制等任务。
  • 存储引擎:
    • 数据库使用存储引擎来管理磁盘上的数据物理存储。不同的存储引擎具有不同的性能特点和功能。
  •  
  • 缓存:
    • 采用缓存机制将经常访问的数据存储在内存中,从而减少从磁盘检索数据的需要,提高整体性能。
  • 优化器:
    • 查询优化器分析 SQL 查询并确定检索数据的最有效方式,同时考虑索引、统计数据和其他因素。
  • 备份和恢复:
    • 数据库采用备份和恢复机制来防止数据丢失或损坏。定期备份可确保在发生故障时恢复数据。

    背后机制

    • 后台会将 INSERT INTO 表中的数据组织成一种称为 B+Tree 的结构。
    • 这样,当你从表中进行 SELECT * 时,它就能有效地找到这些数据。

    不过,由于表的大小可能很大,在内存中存储整棵树通常不太现实。
    取而代之的是,
    每次只有部分树节点会存储在内存中。
    这些节点通常被称为页。

    • 页是数据库引擎每次读取和写入磁盘的最小数据单位。
    • 一页等于一个树节点。

    B+Tree
    B+Trees 和 B-Trees 树的节点最多可以有 N-1 个键。

    因此,如果我有一棵阶数为 3 的 B+Tree 树(或 B-Tree树),这意味着每个节点最多可以包含 2 个键和 3 个指向子节点的链接。

    • 当我们需要在一个完整节点中插入一个新键时,该节点就会分裂,并将中间的键传递给父节点。
    • 这种情况会递归地发生,直到一个节点有空间容纳一个键,或者我们到达根节点。
    • 如果我们到达了根节点,而它已经满了,那么它也会分裂,并创建一个新的根节点,从而使整个树的高度增加 1。

    B+Tree 与 B-Tree 的区别在于:

    • 非叶节点(内部节点)不包含值,因此内部节点中的键将被复制到可以找到数据的叶节点中。
    • 此外,B+树中的叶节点被链接在一起,形成一个链接列表。
    • 这样,如果我们不能按键搜索,就可以线性遍历所有数据(因此需要全表扫描)。

    数据在磁盘上是如何组织的:

    • 在进行写入操作时,可能会锁定整个页面,也可能不会锁定整个页,因此可能会导致同一页上的其他行也被锁定(一些引擎会使用一些技巧将锁定范围限制在行)。
    • 使用自动递增主键有一个很好的特性,即总是写入树中最右边的页,这可以在进行多次 INSERT 时提高页面缓存的命中率。而使用随机主键(如 UUID4)则会导致页缓存崩溃和效率低下,因为会有很多缓存未命中,而且有可能进入不同的叶节点。
    • 页面大小的选择历来与旋转磁盘读取头的默认读取量相匹配,以提高效率。随着固态硬盘的出现,不确定这是否仍然适用。
    • 如果一条记录对于一个页面来说太大,那么节点中就会有一个指向溢出页面(至少在 SQLite 中是这样)的链接。溢出页面本身是一个链接列表,指向其他溢出页面,足以容纳所有数据。
    • 尽管每个节点中可能有很多键需要遍历,但我们仍会通过页探测次数来衡量性能,因为这是迄今为止搜索成本最高的操作。
    • 您可以使用哈希表索引来代替树,但这样就无法进行范围查询。
    • 树的分支因子(一页能容纳多少个键)大致可以估算为(page_size - header_size)/(key_size + link_size)。如果你计算一下,就会发现在深度为 4 的树中,你可能要存储 TB 的行数据。