【书籍】设计数据密集型应用(DDIA)ch3存储和查询

181 阅读4分钟

查询

查询类型主要分为两大类:

引擎类型请求数量数据量瓶颈存储格式用户场景举例产品举例
OLTP相对频繁,侧重在线交易总体和单次查询都相对较小Disk Seek多用行存比较普遍,一般应用用的比较多银行交易MySQL
OLAP相对较少,侧重离线分析总体和单次查询都相对巨大Disk Bandwidth列存逐渐流行多为商业用户商业分析ClickHouse

其中,OLTP 侧,常用的存储引擎又有两种流派:

流派主要特点基本思想代表
log-structured 流只允许追加,所有修改都表现为文件的追加和文件整体增删变随机写为顺序写Bitcask、LevelDB、RocksDB、Cassandra、Lucene
update-in-place 流以页(page)为粒度对磁盘数据进行修改面向页、查找树B 族树,所有主流关系型数据库和一些非关系型数据库

数据库底层数据结构

B 树

与 LSM-Tree 一样,它也支持高效的点查范围查。但却使用了完全不同的组织方式。其特点有:

  1. 以页(在磁盘上叫 page,在内存中叫 block,通常为 4k)为单位进行组织。
  2. 页之间以页 ID 来进行逻辑引用,从而组织成一颗磁盘上的树。

image.png

查找:从根节点出发,进行二分查找,然后加载新的页到内存中,继续二分,直到命中或者到叶子节点。查找复杂度,树的高度—— O(lgn),影响树高度的因素:分支因子(分叉数,通常是几百个)。

插入、更新。和查找过程一样,定位到原 Key 所在页,插入或者更新后,将页完整写回。如果页剩余空间不够,则分裂后写入。

让 B 树更可靠

B 树不像 LSM-Tree,会在原地修改数据文件。

在树结构调整时,可能会级联修改很多 Page。比如叶子节点分裂后,就需要写入两个新的叶子节点,和一个父节点(更新叶子指针)。

  1. 增加预写日志(White Ahead Log, WAL),将所有修改操作记录下来,预防宕机时中断树结构调整而产生的混乱现场。
  2. 使用 latch 对树结构进行并发控制。

B 树的优化

B 树有很多优化:

  1. 不使用 WAL,而在写入时利用 Copy On Write 技术。同时,也方便了并发控制。如 LMDB、BoltDB。
  2. 对中间节点的 Key 做压缩,保留足够的路由信息即可。以此,可以节省空间,增大分支因子。
  3. 为了优化范围查询,有的 B 族树将叶子节点存储时物理连续。但当数据不断插入时,维护此有序性的代价非常大。
  4. 为叶子节点增加兄弟指针,以避免顺序遍历时的回溯。即 B+ 树的做法,但远不局限于 B+ 树。

其他索引结构

聚集索引和非聚集索引

对于存储数据和组织索引,我们可以有多种选择:

  1. 数据本身无序的存在文件中,称为 堆文件(heap file) ,索引的值指向对应数据在 heap file 中的位置。这样可以避免多个索引时的数据拷贝。
  2. 数据本身按某个字段有序存储,该字段通常是主键。则称基于此字段的索引为聚集索引(clustered index),从另外一个角度理解,即将索引和数据存在一块。则基于其他字段的索引为非聚集索引,在索引中仅存数据的引用。
  3. 一部分列内嵌到索引中存储,一部分列数据额外存储。称为覆盖索引(covering index)包含列的索引(index with included columns)

索引可以加快查询速度,但需要占用额外空间,并且牺牲了部分更新开销,且需要维持某种一致性。

多列索引(Multi-column indexes)

可以理解为组合索引

全文索引和模糊索引(Full-text search and fuzzy indexes)

前述索引只提供全字段的精确匹配,而不提供类似搜索引擎的功能。比如,按字符串中包含的单词查询,针对笔误的单词查询。

在工程中常用 Apace Lucene库,和其包装出来的服务:Elasticsearch。他也使用类似 LSM-tree 的日志存储结构,但其索引是一个有限状态自动机,在行为上类似 Trie 树。