存储
单机存储
- 可以简单理解为单个计算机节点上的存储软件系统,一般
不涉及网络交互
本地文件系统
-
此部分以
Linux系统为例 -
Linux有一个著名的哲学:一切皆文件。无论是我们看得到的鼠标,键盘这些外设,还是看不到的文件,进程等等等等,在Linux的系统设计中都被当作文件处理,他们都拥有统一的操作方法,被组织在一个文件系统中。这意味着,我们可以使用相同的命令来处理不同的设备(都是同一个文件,只是文件的内容不同),这极大地增加了它的易用性和扩展性,当我们使用一些安全措施来保护文件时,实际上也保护了设备,病毒等没有相应权限自然无法侵入破坏。 -
而
文件自然就是文件系统的最小管理单元 -
文件系统繁多,一般常见的有ext2/3/4,sysfs,rootfs等,但这些文件系统都遵循
VFS的统一抽象接口 -
Linux文件系统无论采用哪一种,一定离不开两大数据结构:Index Node和Directory Entry
Index Node(INode)
-
即文件索引节点,用于保存文件的元数据(文件类型,权限,UID,时间戳,大小等)
-
它存储了除文件名和文件数据之外的所有信息
-
Linux在创建文件系统时,也会创建大量的INode。文件引用的是一个INode号,当用户搜索/访问一个文件时,Linux会通过INode表来找到正确的INode编号,通过这个编号来访问相应的INode。目录是目录下的文件名和文件INode号的映射。 -
INode编号在分区级别是唯一的,每个分区都有自己的INode表。如果两个文件的文件名和其它等等都一样,我们能正常打开这两个文件的依据是他们的INode编号不同。如果INode编号用完了,即使分区上还有剩余空间,我们也无法创建新文件 -
既然之前说过
Linux下一切皆文件,那么,我们的字符,设备等等,其实都是有属于它们的INode编号的 -
INode在分区级别是唯一的,故当分区不同时,两个文件可以拥有相同的INode编号 -
我们可以通过一个简单的命令查看文件相应的
INode编号
# 仅查看INode
ls -i
# 列出文件的所有信息,第一个是INode
ls -li
-
硬链接和原文件拥有相同的
INode编号,如果删除原文件,原来的数据仍可以用硬链接访问。这里的删除原文件,其实只是删除了指向该INode编号的其中一个名称,链接到这个INode的数据仍然可用。只有删除掉与其关联的所有名称,我们才无法访问指定的数据 -
软链接的创建实际上是建立了一个新的文件,即使它指向一个原有的文件,它也与原文件的
INode的编号不同
Directory Entry
- 目录项,记录了文件名,
INode指针,层级关系(parent)等信息 - 创建硬链接实际上就是创建了一个新的
DEntry,INode索引号相同
Key-Value
世间一切都是Key-Value,Key是你的身份证,Value是你的内涵
- 最典型/常见的例子如
Java中的Map:
Map<String, int> map = new HashMap<String, int>();
// 存储
map.put("ming",1);
map.put("zi",2);
//取值
int result = map.get("ming");
LSM-Tree
-
使用
Key-Value存储方式的典型数据结构,许多NoSQL都基于LSM-Tree构建 -
英文全称:Log-Structure Merge-Tree
-
Log-Structure:日志文件追加写入数据
-
Merge-Tree:一般分为两层,内存和磁盘。每次写入时将数据写入到内存中,当积累到一定程度时,使用归并排序的方式将内存的数据合并追加到磁盘队尾
-
-
优势:由于物理结构限制,磁盘的随机操作速度很慢,顺序访问却很快。如何将磁盘的这一优势取长补短?最好的办法便是一次性读取或写入固定大小的一块数据,并尽可能地减少随机寻道这个操作的次数(有点像我们的
Buffer,不是吗?)。LSM-Tree充分利用了这一特点实现,以日志的形式批次操作数据,牺牲了一部分读的性能来换取绝佳的写性能。前文提过NoSQL在大数据量的情况下表现更佳,作为大多数NoSQL实现的基底,LSM-Tree被设计来提供比传统数据库B+ Tree/ISAM更好的写操作吞吐量,通过消去随机的本地更新来提高性能。 -
多用于写多读少的场景
RocksDB
- 使用
Key-Value存储方式的著名高性能嵌入式数据库,是Google LevelDB的一个重要分支。基于C++编写
分布式存储
- 在单机存储的基础上实现了分布式协议,涉及大量的网络交互
兴起原因
-
成本,成本,还是TMD成本:互联网业务的发展十分迅速,使得存储系统无法依靠传统的纵向扩展的方式:先买小型机,不够用时再买中型机,甚至大型机超大型机。每次设备的迭代都要涉及迁移升级和备份等诸多问题,更何况不断升级的设备所消耗的资金更是大量的。现代的互联网后端都是分布式系统,要求支持横向扩展,即通过增加普通PC服务器来提高系统的整体处理能力。
-
软件需求:普通PC服务器性价比高,但故障率也高,需要在软件层面实现自动容错,保证数据的一致性。
-
线性扩展:随着服务器的加入,需要在软件层面实现自动负载均衡,使得系统的处理能力可以线性扩展。
HDFS(Hadoop Distribution File System)
-
堪称大数据时代的基石
-
核心特点:
-
支持海量数据存储
-
高容错性
-
弱
POSIX语义 -
使用普通
x86服务器,性价比高
-
-
如果客户端想要读取数据,首先从
NameNode获取该文件的位置(指向哪个DataNode),再到具体的DataNode中拿到 -
各部分角色:
-
NameNode:管理整个文件系统的元数据,以及每一个路径(文件)所对应的数据块信息
-
DataNode:管理用户的文件数据块,每一个数据块都可以在多个
DataNode上存储多个副本 -
Secondary NameNode:监控
HDFS状态的辅助后台程序,每隔一段时间获取HDFS状态快照,辅助NameNode管理元数据信息
-
Ceph
-
开源分布式存储系统里的万金油
-
核心特点:
-
一套系统支持对象接口,块接口,文件接口,一切皆对象
-
数据写入采用主备复制模型
-
数据分布模型采用CRUSH算法
- 相比于HDFS,Ceph的扩展性更强,且无单点。当对云计算的大数据进行处理时,特别是离线批量处理,计算性能逊于HDFS
数据库
单机数据库
- 单个计算机上的节点系统。数据库中的事务可以在单机内执行,也可以通过网络交互实现分布式事务。
关系型数据库
-
关系型数据库的通用组件
-
Query Engine:负责解析Query,生成查询计划
-
Txn Manager:负责事务的并发管理
-
Lock Manager:负责锁相关的策略
-
Storage Engine:负责组织内存/磁盘数据结构
-
Replication:负责主备同步
-
-
商业产品中,
Oracle的数据库独占鳌头 -
常见的开源产品:MySQL,PostgreSQL等
非关系型数据库
-
MongoDB,Redis,Elasticsearch三足鼎立
-
不同的非关系型数据库交互方式各不相同,数据结构千奇百怪,因为没有关系约束,scheme相对灵活
-
都在尝试支持SQL(子集) 和 事务
Elasticsearch
-
面向文档存储
-
文档可序列化为JSON,支持嵌套
-
存在index,index就是文档的集合
-
存储和构建索引的能力依赖Lucene引擎
-
实现了大量的搜索数据结构和算法
-
支持RESTFUL API,支持弱SQL交互
MongoDB
-
面向文档存储
-
文档可序列化为JSON,支持嵌套
-
存在collection,collection就是文档的集合
-
存储和构建索引的能力依赖wiredTiger引擎
-
4.0之后开始支持事务(多文档,跨分片多文档等)
-
常用client/SDK交互,可通过插件转译支持弱SQL
Redis
-
数据结构丰富(hash表,set,zset,list)
-
纯C实现,超高性能
-
主要基于内存,支持AOF/RDB持久化
-
常用redis-cli多语言SDK交互
分布式数据库
-
解决问题
-
容量问题:单点存储的容量有限,受硬件限制。分布式数据库将存储节点的数据库池化,可以动态地伸缩容量。
-
弹性问题:单点存储的升级/降级需要大幅度的迁移数据,且缩容时各个磁盘的冗余问题无法解决。将节点池化后,软件层可以自由调度存储空间,用户并不需要关心底层的内存细节,容量由分布式系统决定。
-
性价比问题:对单点存储的机型进行扩容后,
CPU资源浪费会及其严重(更大的磁盘需要更好的CPU)。存储池中的CPU无需升级,当CPU资源不够时,可以从Server层直接升级/增加CPU,存储空间不够时,可以扩展存储池容量,无需当扩容时必须一对一配备更好的CPU。
-
-
新的问题
-
如何优化写入性能,使得多个用户可以共同操作数据?
-
在实现磁盘容量弹性的基础上,如何实现内存的弹性?
-
如何在分布式的基础上,提高对事务的处理能力?
-