从数据库系统到Spark SQL (一)

375 阅读7分钟

怎么说呢?这个系列是想了很久。因为如果直接讲Spark SQL好像只是局限于spark这个数据处理框架,简述一种轮子感觉没多大意义。正好最近研读了一遍5月份阿里云数据库团队的前辈翻译的文献<<数据库系统内幕>> 打算从数据库系统的前世今生为主线叙述一遍

数据库可以用于不同目的的查询,比如用于临时热数据,用作冷的长期存储,复杂的分析查询,只能通过键访问值的查询,时间序列数据的查询,可以存储二进制大对象的查询

  • 联机事务处理(OLTP)数据库 处理大量面向用户的请求和事务,查询通常是预定义的,且运行时间很短
  • 联机分析处理(OLAP) 数据库 处理复杂的聚合,常用用于分析和数据仓库,处理复杂的,长时间运行的查询
  • 混合事务和分析处理(HTAP) 数据库 结合了OLTP和OLAP的属性

那么,一个最原始的数据库架构是什么样子呢?

1)客户端请求通过传输模块子系统到达数据库,而请求则以查询的形式出现,通常以某种查询语言标识,传输子系统还负责与集群中的其他节点进行通信

2)在收到从查询后,传输子系统将查询移交给查询处理器,由查询处理器对该查询进行语法解析。解释和验证,稍后,数据库将执行访问控制检查,因为只有在解释查询之后才能完全执行这些检查

3)解析后的查询被传递给查询优化器,优化器首先消除查询中不可能执行的部分和冗余的部分,然后根据内部统计信息(索引基数,近似交集大小)和数据分布(数据存储在哪些节点,传输的成本),尝试找到执行效率最高的方法,通过解析所需的关系操作(表示为依赖关系树),也处理查询优化(索引排序,基数排序,访问方法的选择)

4)查询以执行计划的形式体现,由于执行同一个查询的不同执行计划存储效率上的差异,所以由优化器挑选最有效率的计划

执行计划由执行引擎处理,负责收集本地和远程操作的执行结果。远程执行涉及从集群中的其他节点写入数据,从其他节点读取数据以及数据复制

本地查询由存储引擎执行,存储引擎包括如下:

  • 事务管理器

事务管理器调度事务,并确保他们不会使数据库处于逻辑不一致的状态

  • 锁管理器

锁管理器为正在运行的事务锁定数据库对象,确保并发操作不会破坏物理数据的完整性

  • 访问方法 (存储结构)

管理磁盘上的数据并负责组织磁盘上的数据,访问方法包括堆文件和存储结构,例如LSTM树和B树

  • 缓冲区管理器

将数据页缓存到内存当中

  • 恢复管理器

维护操作日志并在出现故障时还原系统状态

具体在数据表中,数据行和数据列是怎么被存储的呢?

按数据在磁盘上的存储方式进行分类,按行或者列进行分类,表可以水平分区(将属于同一行的值存储在一起),也可以垂直分区(将属于同一列的值存储在一起)

显然这样有些笼统,具体如何组织呢

数据文件与索引文件

数据文件有时候被称为主文件,通常可以用索引组织表,堆组织表,哈希组织表来实现

  • 堆组织表 在堆的记录中按写顺序放置的,在追加新的页的时候,数据库便不需要额外的工作或者文件重组。堆文件需要额外的索引结构只想存储数据记录的位置

  • 哈希组织表 在hash文件中,记录存储在桶中,并且键的hash值确定记录属于哪个桶,存储在桶中记录可以按追加顺序存储

  • 索引组织表 将记录存储在自身,由于记录是按照键的数据存储的,所以范围扫描可以通过顺序扫描来实现 当遍历单独的索引文件时,所以文件保存中数据条目(data enrty) 每个条目唯一标识了数据记录,并包含足够的信息使数据库可以在数据文件中找到它们,我们可以存储文件偏移量来定位具体的数据条目位置

在数据库社区中,对于直接通过文件偏移量引用数据记录还是通过主键索引引用数据记录存在不同的意见。

  • 通过直接引用数据,可以减少查找磁盘的次数,但在维护过程中,每当更新或者重新定位记录时,必须承担指针所带来的成本
  • 通过主索引间接引用数据可以降低指针更新的成本,但读取路径上成本更高

如果工作负载主要由读操作组成,上述两种做法显然都可以使用。但对于多个索引以写为主的工作负载,则分为以下两种,如下图

主(数据)文件上的索引被称为主索引,但是,大多数情况下,可以假设主索引是作为主键,或者主键上的一组键构建的。所有其他的索引被称为二级索引。

二级索引可以直接指向数据记录,也可以简单存储它的主键。指向数据记录的指针可以保存堆文件或者索引组织表的偏移量。多个二级索引可以指向同一记录,从而允许单个数据记录能由不同字段标识并可以被不同字段标识可以被不同索引来定位,虽然主索引文件中每个搜索键都有唯一的条目,但对于每个搜索键,二级索引会对每个搜索键保存多个条目

如果数据记录的顺序遵循搜索键的顺序,则这种索引被称为聚簇索引。聚簇索引中的数据记录通常和索引存储在同一文件,有时也单独存放到聚簇文件中,而这些文件均保留了键的顺序,如果数据存储在单独的文件里,其索引被称为非聚簇索引

那么回过头来,为什么寻址方式是上面这两种呢? 因为索引文件被组织成专门的结构,将键映射到数据文件里的记录,这些记录由对应的键(在堆文件情况下)或者主键(索引组织表的情况下)所标识,下图以索引组织表为例进行说明

PS:将数据记录存在索引文件中与偏移量存储在数据文件中(白色为索引段,灰色为保存数据记录的段) a) 索引组织表:其数据记录直接存储在索引文件内部 b) 索引表:索引文件仅保存偏移量,而用另外的文件保存数据记录

结论:之所以要用主索引和二级索引直接或者间接的定位数据条目,是因为查询偏移量是遍历主键索引之后才进行更新索引文件

如果像第一种方式:通过主索引和二级索引直接指向数据条目,那么每当数据库出现写操作的时候,要遍历主索引去获取新的数据文件偏移量。这样一来不仅主索引的指针需要需要频繁移动,二级索引自身也要移动指针到新的磁盘块,每次磁盘寻道次数会损耗IO性能。 所以像mysql的innodb引擎选择了第二种寻址方式,每次偏移量更新,会通知主索引,这样只需要维护二级索引就可以了