数据密集系统设计

一、计算密集与数据密集

1. 计算密集

有限数据,复杂计算,产生计算结果

2. 数据密集

海量数据查询/多数据系统综合协同/负责业务流程处理

 

OLTP(Online Transaction Process): 事务性处理,一次业务请求,得到结果/产生数据,多为在线业务系统,注重时效。

OLAP (Online Analysis Process):交互式分析,不断递进,多为离线分析系统,对时效依赖要求性没那么高。

 

二、系统指标

1. 可靠性

1)结果符合预期-功能正确

2)应对非预期输入【参数错误,权限认证】-容错

3)应对流量波动-弹性

硬件故障具有独立性,软件故障具有关联性,

2. 扩展性

AKF立方体,流量扩展维度-应用复制,应用维度-应用拆分,数据维度-数据分区。

3. 可维护性

三、数据模型

系统由多层模型组成,每层提供本层的简单抽象,隐藏实现的复杂性,上层组装下层的api,以高效协作。

二、数据存储查询

1. 数据库的基本功能

将指定数据存放起来,并能根据特定条件查询出特定的数据或返回不存在。【最简单的数据库可以用几行shell(echo/grep)实现】

2. 关系数据库/NoSql数据库/融合趋势

一对一关系:KV查询

一对多关系:索引查询

多对多关系:多表联合查询【一个人有多种职业,一个职业也可以对应多个人。】

 

3. 行存储/列存储

行存储,一行作为一个基本单位,物理上一行时放在一起的。要读取一行中的某列,必须先读取整行,在提取其中的一列。

列存储,一行可以有多列,以一列作为基本存放单位,相邻多行的同一列在物理上时临近的。要读取一行中的某一列,可以直接读取那一列所在的磁盘位置。

对于大数据分析场景,扫描提取一个范围区间的多行的某几列值,相当于在磁盘上的临近位置读取数据,相比行存储效率要高不少。

4. 数据库与数据仓库

数据仓库和数据库本来没什么不同,但是随着olap和oltp的分离,为了避免离线分析对在线业务的影响,离线任务对数据库的访问需求不能和在线业务共用,且离线任务需要的数据更全,更多,来源更广,一般将多来源数据流通过汇总后,形成适合离线分析使用的数据仓库中的数据格式。

5. 持久化与内存数据库

内存数据库,一般用作缓存,要求所有数据加载到内存。但内存数据库不意味着数据不需要持久化,也需要落盘的,以避免数据丢失和停机重启。但是内存数据库不用考虑从磁盘搜索指定数据,不用考虑磁盘索引等。所以不用设计lsm/btree等数据索引方式,只用考虑快速加载等就可以了。

6. LSM

实现

log structure merge,内存排序,然后快照落盘。磁盘数据分层合并。内存存放稀疏索引,块内数据有序。

查询数据时,两个层面,1. 稀疏索引确定数据块,块内二分查找。2. 先查内存中,内存中没有再查磁盘中。为了提高查询效率,减少无效的查找,采用bloom过滤器。

优点

1. 稀疏索引和全索引相比,节省内存,避免数据扩张索引挤爆内存

2. 数据局部有序,与全哈希策略相比,更容易命中缓存。【访问临近数据的概率相对较大】

3. 经常整理合并,磁盘碎片较少。

4. 数据写,磁盘顺序写,效率更高。

缺点

1. 磁盘上不同layer的数据中,内存数据中,会包含重复数据,

2. 每次查找必须挨着找一遍,浪费计算资源。

3. 后台数据整理线程,耗费计算资源。

4. 整理过程中,合并块数据double,资源占用翻倍

4. 后台整理造成写放大问题。

适合数据顺序读写的场景,尤其是物理磁盘,顺序读写比随机读写性能优势巨大。hbase,redis,mongo,scalla等nosql 都是采用此底层存储方式。

7. Btree

实现

一棵平衡树,每个非叶子结点将本结点所涵盖的区域分成多个子节点【一般256/512】,并包含指向子节点的指针。叶子结点包含实际的数据,直接落在磁盘上的某个位置。查找时,一层一层找下来,每层二分查找,便可找到最终的叶子结点上的数据。叶子节点一般存放一个磁盘页,4k。

4层btree举例,可以容纳数据量 512*512*512*512*4*1024=256T

优点

相比lsm,读数据效率更高,不用后台整理数据,不存在数据重复资源占用多的问题。

不存在多副本,加锁更容易,事务能力强。

缺点

数据覆盖时,如果原有数据空间不够,需要更改位置,并修改引用,容易造成磁盘碎片

 

传统数据库通用存储方式,sql数据库都采用此方式。

8. 空间多维数据索引

多位数据的存放,采用多维数据分割包含的策略,外层包含内层,形成一个搜索树,R树/KD树等。

9. 物理磁盘/SSD

磁盘访问耗时,一次物理询盘4-10ms之间,一次ssd磁盘访问0.xms。

10. 事务

10.1ACID

针对单个数据库的不同操作间的要求, 一个操作可以是修改一个数据,也可以是一组连续动作修改一组数据【事务】。

原子性,同一个操作要么完全成功要么完全失败,不存在中间状态。【2PL,两节点加锁】

一致性,不是多节点间同一份数据的一致,而是同一个节点一组关联数据间的一致性。【事务】

隔离性,不同操作间相互隔离,不会相互影响。【并发隔离,加锁,CAS】

持久性。 提交后数据不会丢失。【防机器崩溃,WAL】

 

锁:

锁库:最严格的,影响范围大,不实用

锁表:影响一个表

锁行:指影响某一行

锁索引:影响符合索引条件的所有行,适合同时读写符合某一条件的多份数据,影响范围小于锁表。实现难度大。【条件之间有包含关系,是否能穷举,是否有案例?

悲观锁:【适合多写少读场景】

读写锁:读共享,写独占。

独占锁: 完全互斥。

乐观锁:【适合多读少写场景】

写入时检查是否符合预期,如果不符合返回错误,符合则写入【CAS】

10.2 读-提交

只有提交后的数据才对其他读或写操作可见。

脏读/脏写。

实现方式:1. 行锁,可以防止脏写,但如果用在防止脏读,性能受不了。解决方式为写时保存新旧两个版本的数据,写只锁新数据,写之间互斥,不影响读旧的值。写提交后,替换旧的值。

 

此处读不加锁,就容易造成读倾斜,意思是读事务中的多个动作,有可能分别读到写事务提交之前和之后的值,综合起来数据就不符合实际,是一个错的数据。解决方案为快照隔离

 

10.3 快照级别隔离

每条数据保存多个版本,每个写事务分配一个ID,写入的数据的版本带上当前事务的ID。读事务只能读到本事务开始之前的所有已提交事务的数据。

多版本方案还可以实现删除回滚。【删除只是标记删除】

 

事务不能解决 更新丢失问题【两个线程同时递增计数器】,CAS可以,但CAS不能解决ABA问题,但串行化可以。

ABA的定义见:https://blog.csdn.net/huangquan__/article/details/116241700

10.4 串行化

串行化算法,两阶段加锁 2PL。读加共享锁,写加独占锁。

SSI 可串行化快照

乐观锁+多版本控制。每次基于一个快照版本写入,提交前检测是否过期,过期则失败,不过期则提交。

二、分布式系统

1. 数据密集分布式系统出现的原因

高可用/低延迟/可扩展

2. CAP

针对分布式系统不同节点间相互配合的三个要求。

一致性, 多节点返回数据状态一致。

可用性, 任何情况都有结果返回。

分区容忍性,节点间失去联系时,系统仍可用。

CA系统,允许脑裂,不管分区性,失去链接的节点不管状态。

CP系统,允许节点不返回,不管可用性,不能同步时,系统不可用,直到全部同步为止。

AP系统,允许不一致,不管一致性,允许不同节点返回不同数据。

3. 分布式事务:

2PC【两阶段提交】

1. 提交数据,2. 询问是否可以提交,3. 发出提交/回滚指令,4. 提交或者回滚。

主者决定第三步后,必须完成动作3,机器崩溃后也必须有恢复机制,不能回头。

从节点回复2后,必须等待3的指令并执行。如果主节点死掉,也必须等,并执行3的指令。不能回头。

 

由于有用不回头的状态,有时候可能造成死锁,必须认为介入。

3. 一致性/最终一致性/弱一致性/线性一致

分布式系统中,除非应用分布式事务,否则强一致性很难达成。但分布式事务性能较低,难以大规模使用。

最终一致性,只保证多节点最终会同步完成,但时间延迟不定,系统设计时应考虑业务需求和同步延迟的匹配。

弱一致性,1. 读自己的写,【是对特定业务的一些取巧操作,例如看自己的数据被路由到主节点,看别人的数据才路由,避免自己刚刚修改的数据看不到的问题给用户造成困扰。】2. 单调读一致性。【每个用户路由到固定的节点】

线性一致:各节点间可能状态不同,但保证状态改变的顺序是一致的。【版本号保证】【聊天程序,聊天上下文不能乱序,否则造成混乱】

 

4. 数据复制

4.1 主从复制

主写从读,只有一个主节点提供写功能。同步复制+异步复制,合理配置解决高可用和性能的权衡

4.2 多主复制

多个主节点,都负责写入,然后相互复制。

写冲突问题:1. 避免冲突,2. 合并冲突【时间戳,最后写入获胜,容易丢数据,节点时钟不同步】

3. 推给应用层解决。

4.3 无主复制

同时写多副本,同时读多副本,少数服从多数。也可以读时修复。

 

 

5. 并发【多客户端写入一个节点】

两件事情没有因果依赖关系,相互无法感知,就可以视为可并发事件,也可以说是独立事件。

LastWriteWin:丢弃并发写入,只保留最新值。

多客户端并发写入数据库,如何合并。可以采用多版本号,每次写入都要发送自己依赖的版本号,和新的值,数据库收到后用请求值分配最新的版本号,并覆盖原版本号。读取时,返回所有未被覆盖的版本号的值,客户端负责合并,并作为自己下次写入的基础。

6. 数据分区

 

1. 基于关键字的分区,拥有过良好的区间查询性,容易导致热点

2. 基于关键字哈希的分区,区间查询性能不好。

3. 复合主键,第一列哈希,其他列排序。适合一对多的模式。【cassandra】

二级索引:

1. 索引基于文档分区,查询索引时查询所有分区

2. 索引基于词条分区,查询索引时只需要查询一个分区。写入时需要更新多分区索引,慢。同步更新依赖事务不现实,多用异步更新模式。

7. 一致性

6.1 因果一致性,偏序

如何捕获因果关系,cas的用法,ssi的用法。

捕获全局因果关系难度不可想象。

6.2 线性一致性,全序,无并发。

序号排序,逻辑时钟

6.3 全序广播

所有节点所有操作遵循一套版本号,每个操作广播,每个节点将自己当前版本号返回,当前节点在最高版本号基础上加1,如此往复。【阻塞,节点多了不行,网络不稳定造成】

7. 冲突解决方案

7.1 2pl 解决单节点事务的串行化隔离问题,解决单节点并发问题。2pc 解决多节点分布式事务的原子提交问题。2pc中,参与者准备请求回复OK/FAIL,和协调者提交/放弃 决定的不可撤销性,保证了多节点分布式系统的原子提交。2pc+2pl合作才能解决 并发+分布式的问题。

7.2 快照隔离,多版本合并

7.3 乐观锁+快照隔离(可串行化快照隔离SSI)

8. 共识

多个节点对同一件事达成一致。

8.1 2PL 分布式事务,两阶段提交。【强一致性,性能弱,不符合A,有节点掉线则Block等】

8.2 共识算法,底层逻辑都是选举,一般采用广播+版本号确定主节点,然后采用主从复制来实现全局线性一致性。选举算法超过一半投票即生效,以保证可用性,不依赖全部节点

8.3 共识算法适合少数机器构成分布式协调系统【zookeeper,etcd等】

8.4 raft 分为两阶段,广播选举leader,leader作为主几点保证全局线性一致。每次提议,需要大于半数以上的节点同意,保证高可用性达成。所有节点随机的超时时间收不到心跳则发起竞选流程,每次竞选后递增epochnum,防止脑裂后旧的主节点误以为自己还是主节点。

 

三、派生数据系统

1. 批处理

unix管道处理思想,map+reduce。

map和reduce之间,多个批处理任务之间,通过hdfs文件做衔接接口。每一步都落hdfs盘。hdfs多副本提供给高可用性。

慢:每一步都要等待前一步完全完成,才能从hdfs文件开始新的一步。

容错:每一步都可以从hdfs临时文件重新拉起进行重试。

2. 流处理

无界数据的处理。算子之间直接衔接。

统计:时间窗口统计处理。

 

事件流发送系统:

消息系统的实现方式:1. 消息代理【JMS标准/AMQP标准】,负载均衡式/扇出式。系统中可以综合两种方式实现灵活的数据流的处理。

消费后删除【确认消费有副作用】

2. 分区日志:

通过偏移量记录订阅进度,客户端可以反复订阅。

3. 消息系统:

AMQ类型的处理,consumer消费消息后,producer会删除。所以有确认机制。

at-most-once:consumer处理提交前发送确认,如果发送确认后崩溃,下次不会在有消费处理

at-least-once: consumer处理提交后发送确认,如果提交后还没发送确认崩溃,则重启后还会在消费一次。

exactly-once: 和分布式系统遇到的问题一样,producer和consumer必须就每一次消费达成一致,可以使用两阶段提交来达成。

 

 

四、 hadoop架构

hdfs+mapreduce+yarn

五、Hbase

基于hdfs,存储结构采用lsm。顺序写,写性能比较高,读性能比较差。

 

附录:

服务器主频一般3.8GHZ【每秒3.8G次】

 

 

深度:

关联搜索,小型流式计算,为了极致性能,将kv数据的分片存储和计算节点放在同一台物理机上,qps30万以上。500万的数据,10台机器,2秒以内就可以完成。es hack 返回整个倒排。流式分发统计,小型mr。

meta数据的同步,计算阶段状态的同步,ssession的保存,程序内部也是启动不同的task,负责分发的,负责接收的,负责统计的。

时间比较紧的情况下,简化设计,无中心化,谁收到请求,谁就作为master,向其他机器分发任务。 之所以没有复杂化,第一探索阶段摸坑,第二oltp系统,尽量简化流程。

 

react 模型,程序内部的,spring的bean初始化的过程中,consumer 注册自己到一个队列queue,producer生产完之后,使用queue.active(event)来激活对event的各种处理,好处是,架构上分离了consumer,独立化,切可以多种组合。最终还是在一个线程中执行。

epool,事件通知机制,内核信号量等通知机制,一直挂起等待,有事件时收到通知去处理。

 

消息队列,kafka/mq。

轮询机制,某些场景下可以使用,简化整体架构对外部消息组件的依赖

posted @   bwuhpqt  阅读(150)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
点击右上角即可分享
微信分享提示