单机存储系统

首先,我们下个定义,什么是单机存储引擎?

单机存储引擎就是哈希表、B树等数据结构在机械磁盘、SSD等持久化介质上的实现。

单机存储系统的理论来源于关系数据库,关系数据库中,事务(一组操作)的ACID特征要牢记(Atomicity, Consistency, Isolation, Durability)。


1. 硬件基础

这些是最底层的硬件基础。

(1)CPU架构

经典的多CPU架构为Symmetric Multi-Processing(SMP,对称多处理结构)如下图:


由于多CPU对前端总线的竞争,SMP的扩展能力非常有限。为了提高扩展性,现在主流服务器的架构一般为Non-Uniform Memory Access(NUMA,非一致存储访问),每个NUMA节点是一个SMP架构,如下图:



(2)IO总线

  • 北桥快:与CPU、内存模块、PCI-E设备(高端SSD设备)都挂在北桥上;
  • 南桥慢:网卡、硬盘、中低端SSD挂在南桥上;

南北桥通过Direct Media Interface(DMI)相连,看下图:



(3)网络拓扑

传统的数据中心网络分为三层:Edge(接入层), Aggregation(汇聚层), Core(核心层),如下图:


2008年,Google将网络改造为扁平化拓扑结构,即三级CLOS系统。

同一个数据中心内部传输时延比较小,在1ms之内;两个数据中心之间的传输延时较大,比如北京到杭州,时延取决于距离除以光速。


(4)性能参数

常见硬件性能如下(选取了几个有代表性的):

  • 访问一次L1 Cache:0.5ns;
  • 访问一次内存:100ns;
  • 从内存读取1M数据:0.25ms
  • 机房内网络来回:0.5ms
  • 访问一次SATA磁盘(寻道):10ms
  • 访问一次SSD:0.1~0.2ms
由上可见,内存以上是ns级的,磁盘及网络是ms级的。
存储系统的性能瓶颈主要在于“磁盘随机读写”,所以,设计存储引擎的时候,会针对磁盘特性做很多处理,比如将随机写转化为顺序写,通过缓存减少磁盘随机读等。


2. 单机存储引擎
存储系统的基本功能包括:增删改查,读取操作又分为随机读取和顺序读取,下面看典型的三种存储引擎。
(1)哈希存储引擎(比如Bitcast)
读写操作支持随机读取,由于是key-value映射,所以不支持顺序读取,观察下图的具体机制:

由于记录删除或修改后,原来的数据会成为垃圾数据,Bitcask用compaction(合并)操作实现垃圾回收,即对所有老数据文件扫描,对同一个key的多个操作保留最新的一个,所以每次compaction之后新数据文件就不会有冗余数据了。
任何系统都要考虑断电恢复的过程,但如果扫描一遍所有数据来重建哈希表需要很长时间,所以,bitcask用一个hint file(索引文件),将哈希表转储到磁盘,每次compaction会产生新的hint file,这样重启恢复只需要扫描hint file就可以确定所有具体value的位置,重建hashtable了。

(2)B树存储引擎
不仅支持随机读写,还支持范围顺序扫描,如MySQL InnoDB,组织成B+树。B+树的根节点常驻内存。修改操作需要先写提交日志,再修改内存中的B+树,磁盘访问次数为h-1次,时间复杂度为O(h),如下图:

我们下面讨论一下缓冲区管理器,它的关键在于替换策略(常见于操作系统),常见算法有如下两种:
LRU(Least Recently Used):最近最少访问;
LIRS(Low Inter-reference Recently Set):现代数据库一般用这个,将缓冲池分为两级,数据首先进入第一级,如果数据在较短时间内被访问两次以上,那么它就是热点数据,就会进入第二级,每一级的内部还是采用LRU。
Oracle中的Touch Count与MySQL InnoDB的替换算法都是类似的分级思想,此类方法在大多数情况下表现良好,但有一个问题:假如做了一次全表扫描,将导致缓冲区大量页面被替换,这些被替换的页面可能包含很多快被访问到的热点页面,所以这种全表扫描的出现会污染缓冲区。
InnoDB的具体解决办法为:将LRU链表分为两部分new sublist和old sublist。默认new占5/8,old占3/8,页面首先会进入old sublist,要求页面停留时间超过一定时间(比如1s),才有可能被转入new sublist。这样做的话,当遇到全表扫描时,由于数据页面在old sublist中停留时间很短,所以不会被专移到new sublist中,有效避免了new sublist中的热点数据被替换出去的情况。

(3)LSM存储引擎
Log Structured Merge Tree,其思想是,将修改保存在内存中,达到一定量就转储磁盘。
读取时需要合并磁盘中的历史数据与内存中的修改数据,优势在于有效规避了磁盘随机写入的问题(每次写都要寻道等耗时操作),但读取时需要访问较多的磁盘文件。
LevelDB的LSM存储引擎结构如下:

我们看上图,当数据写入时,首先写入MemTable,当MemTable达到上限时,会将其冻结为不可变MemTable,等待后台线程将不可变MemTable利用“compaction”操作转储到磁盘中,形成SSTable;每个层级有多个SSTable,按照记录的主键排序。
对于LevelDB来说,写入容易,读取困难,因为每次读取都需要从老到新读取每个层级的SSTable文件以及内存中的MemTable,当然还有具体的优化措施,这在后面会提到。


3. 数据模型
有一个很好的比喻,如果存储引擎是存储系统的发动机,那么,数据模型就是存储系统的外壳。
主要有三种数据模型:文件模型,关系模型,以及随着NoSQL技术流行起来的key-value模型。
  • 文件模型:记住目录树,像UNIX文件系统中的文件组织;
  • 关系模型:每个关系是一个表,常见的SQL属于这类;
  • key-value模型:被大量NoSQL系统使用,但key-value模型过于简单,所以NoSQL系统中使用比较广泛的模型是表格模型,这种表格弱化了关系模型中的多表关联,典型的是Google Bigtable,后续介绍。
那么,什么是NoSQL?
SQL(Structured Query Language),在数据规模和并发量越来越大的情况先,显得力不从心;NoSQL系统弱化设计范式、弱化一致性要求、提高可扩展性,一定程度上解决了海量数据和高并发的问题。
看关系数据库在海量数据场景下的挑战:
  • 事务:ACID特性在分布式系统中要用到两段提交协议,这个协议性能低,且不能容忍服务器故障,所以难以应用于海量数据场景;
  • 联表:海量数据中,使用数据冗余来弱化联表,实践证明这种做法收益远大于成本;
  • 性能:关系数据库采用B树存储引擎,更新性能不如LSM树这样的存储引擎;对于增删改查性能不如专门定制的key-value系统。
NoSQL系统目前缺少统一标准,各个系统使用的方法不同,切换成本高,运维复杂。
从技术的角度看,不必纠结与二者区别,应借鉴两者优势,着重理解关系数据库的原理以及NoSQL系统的高可扩展性。

4. 事务的并发控制
ACID特性我们都知道,多个事务并发执行时,可串行化的隔离等级是比较理想的,但业界为性能考虑定义了多种隔离等级。
一般事务并发通过锁机制来实现,当然这里要面对的是死锁的问题。
在互联网业务中,读事务往往高于写事务,所以为提高读事务性能,可以采用COW(Copy-On-Write)来避免写事务阻塞读事务;
下面我们介绍COW下的B+树:

如上图,COW中,读操作不用加锁,极大提高了读取性能,执行写操作的步骤如下:
  • 拷贝:从叶节点到要修改的根节点数据都copy出来;
  • 修改:修改copy出的内容;
  • 提交:原子性的切换根节点Root的指针,注意这个有意思的细节,只需要切换根节点的指针即可。

5. 故障恢复
当然用到log(日志)了,操作有undo回滚和redo重做,步骤都是先写日志再操作,无可厚非。
日志具体的优化措施,比如说成组提交,比如说checkpoint与快照,等等,我们就不细讨论了。


6. 数据压缩
像Huffman,Bigtable中的BMDiff、Zippy(LZ系列)等。自从以色列人Jacob Ziv和Abraham Lempel发表了《A Universal Algorithm for Sequential Date Compression》,从此,LZ系列压缩算法几乎垄断了通用无损压缩领域。
压缩算法的核心是找重复数据,Huffman编码通过统计字符出现的频率来计算最优前缀编码;
LZ系列压缩算法是基于字典的压缩算法,比如要压缩一篇文章,我们只需保存每个单词在字典中“出现的页码与位置”就可以了。LZ系列算法是一种动态创建字典的方法,压缩过程中动态创建字典并保存在压缩信息里面。
LZ系列压缩算法的如下几个问题值得思考:
  • 如何区分匹配信息和源信息?用额外的1 bit;
  • 需要使用多少个字节来表示匹配信息?<匹配串的相对位置,匹配的长度>
  • 如何快速查找最长匹配串?一种低效的做法是将所有子串放在hashtable中;这个问题值得深究。
最后提一点,位图在压缩中也应用广泛,比如说男女性别一列数据的的压缩。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值