分布式存储认知

1.分布式存储认知总览

这里说的分布式存储,不是特指某个系统,而是泛指具有以下两个特性的组件:

•具备对某种数据模型进行存储的功能(增删改查)

•分布式

已有的常见数据模型有:表模型、文档模型、kv模型、消息模型,当然也可以按需设计新的数据模型。本文只涉及已有的数据模型。

举例:哪些组件具有对某种数据模型进行分布式存储的能力?

• 传统RDBMS的分库分表架构,可以对表模型数据进行分布式存储。

• Elasticsearch的文档存储部分,可以对文档模型数据进行分布式存储。

• Redis的cluster部署模式,可以对KV模型数据进行分布式存储。

• Kafka,可以对消息模型数据进行分布式存储。

思考:对于分布式存储,它们是怎么衍生出来的?在高维度上的共性是什么?在设计上有没有一些通用维度?

1.1 衍生过程及高维度上的共性思考

分布式存储有一些通用的设计维度:模型设计、存储设计、索引设计、网络通信设计、高可用设计和高可扩展设计。这些维度的衍生过程及包含的核心技术点如下图所示:

分布式存储衍生过程及共性思考


后续将对这6个维度依次展开简要说明。

1.2 通用架构模型

分布式存储系统可能采用的架构模型如下图所示:

分布式存储通用架构

2.数据模型

要存储数据集,那么,首先需要明确这些数据可能的应用场景,针对应用场景,为数据选取一个合适的模型。

目前常见的数据模型有:表模型、文档模型、KV模型、消息模型。数据存储底层的核心就是基于这些数据模型的。

数据模型概览如下图所示:

数据模型概览

2.1 Schema元数据

选用某种数据模型之后,需要对这种模型的数据进行描述,以便知道要处理的数据的结构。也就是对应模型的schema信息,亦即元数据。元数据先定义,后使用。

定义了schema之后,在接收到数据时,结合元数据,就可以知道数据的结构信息,然后可以作出相应的处理。比如在表模型中,可以知道数据有哪些字段、字段名是什么?字段类型是什么?数据要插入的表存储在哪个文件上等等。在其它模型中也是相似的。


Schema元数据

2.2 Schema元数据在表模型中的应用示例

CREATE TABLE `product` (

  `id` BIGINT(20)NOT NULL AUTO_INCREMENT,

  `name`VARCHAR(32) DEFAULT NULL,

  `type`VARCHAR(32) DEFAULT NULL,

  `desc` VARCHAR(255) DEFAULT NULL,

  `create_time` DATETIME DEFAULT NULL,

  PRIMARY KEY(`id`)

) ENGINE=INNODB DEFAULT CHARSET=utf8;

INSERT INTO product(`name`, `type`, `desc`, `create_time`)

VALUES('荣耀手机', '手机',

'1亿像素超清影像 5G 6.57英寸超曲屏 66W超级快充 3200万像素高清自拍

全网通版8GB+128GB 初雪水晶',  '2021-07-09');

SELECT * FROM product WHERE NAME='荣耀手机';

表元数据定义好以后,进行CRUD时会使用到元数据。

2.3 Schema元数据在文档模型中的应用示例

在Elasticsearch中定义索引的schema信息示例如图下图所示:

Elasticsearch索引schema定义

插入文档数据:

写入文档

搜索文档:

query DSL查询

2.4 Schema元数据在KV模型中的应用示例

Redis内置全局哈希表,及Value的多种数据结构,可以看作元数据信息是内置的

Value各种数据类型对应的操作API见redis官网https://redis.io/commands

2.5 Schema元数据在消息模型中的应用示例

在java领域,万物皆对象,RocketMQ中,元数据可以定义为各个对象。比如定义TopicConfig、topicConfigTable、TopicRouteData、Message等等。

后续消息的接收,存储都会使用到元数据。

消息定义


消息定义2

3. 存储模块

数据的持久化,依赖于文件系统(更底层来说就是磁盘)来存储和缓存消息,正常来说,磁盘随机读写是比较慢的。

普通的IO操作在OS层面是如何执行的?


普通IO

为了提升数据存储的性能,同时保证数据的可靠性,需要采取一些优化措施,比如:磁盘顺序写、写内存+WAL日志、mmap、零拷贝等等。

3.1 磁盘顺序写、WAL预写日志

•顺序写磁盘

有关测试结果表明,一个由6 块7200r/min的RAID- 5 阵列组成的磁盘簇的线性(顺序)写入速度可以达到600MB/s ,而随机写入速度只100KB/s ,两者性能相差6000 倍。

顺序写磁盘的性能与写内存的性能是一个数量级的,因此磁盘文件的顺序追加写入效率很高。

•WAL预写日志

为了保证内存中的数据在系统崩溃后能恢复,可以使用 WAL技术(Write AheadLog,预写日志技术)将数据先高效写入磁盘进行备份。

WAL 技术保存和恢复数据的步骤可以归结如下:

1.程序在处理数据时,会先将对数据的修改作为一条记录,顺序写入磁盘的log文件中。

2.数据写入 log 文件后,就有了备份。该数据就可以长期驻留在内存中了。

3.系统会周期性地检查内存中的数据是否都被处理完了(比如,被删除或者写入磁盘),并且生成对应的检查点(Check Point)记录在磁盘中。然后,就可以随时删除被处理完的数据。避免log 文件无限增长。

4.系统崩溃重启后,从磁盘中读取检查点,获取最后一次成功处理的数据在log 文件中的位置。把这个位置之后未被处理的数据,从 log 文件中读出,重新加载到内存中。

通过这种预先将数据写入 log 文件备份,并在处理完成后生成检查点的机制,就可以使用内存来存储和检索数据了。


WAL与ChangeBuffer

3.2 mmap及其在RocketMQ中的应用

mmap技术,也就是内存映射。直接将磁盘文件数据映射到内核缓冲区,这个映射的过程基于DMA引擎拷贝,同时用户缓冲区是跟内核缓冲区共享一块映射数据的,建立共享映射之后,就不需要从内核缓冲区拷贝到用户缓冲区了。


mmap

代码演示mmap在RocketMQ中的使用

mmap使用示例

3.3 零拷贝

零拷贝是指将数据直接从磁盘文件复制到网卡设备中,而不需拷贝到应用程序。零拷贝大大提高了应用程序的性能,减少了内核和用户模式之间的上下文切换。

对Linux操作系统而言,零拷贝技术依赖于底层的sendfile()方法实现。对应于Java语言,FileChannel.transferTo()方法的底层实现就是sendfile()方法。

零拷贝技术通过DMA( Direct Memory Access )技术将文件内容复制到内核模式下的ReadBuffer 中。但是没有数据被复制到Socket Buffer,只有包含数据的位置和长度的信息的文件描述符被加到Socket Buffer 中。DMA 引擎直接将数据从内核模式中传递到网卡设备(协议引擎)。数据只经历了2 次复制就从磁盘中传送出去了, 并且上下文切换也变成了2 次。


零拷贝

4. 索引模块

当系统中存储的数据量比较大的时候,如何高效的将所需的数据取出来?

索引的出现是为了提高数据检索的效率,本质上是一种用于快速查找的数据结构。

可以用于提高读写效率的数据结构有很多,简要介绍几种比较常见的数据结构。

举例如下图:

索引可选数据结构举例

4.1 哈希索引


哈希表

4.2 B+树


B+树

4.3 倒排索引

倒排索引的具体实现是哈希表,它不是将文档唯一标识ID作为key,而是反过来,将内容或者属性作为key来存储对应的文档列表,这样可以在O(1)的时间复杂度内完成对包含key的文档列表的查找。


倒排索引

图中只是对倒排索引的示例,实际可能根据分词器的不同,分词后不一定是图中所示的那些关键字。

4.4 LSM树

在写大于读的应用场景下,尤其是在日志系统和监控系统这类应用中,我们可以选用基于LSM 树的NoSQL 数据库,这是比 B+树更合适的技术方案。

LSM 树具有以下3 个特点:

1.将索引分为内存和磁盘两部分,并在内存达到阈值时启动树合并(Merge Trees)

2.用批量写入代替随机写入,并且用预写日志WAL 技术保证内存数据,在系统崩溃后可以被恢复

3.数据采取类似日志追加写的方式写入(Log Structured)磁盘,以顺序写的方式提高写入效率。

LSM 树的这些特点,相对于 B+树,在写入性能上有大幅提升。许多 NoSQL系统都使用 LSM树作为索引数据结构。

5. 网络通信框架

节点与节点之间要实现数据的交互,就离不开网络通信。

网络通信是一个复杂的过程,主要包括:对端节点的查找、网络连接的建立、传输数据的编码解码以及网络连接的管理等等,每一项都很复杂。这里只对实现网络通信的必要步骤做简要的说明。

为了避免在编码时,对涉及到网络通信的逻辑都进行一系列复杂的编码,屏蔽网络编程细节,需要对网络通信的过程做包装,构建一个网络通信框架。

实现网络通信的必要步骤:

1.制定通信协议:一般分为消息头和消息体,消息头一般包括:协议标识、数据大小、请求类型、序列化类型等信息,消息体主要是业务数据。

2.序列化编解码:发送端将对象序列化为二进制数据。接收端将二进制数据反序列化为对象。

3.网络传输二进制数据

5.1 自定义通信协议示例

rocketmq中,自定义协议抽象为对象RemotingCommand,这里只抽取其中部分的协议头,作为演示,示例如下图:

自定义协议头


自定义协议体

5.2 序列化编码示例


序列化编码示例

5.3 通信流程与模型抽象


通信流程与模型抽象

5.4 RocketMQ网络通信框架示例

代码演示如何徒手实现RocketMQ中的RPC框架,简便起见,目前只实现同步通信方式作为演示。

代码结构简要说明:


代码结构简要说明

测试代码位置说明:


测试代码

完整代码下载:如何徒手实现RocketMQ中的RPC框架

6.高可用集群支撑

高可用架构的实现,离不开副本思想,都有一定的相似相通性,但是也都有各自不同的技术实现,以及相对应的区别。

主分片:接收读写请求

副本分片:接收读请求,也可不对外提供服务,仅做容错用,主分片所在节点故障时,将其对应的副本提升为主分片(master、primary、leader)

数据恢复:原主分片所在故障节点重启,原主分片变为副本,并拷贝故障到重启恢复期间丢失的数据


高可用概览

6.1 副本思想在Mysql中的应用


mysql master-slave

6.2 副本思想在Redis中的应用

Redis 提供了主从库模式,以保证数据副本的一致,主从库之间采用的是读写分离的方式。

读操作:主库、从库都可以接收;

写操作:首先到主库执行,然后,主库将写操作同步给从库。


redis主从

6.3 副本思想在Elasticsearch中的应用

示例为索引创建一个Primary Shard,2个Replica Shard

每个Shard都是一个Lucene实例,有完整的创建索引的处理请求能力

数据只能写入到Primary Shard,Primary会将数据同步给Replica Shard


es高可用

6.4 副本思想在Kafka中的应用

为保证集群中的某个节点发生故障时,该节点上的 partition 数据不丢失,且kafka仍然能够继续工作,kafka 提供了副本机制。每个分区都有若干个副本,一个leader 和若干个 follower。

leader:对于每个分区,生产者发送数据的对象,以及消费者消费数据的对象都是leader。

follower:实时从 leader 中同步数据,保持和 leader 数据 的同步。leader发生故障时,某个follower 会成为新的 leader。

Kafka中的从实例follower,不对外提供服务,仅在发生故障时,某个follower会成为新的Leader


kafka高可用

7. 高可扩展集群支撑

高可靠的实现离不开分片思想。当数据量越来越大时,单机已经存不下,就需要将大的数据集进行分片存储,通过横向扩容来支撑海量数据的存储及高负载。

分片集群是一种通用的机制,不同的分布式存储系统对其有不同的实现。

比如在Redis中实现为Redis Cluster,对于传统的关系型数据库,可以通过分库分表手动实现一套切片集群,在Elasticsearch及Kafka等系统中也有对应的实现。

集群扩缩容时,为了避免迁移数据,往往将数据与切片的关系固定,由于数据到切片的算法不变,所以需要保持切片的数量不变。

比如rediscluster切片(哈希槽)的数量固定为16384,Elasticsearch中创建索引后,索引PrimaryShard的数量不能改变,如果要改变,需要重新建索引。

对于分库分表,扩容库表数量时,由于库表数量改变,所以涉及到数据迁移,对于分库分表可以预估后续可能的数据量,尽量分配充足的库表个数,避免后续数据迁移。

集群扩缩容时,会改变实例的个数,此时可以改变实例与切片的映射关系 。改变实例与切片的映射关系,不需要重新映射数据与切片的关系,不需要迁移数据。


映射关系的变与不变

7.1 数据分片在分库分表中的应用


mysql的高可用与高可扩展

7.2 数据分片在Redis中的应用

哈希槽数固定为16384,数据与切片(槽)的映射关系不变。

根据键值对的 key,按照 CRC16 算法计算一个 16 bit的值;然后,再用这个 16bit 值对16384 取模,得到 0~16383 范围内的模数,每个模数代表一个相应编号的哈希槽

Redis Cluster集群实例的数量是可变化的,集群扩缩容时,切片(槽)与实例的映射关系是变化的。涉及到哈希槽在节点上的移动。


redis的高可用与高可扩展

7.3 数据分片在Elasticsearch中的应用


Elasticsearch的高可用与高可扩展

7.4 数据分片在Kafka中的应用


kafaka的高可用与高可扩展

你可能感兴趣的:(分布式存储认知)