大规模分布式存储系统笔记一二章 概述与单机存储系统

第1章 概述

1.1 分布式存储概念

分布式存储系统的特性:
  • 可扩展
  • 低成本
  • 高性能
  • 易用
 
分布式存储系统的挑战:
  • 数据分布
  • 一致性
  • 容错
  • 负载均衡
  • 事务与并发控制
  • 易用性
  • 压缩/解压缩

1.2 分布式存储分类

  • 非结构化数据,如办公文档、文本、图片、图像、音频、视频信息等。
  • 结构化数据,如关系数据库
  • 半结构化数据,如HTML文档
 
本书将分布式存储系统分为四类:
  1. 分布式文件系统,存储图片、视频等非结构化数据对象,一般称为Blob(Binary Large Object,二进制大对象)数据。典型系统如facebook Haystack、Taobao File System(TFS)。总体上看,分布式文件系统存储三种类型的数据:Blob对象、定长块以及大文件。
  2. 分布式键值系统,存储关系简单的半结构化数据,典型系统有Amazon Dynamo以及Taobao Tair,一般用作缓存。
  3. 分布式表格系统,存储关系较为复杂的半结构化数据,典型系统包括Google Bigtable以及Megastore,Microsoft Azure Table Storage。主要支持针对单张表格的操作,不支持如多表关联、嵌套子查询等。
  4. 分布式数据库,存储结构化数据,提供SQL关系查询语言,支持多表关联、嵌套子查询等复杂操作,并提供数据库事务以及并发控制。典型系统包括MySQL数据库分片(MySQL Sharding)集群,Amazon RDS以及Microsoft SQL Azure。

第2章 单机存储系统

2.1 硬件基础

2.1.1 CPU架构
经典的多CPU架构为对称多处理结构(Symmetric Multi-Processing, SMP),为提高可扩展性,现在主流服务器架构一般为NUMA(Non-Uniform Memory Access,非一致存储访问)架构。
 
2.1.2 IO总线
以Intel x48为例,是典型的南、北桥架构。
 
2.1.3 网络拓扑
分为传统的数据中心网络拓扑,Google在2008年将网络改造为扁平化拓扑结构,即三级CLOS网络。
 
2.1.4 性能参数
存储系统的性能瓶颈主要在于磁盘随机读写。设计存储引擎的时候会针对磁盘的特性做很多的处理,比如将随机写操作转化为顺序写,通过缓存减少磁盘随机读操作。
 
固态磁盘(SSD)特点是随机读取延迟小,能够提供很高的IOPS(每秒读写,Input/Output Per Second)性能。它的主要问题在于容量和价格,设计存储系统的时候一般可以用来做缓存或者性能要求较高的关键业务。
 
2.1.5 存储层次架构
存储系统的性能主要包括两个维度:吞吐量以及访问延时,设计系统时要求能够在保证访问延时的基础上,通过最低的成本实现尽可能高的吞吐量。磁盘和SSD的访问延时差别很大,但带宽差别不大,因此磁盘适合大块顺序访问的存储系统,SSD适合随机访问较多或者对延时比较敏感的关键系统。二者也常常组合在一起进行混合存储,热数据(访问频繁)存储到SSD中,冷数据(访问不频繁)存储到磁盘中。
 

2.2 单机存储引擎

存储系统的基本功能包括:增、删、读、改,其中,读取操作又分为随机读取和顺序读取。哈希存储引擎是哈希表的持久化实现,支持增、删、改,以及随机读取操作,但不支持顺序扫描,对应的存储系统为键值(Key-Value)存储系统;B树(B-Tree)存储引擎是B树的持久化实现,不仅支持单条记录的增、删、读、改操作,还支持顺序扫描,对应的存储系统是关系数据库。当然,键值系统也可以通过B树存储引擎实现;LSM树(Log-Structured Merge Tree)存储引擎和B树存储引擎一样,支持增、删、改、随机读取以及顺序扫描。它通过批量转储技术规避磁盘随机写入问题,广泛应用于互联网的后台存储系统,例如Google Bigtable, Google LevelDB以及Facebook开源的Cassandra系统。

2.2.1 哈希存储引擎

Bitcask是一个基于哈希表结构的键值存储系统,它仅支持追加操作(Append-only)。
1 数据结构
内存中采用基于哈希表的索引数据结构,哈希表的作用是通过主键快速地定位到value的位置。
Bitcask在内存中存储了主键和value的索引信息,磁盘文件中存储了主键和value的实际内容。
2 定期合并
3 快速恢复
Bitcask通过索引文件(hint file)来提高重建哈希表的速度。简单来说,索引文件就是将内存中的哈希索引表转储到磁盘生成的结果文件。

2.2.2 B树存储引擎

1 数据结构
MySQL InnoDB按照页面(Page)来组织数据,每个页面对应B+树的一个节点。其中,叶子节点保存每行的完整数据,非叶子节点保存索引信息。数据在每个节点中有序存储,数据库查询时需要从根节点开始二分查找直到叶子节点,每次读取一个节点,如果对应的页面不在内存中,需要从磁盘中读取并缓存起来。B+树的根节点是常驻内存的,因此,B+树一次检索最多需要h-1次磁盘IO,复杂度为O(h)=O(logd^N)(N为元素个数,d为每个节点的出席,h为B+树高度)。修改操作首先需要记录提交日志,接着修改内存中的B+树。如果内存中的被修改过的页面超过一定的比率,后台线程会将这些页面刷到磁盘中持久化。
2 缓冲区管理
缓冲区管理器负责将可用的内存划分成缓冲区,缓冲区是与页面同等大小的区域,磁盘块的内容可以传送到缓冲区中。缓冲区管理器的关键在于替换策略,即选择将哪些页面淘汰出缓冲池。常见的算法有以下两种。
(1)LRU,LRU算法淘汰最长时间没有读或者写过的块。
(2)LIRS,解决全表扫描污染缓冲池的问题。现代数据库一般采用LIRS算法,将缓冲池分为两级,数据首先进入第一级,如果数据在较短的时间内被访问两次或者以上,则成为热点数据进入第二级,每一级内部还是采用LRU替换算法。
 

2.2.3 LSM树存储引擎

LSM树(Log Structured Merge Tree)的思想非常朴素,就是将对数据的修改增量保持在内存中,达到指定的大小限制后将这些修改操作批量写入磁盘,读取时需要合并磁盘中的历史数据和内存中最近的修改操作。
 
1 存储结构
LevelDB存储引擎主要包括:内存中的MemTable和不可变MemTable(Immutable MemTable, 也称为Frozen MemTable,即冻结MemTable)以及磁盘上的几种主要文件:当前(Current)文件、清单(Manifest)文件、操作日志(Commit Log, 也称为提交日志)文件以及SSTable文件。当应用写入一条记录时,LevelDB会首先将修改操作写入到操作日志文件,成功后再将修改操作应用到MemTable,这样就完成了写入操作。
当MemTable占用的内存达到一个上限值后,需要将内存的数据转储到外存文件中。
SSTable中的文件是按照记录的主键排序的,每个文件有最小 的主键和最大的主键。
2 合并
LevelDB的Compaction操作分为两种:minor compaction和major compaction。minor compaction是内存中的MemTable转储到SSTable。major compaction是合并多个SSTable文件。

2.3 数据模型

2.3.1 文件模型

POSIX(Portable Operation System Interface)是应用程序访问文件系统的API标准,它定义了文件系统存储接口及操作集。

2.3.2 关系模型

每个关系是一个表格,由多个元组(行)构成,而每个元组又包含多个属性(列)。关系名、属性名以及属性类型称作该关系的模式(schema)。
数据库语言SQL用于描述查询以及修改操作。

2.3.3 键值模型

大量的NoSQL系统采用了键值模型(也称为Key-Value模型),每行记录由主键和值两个部分组成,支持基于主键的如下操作:Put, Get, Delete
NoSQL系统中使用比较广泛的模型是表格模型。表格模式弱化了关系模型中的多表关联,支持基于单表的简单操作,典型的系统是Google Bigtable以及其开源Java实现HBase。主要操作如下:Insert, Delete, Update, Get, Scan。

2.3.4 SQL与NoSQL

关系数据库在海量数据场景面临如下挑战:
  • 事务
  • 联表
  • 性能
NoSQL系统面临如下问题:
  • 缺少统一标准
  • 使用以及运维复杂

2.4 事务与并发控制

2.4.1 事务

事务的四个基本属性:
(1)原子性(Atomicity)
(2)一致性(Consistency)
(3)隔离性(Isolation)
(4)持久性(Durability)
 
SQL定义了4种隔离级别:
  • Read Uncommitted(RU):读取未提交的数据
  • Read Committed(RC):读取已提交的数据
  • Repeatable Read(RR):可重复读取
  • Serialization(S):可序列化,即数据库的事务是可串行化执行的。这是最高的隔离级别。
 
隔离级别的降低可能导致读到脏数据或者事务执行异常,例如:
  • Lost Update(LU):丢失更新
  • Dirty Reads(DR):脏读
  • Non-Repeatable Reads(NRR):不可重复读
  • Second Lost Updates problem(SLU): 第二类丢失更新
  • Phantom Reads(PR): 幻读

2.4.2 并发控制

1 数据库锁
事务分为几种类型:读事务,写事务以及读写混合事务。相应地,锁也分为两种类型:读锁以及写锁,允许对同一个元素加多个读锁,但只允许加一个写锁,且写事务将阻塞读事务。
 
2 写时复制(Copy-On-Write,COW)
写时复制读操作不用加钞,极大地提高了读取性能。
图2-10中写时复制B+树执行写操作的步骤如下:
(1)拷贝:将从叶子到根节点路径上的所有节点拷贝出来。
(2)修改:对拷贝的节点执行修改。
(3)提交:原子地切换根节点的指针,使之指向新的根节点。
 
写时复制技术原理简单,问题是每次写操作都需要拷贝从叶子到根节点路径上的所有节点,写操作成本高,另外,多个写操作之间的互斥的,同一时刻只允许一个写操作。
 
3 多版本并发控制(MVCC,Multi-Version Concurrency Control)
也能够实现读事务不加锁。以MySQL InnoDB存储引擎为例,InnoDB对每一行维护了两个隐含的列,其中一列存储行被修改的"时间",另外一列存储行被删除的"时间"。注意,InnoDB存储的并不是绝对时间,而是与时间对应的数据库系统的版本号。
 
MVCC读取数据的时候不用加锁,每个查询都通过版本检查,只获得自己需要的数据版本,从而大大提高了系统的并发度。

2.5 故障恢复

数据库系统以及其他的分布式存储系统一般采用操作日志(有时也称为提交日志,即Commit Log)技术来实现故障恢复。操作日志分为回滚日志(UNDO Log)、重做日志(REDO Log)以及UNDO/REDO日志。
 
2.5.1 操作日志
关系数据库系统一般采用UNDO/REDO日志。
 
2.5.2 重做日志
 
2.5.3 优化手段
1 成组提交(Group Commit)
2 检查点(Checkpoint)
需要将内存中的数据定期转储(Dump)到磁盘,这种技术称为checkpoint(检查点)技术。系统定期将内存中的操作以某种易于加载的形式(checkpoint文件)转储到磁盘中,并记录checkpoint时刻的日志回放点,以后故障恢复只需要回放checkpoint时刻的日志回放点之后的REDO日志。
 

2.6 数据压缩

2.6.1 压缩算法

压缩的本质就是找数据的重复或者规律,用尽量少的字节表示。
 
1 Huffman编码
前缀编码要求一个字符的编码不能是另一个字符的前缀。
 
2 LZ系列压缩算法
LZ系列压缩算法是基于字典的压缩算法。
 
3 BMDiff与Zippy
在Google的Bigtable系统中,设计了BMDiff和Zippy两种压缩算法。
 

2.6.2 列式存储

OLTP(Online Transaction Processing,联机事务处理)应用适合采用行式数据库。OLAP类型的查询可能要访问几百万甚至几十亿数据行,且该查询往往只关心少数几个数据列,列式数据库可以大大提高OLAP大数据量查询的效率。列组(column group)是一种行列混合存储械,这种模式能够同时满足OLTP和OLAP的查询需求。
 
由于同一个数据列的数据重复度很高,因此,列式数据库压缩时有很大的优势,如位图索引等。
 

猜你喜欢

转载自www.cnblogs.com/sxpujs/p/11442970.html