【2.综述数据存储/计算】【精】

实在不知道起什么名字,打算写三个系列的文章,由于系统尽可能有状态和无状态分离,第一个想写下系统中涉及到数据存储/分析/选型/本身设计要怎么作,包含内存/持久,数据量单机到大量到海量分布式的存储和计算。第二个想写无状态系统如何作到高处理能力,涉及到网络/分流/通讯,进程/线程模型,操做系统IO,CPU调度等等。第三个为C++系列基础编码。
本篇为第一部分,先说下非海量数据存储选型应该考虑的点,本身设计也能够参考的点,对比了经常使用的非海量数据存储软件,这部分对数据模型,索引/存储,分布式的可靠性,可扩展,一致性给出总结性的解决方案。还介绍了海量数据(可能通常核心数据没这么多,但衍生数据量大)和流数据的存储和计算的总结性解决方案,这其中涉及众多组件,我只用过少部分,为了直观性,给出了几种常见衍生数据处理系统的应用设计。
系列文章涵盖了文中提到或未提到但常见的内容都分别单独篇章。node

第一部分 数据的存储

存储应该考虑的点:
   ——性能(存储介质,数据格式,数据组织,索引,cache)
   ——功能(索引,事务)
   ——一致性
   ——可靠性
   ——扩展性(对于scale up共享内存和磁盘部分忽略)
   ——成本(物理,维护)
其中成本是须要再各个指标中关联的,好比性能里介质压缩大小,内存占用,可靠性才会冗余备份等,不单独讨论了。

性能

影响性能的:存储介质,数据模型,索引,存储格式。mysql

存储介质 特色 数据组织
磁带
内存 map,set,list,skip-list,memory-table,stm(支持内存事务)
磁盘 顺序读写强,随机读写差,block -Tress =>B+ 层数同样,性能稳定,中间节点只有索引,容易缓存,数据只在子节点,数据能够扫描
SSD 随机性能高,并行度高,擦除影响寿命 SST(sst为什么适合SSD?SSD是并发写,适合一次写入大量,擦除影响寿命,但愿更新少,由于并发随机读写能力更强)
PCM

数据组织

  • 模型
    1.SQL 对象和关系不批配,ORM不能隐藏,支持XML/JSON转为行或者列
    2.文档型,多对1关系,文档对链接支弱,多对多=》文档引用(并不是全部文档性都支持mongodb不支持链接)。两者对于复杂关系都抛弃了旧的网络模型(网络模型必须搜索全部路径)
    文档读取只能读取一整条,没法直接获取第二项;使用文档的好处是:文档(即对象)对应于许多编程语言中的本机数据类型。嵌入式文档和数组减小了对昂贵链接的需求,//减小join的IO。动态模式支持流畅的多态性。
    3.图模型:当多对多特别频繁,社交图谱,网络图谱等。三元组存储。SPARQL
    与网络差异:无模式,任意记录类型的嵌套,经过惟一ID直接饮用任何节点,也能够创建索引查询定点,网络中只能经过范耐高温你路径,订点和边无序,查询支持SPARQL
  • 索引及数据存储方式
    1.文件追加加入+hash索引:
    hash索引key和偏移量:散列表必需要内存中放得下,不然须要大量随机访问,不支持范围查询
    分段,开启新段,压缩合并旧段。逐个向后找。旧段的索引、旧数据的删除能够经过偏移量最小点删除或者保持冗余
    考虑点:删除标记,崩溃hash索引的恢复,数据库中途崩溃,并发控制

    2.内存放不下,hash索引=》稀疏索引+有序
    SSTables:sorting string table 要求每一个键只在每一个段中出现一次且排序能够超出内存(随加载随merge),不须要保存全部键的索引(排序),范围查找
    如何让数据有序:磁盘上B树,内存中更容易:红黑树/AVL树
    LSM,日志合并,保存一系列在后台合并的SSTable,写入时,添加到内存的红黑树中。做为SSTable写入磁盘。关键在于会如何压缩和合并
    LevelDB 水平,HBase 大小分层
    3.从日志追加到就地更新=》B树
    写操做用新数据覆盖磁盘页面,并发控制复杂。要等一整页一块儿写,须要用额外的磁盘(redo log)[二次写,若不这样作须要控制块对其和大小写入512一次或标识头部等,实际上由于内存延迟刷新,WAL只要有内存延迟都配了]。LSM只是追加
    一些优化:修改页面写入不一样位置,父页面建立新版本(mongodb)。B+树等
    比较B树与LSM
    B 读取速度快。对于两个逻辑相近的页物理可能很远,随机读写多
    LSM 写入速度快,非页单位写入量大,都是顺序写。碎片少。LSM因为压缩更小。SSTbales在合并时复写(有时应为磁盘写入带宽等有限,有影响,压缩和写入速率控制)。
    4.其余索引:多列索引(B树和LSM都不能很好的支持);空间,二维填充曲线转为单个数字再用B树索引,或者用特殊化的空间索引R树;全文索引/模糊索引,lucene
    5.内存中存储一切:性能+实现特殊的磁盘和索引难以实现的数据模型好比队列。=》反缓存web

  • 分析=》大量扫描索引不重要了,解决IO问题。压缩
    上面的模型和结构在事务中很常见,在分析系统中,有些经常使用过简单的模型和索引等
    星型和雪花型
    列存储:行巨多但每次只查询少数列。=》列压缩,避免修改的LSM
  • 数据格式
    内存中:针对cpu和操做优化,树、列表
    文件/网络传输:编码,json/xml/csv/二进制(thrift,protocal buffer)
    须要考虑:数据表更改兼容(数据库中的数据,REST/RPC/Mq中的数据)
    这部分详见:https://segmentfault.com/a/11...

功能

功能各个系统本身针对不一样会有很大差异(排序,聚合,搜索等),通用的功能只有事务,这里作下讨论,其余特性能够查API,功能和使用通常文档都比较好查,不仔细说了redis

事务

  • 单对象,日志崩溃恢复和回滚实现原子性+锁实现隔离性+CAS实现复杂的自增原子操做,可是事务更多指多对象
  • 原子性保证:停止后能够安全重试。无主复制的数据存储尽力而为遇到错误不撤销
    但重试还可能的问题:
    若是服务器已经成功但返回超时,可能成功两次
    错误因为负载过大形成,重试会形成更大的
    仅在临时性错误后才值得重试,永久性错误重试没没有意义
    事务后还反作用,好比发送邮件,几个系统一块儿,二阶段提交
  • 隔离
    1.脏写:行写锁
    2.脏读:锁代价大=》持有写入锁能够设置新值,读旧数据。读已提交的隔离级别,只须要保留一个版本
    3.可重复读:A事务过程当中读取数据是一致的,即便该数据在期间被B修改,实现:MVCC。读已提交为每一个查询使用单独的快照,可重复读对整个事务使用相同的快照。记录建立和删除的事务号,对大事务号的写入操做都忽略。索引能够指向全部版本,再过滤,能够全部版本存储在一个节点。还有仅追加,不更新,建立新的树分支,不须要再写另外存储(mongodb),mysql则在回滚段中恢复。
    4.关于幻读:网上的资料都是错的,MVCC能够保证读的正确性,会找旧版本(mysql是undo恢复)。可是对写不保证,由于写会取最新的,只能用锁保证,好比虽然事务中select * 取不到另外一个事务的,可是Insert可能会冲突报错,select * for update也会读到B事务的,由于读取是最新的。若要保证更新没问题用select for update加锁(select * for update读最新加间隙锁)。这种先select检查再update的行为致使会议室预约等问题。幻读实际上是写误差。当能够间隙所时加锁,不能好比!=1
    5.丢失更新:A select value=2后update value=value+1。可是期间B事务insert value=3。此时value=4都是由于写都是最新的。这种问题mysql可重复读不保证,要么数据库加排它锁保证整个事务原子,要么显示锁定好比for update,代价较大。或者CAS,update value=value+1 where value=3;
    分布式丢失更新?容许多节点并发,不能用锁和CAS=>冲突解决
    6.串行化:单线程(扩展性很差,分布式要一个一个等,为了提升性能能够一个分区一个CPU一个事务线程,对于夸多分区的须要分区锁,尤为二级索引,吞吐量差)
    7.锁
    两阶段锁定:读共享锁写升级为排他锁,写要等全部的读释放,读必须等写释放,常常发生死锁。锁粒度控制:谓词锁(性能很差,符合搜索条件的再加锁)=》间隙锁(范围或全表)
    SSI 序列化快照隔离:悲观锁=》乐观锁(先执行到提交时再检查是否与隔离违背,违背则停止重试)。快照隔离+检测写入序列化冲突。(读以前存在未提交的写入)MVCC的读在提交时检查是否有被忽略的写入已经被提交。写入数据时通知全部最近读取的其余事务(读以后发生写入)

扩展性

  • 分片
    hash(事先分多)
    hash+键值组合
    一致性hash(不推荐,写很差)
    range(hbase 热点问题,分配不均,扫描和范围查询)
  • 二级索引:
    1.按主键ID范围分区,写入本地分区二级索引,合并读取。好比a的color放a机,按color查须要合并a和b
    2.全局按关键词索引,索引自己分布式。color自身分布式,每次写入a要写更新a和b机器的color索引,写慢读快。由于跨分区事务问题和通常异步更新二级索引,二级索引有不一样步问题(不一致或部分更新失败,我认为分别是一致性问题和事务原子性问题)
  • 分区再平衡:
    1.固定分区数:若是用hash%n的形式(须要移动所有数据),须要提早分配更多,按照子集扩展,好比。虽然只有4个节点,开始就设置N为20
    2.动态分区数,按数据范围,相似B树节点的合并和拆分。mongodb的范围和hash都是动态分球
    3.固定节点数。每一个节点有n个分区。新加入节点随机获取几个旧节点中几个的分区的一半组成新的分区,剩余的留在原来分区中,增长节点会增长分区数量,为了均衡随机选择分区拆分,要求分区边界基于散列的分区。(最符合一致性hash的语义)。好比卡桑德拉https://issues.apache.org/jir...,一致性hash基础上,每一个节点256个虚拟节点,配置范围,新增节点将其余节点的部分范围分给新的vnodes(好比256分82%~102%)
    4.CRUSH这种(3须要记录具体配置,或者其余负载均衡都须要记录对应配置)不须要记录配置,hash到虚拟gid上个,gid与节点id一块儿产生随机数,全部节点id中选择随机数*权重最大的。随机数是伪随机,固定的,新增只须要把更大的移走,减小只须要移动到次大的。
  • 元数据部署
    路由决策组件:节点,单独路由,客户端
  • 分区并行查询问题后续再说

可靠性

单独说可靠性指异常补偿,涉及到复制,故障发现,故障自动转移。比较简单。有些副本提供读服务还有多写副原本提高可用性,有时存储的可靠性和可用性能够一块儿解决,简单说下这种部署和延时问题,详细的一致性在下面说。算法

  • 目的:考虑高可用的备份,高性能的负载,地理就近进行数据复制,通常三个副本能够保证11个9,两副本大规模下3个9必丢数据
  • 运行:同步,异步(绝大多数领导者形式都是这个),半同步:一个追随者同步,其余异步,至少有两个节点拥有最新的数据副本
  • 扩容:设置新从库,从主库拉取历史快照,从主库读取落后新数据直到commentid一致(须要log)
  • 故障发现,主动上报/心跳/lease
  • 故障恢复:
    1.从库,日志点
    2.主库:确认主库失效(须要考虑超时设置多少合理?)
    选择新主库 (脑裂=》单网络通道,奇数,仲裁/抢锁通道)
    更新客户端和其余从库配置
    旧主库恢复数据冲突处理
  • 复制方案:
    基于语句的。不肯定性处理rand().now(),自增(mysql5.1之前),紧凑
    基于物理的。用于主存储或崩溃恢复。记录数据底层磁盘块字节更改,与存储紧密耦合,存储格式更改版本,若复制协议不支持匹配版本,须要停机处理
    逻辑日志,行复制。更新:惟一标识+全部列新值
    触发器(只复制部分)
  • 复制延迟:几种保证:读写一致性(本身更新本身马上可看),单调读(同一用户屡次读取一致),一致性前缀读(保证读取顺序按写入顺序),最终一致性。
    前三个一致性解决方法举例:更多内容见一致性。
    1)读主库,刚更新数据读主库,记录时间戳;本身请求读主库,注意不一样网络和设备,分布打到多个数据中心的须要路由到主库的数据中心
    2)同一用户读固定从库,防止更旧
    3)依赖事务有因果关系的写入 同一个分片。不一样分片不保证顺序一致
  • 部署:
    1.单主
    2.多主,只有多活多数据中心用到。或者须要每一个本地离线写入。

    clipboard.png
    多活多数据中心与单活对比:单活每一个写入都要进入主库数据中心,增长写入时间;经过网络中心的写是同步的,对网络容忍度比多中心异步复制低不少;故障一个主从切换,一个换主。
    处理写入冲突:写入检测两个主违背多主目的,写入都成功后同步时检测冲突用户没法补救。
    并发写入:哪些须要覆盖(好比依赖关系的B知道A先发生,只须要覆盖便可),哪些是并发(B不知道A的发生,并发须要按版本解决或者容许丢失用时间或序号。)
    =》
    预防冲突:同一写入只入一个,在故障等时特殊处理(须要切换,保证不了同一个请求不丢失或不重复)。
    冲突合并:每一个写入或副本一个惟一ID,a.覆盖丢弃,b.维护多版本,自动处理的数据类型:集合等,带多版本等。
    b.版本向量:返回时将读取自的版本返回,再次写入时带该版本的就是依赖(原本知道有这个版本),可覆;不然是并发保留保本(原本不知道有这个版本)。sql

    clipboard.png

    3.无主:所有多读多写,返回数量足够成功。(读写法定人数的确认)
    单个落后如何恢复:读取时选择版本高的反更新(读修复)或 异步不断同步差别(反熵,不保证顺序)
    写入冲突和多主同样mongodb

一致性

困难:丢包\延迟,时钟不一样步。会致使设计上再延时(同步,半同步,异步)和一致性(线性一致性,原子事务提交)上有必定取舍,现实中模型:同步模型,假设网络延迟,进程暂停,时钟偏差都有界限;部分同步:大多数同步,偶尔变得至关大;异步模型:不能用超时,没有时钟。崩溃-中止故障:中止后永远不回来\崩溃恢复故障:未知时间后再次开始响应,节点具备稳定的存储且会保留/内存会丢失。拜占庭故障:包括试图戏弄和欺骗其余节点。能够实现不一样等级的一致性:如ACID,最终一致性,sessioin一致性,单调一致性
CAP是针对一条数据的(同一个系统数据可根据要求作不一样处理)。一致性的定义应该是普遍的(不仅是副本之间数据相同),能够理解为对一条数据获取的一致性,多我的多同一条数据读结果一致,一个事务对同一数据读结果一致,惟一性读取一致(惟一被获取其余读应该失败)
写入顺序与实际一致 不属于一致性范畴但应尽可能保证才使得读有意义,也归到这里讨论。叫因果一致性
最强的一致性是线性一致性(一旦写入成功读取的就是该值,直到再次覆盖)
所以涉及到问题大概有:因果一致性保证,线性一致性保证,事务一致性保证,惟一性约束
  • 因果一致性
    lamport时间戳,一个客户端在多个写节点中的顺序保证,多节点写入时保证同步时因果覆盖正确。解决同一客户端发出对同一操做两个有序请求,最终到两个主库上,序号(到达是有序的,但时钟不可靠)不必定哪一个在后,因此同步时有错的问题。办法就是每次请求带节点最大序号,更新落后的节点。(不需去取全局递增序号)
    在纸上画了一下,与其余方案对比(不一样写入按时间戳,分奇数偶数都会使得最终多副本顺序和真实一个客户端写入顺序不一样)。不方便画。注意和上面多版本不要紧呦,一个是单数据库控制多版本并存解决冲突,一个是对同一cli的多数据库写提供顺序sid保证安全覆盖,不一样cli仍是要两个version(知道因果的有序)

    只是提供同步序号问题,只有同步解决才会获得全局数据,读数据调的应用需阻塞在全局回复上数据库

  • 全序广播
    顺序在消息传递时被固化,不容许将消息插入到顺序中较早位置。全序广播保证以固定顺序可靠的发送,不保证消息什么时候被送达.
    全序广播实现线性一致性存储apache

    向全序日志中追加一个惟一的用户名,读日志,等待消息被送回时执行。用一个全序日志作同步(C读,A读,B写,A写、每一个写所有节点都执行,读要等写都执行完,是本身调的返回客户端)
    写一致性:若该日志第一条消息全部权是本身的,确认提交(给客户端返回)。如有并发写入,全部节点会对最早到达者达成一致。非本身节点也执行的
    读取一致性:当有读取时追加一条消息,消息回送时读取日志,执行实际的读取返回。用全序控制线性,串行。
  • 线性一致性:包含因果性(从概念上是读取最新的写入,可是写入有延迟是可接受的,读取不变便可)
    线性一致性是新鲜性的保证,读取必定能看见最新的写入值
    线性一致性存储实现全序广播
    每次全序广播发送首先从线性一致执行原子自增返回(好比寄存器执行自增并返回)操做,做为序号附加到消息中,将消息发送到全部节点,收件人按照序号连续处理消息。与lamport时间戳不同,一致性存储数字没有间隙,若是节点发送了4而且收到6,在传递6以前必须等5再发送6。
    全序广播和线性一致都等价于共识
  • 分布式事务原子提交
    意义:在客户端或者proxy或者C赞成提交后挂掉(不考虑回来,回来补偿)的状况下,让分布式的提交原子性,要么不commit,一个commit就全commit
    前提:2pc,3pc都是要一致(崩溃是最终一致),CL之间不通讯。CL崩溃不恢复(恢复能够单独用日志,即保证在线节点一致便可)。一旦commit没法rollback
    对应mysql语句:update1 end1 prepare1;update2 end2 prepare2; commit1->commit2/rollback1->rollback2
    1.二阶段提交

    clipboard.png

    clipboard.png
    第一阶段:收集协商,若参与者返回OK,即保证undo落盘可提交可回滚了。第二阶段把redo落盘或者undo回滚
    若是协调者在准备好后失败,不得不等待他从新恢复。协调者上的常规单点提交。协调者有问题,锁没法释放棘手问题.即便协调者从新第二阶段没法判断是否是要提交,1在线可是只能知道本身yes,若2失败没法获取2的投票和状态。
    缺陷:协调者单点,引入协调者可能使得服务器再也不是无状态的不能随意扩容,当夸各类数据系统时,须要时全部系统的最小集不能检测系统间死锁,没法SSI等,数据库内部的分布式事务(其实非XA)没有3问题可是系统任何部分失败都会失败 扩大失效=》改用共识编程

    2.三阶段分布式原子提交

    clipboard.png

    只要有一个进入precommit就说明确定能够commit,不然不会commit。
    二阶段严重依赖C,节点不能根据自身投票决定,C和CL1在图中位置都挂后,新C不知道是否是都赞成,会使得pre无心义。三阶段能够在加锁后不依赖C释放。Pre能说明投票的状况,因此有了p能够自动工作,新C根据P决定do,若是C2和C同时失败,此时C1收到pre则继续没收到C1会abort,C2确定也没有commit。Pre时间必定要设置很长,保证C能够判断集群p的信息,不然若是出现pre最终有收到有没挂没收到自动超时仍是有问题。
    对于网络分区能够用共识解决,恢复重启须要同步日志

  • 共识
    详细见单独:https://segmentfault.com/a/11...
    全序广播至关于重复多伦共识。

常见数据存储开源方案

1.redis、codis

性能

内存,多种内存数据结构

功能

无索引,基本不支持事务

redis的分布式

1,代码中写;
2,redis Cluster。请求不在的key要两次,先返回ip再请求一次
3,代理分片,好比tuemproxy,codis

redis cluster

1.主从模式。一主多从

  • 可靠性:没法自动故障转移
  • 无扩展性
  • 复制:BGSAVE命令生成一个RDB文件+缓冲区命令
  • 同步:
    写命令传播给从服务器
    每秒一次频率向主服务器发送REPLCONF ACK <replication_offset>进行心跳检测。检测网络和命令丢失。(主服务器配置min-slaves-to-write n, min-slaves-max-lag m当从服务器数量少于3个,或者延迟大于等于10将拒绝执行写命令根据replication_offset检测是否丢失命令,补发命令)
    断点续传:replication_offset,复制积压缓冲区,服务运行ID

2.哨兵模式。哨兵系统也是一个或多个特殊的redis服务器,监视普通服务器,负责下线主服务器和故障转移

  • 可靠性:自动故障转移(哨兵经过raft协议选主,主哨兵选择主服务器)。
    1.相互发现
    2.sentinel默认1s/次的频率向全部主/从/sentinel服务器发送PING命令,有效回复为+PONG,-LOADING,-MASTERDOWN。当一个实例在down-after-milliseconds内,连续向sentinel返回无效回复,sentinel修改实例中flags加入|SRI_S_DOWN标识主观下线
    3.当接收到认为下线的sentinel数量超过quorum则flags加|SRI_O_DOWN
    4.第一个标主观的发起选主,成为主哨兵
    5.故障转移
    领头进行故障转移
    1) 选出新的主服务器
    在线的,5s内回复过INFO的,与主服务器断开链接时间足够短,优先级高,复制偏移量大,runid最小的,发送SLAVEOF no one,以1s/次(其余是10s/次)的频率向该服务器发送INFO。当role变为master时继续2
    2) 向下线的主服务的其余从服务器发送SLAVEOF命令
    3) 向旧的主服务器发送SLAVEOF命令
  • 无扩展性

3.集群模式。去中心化,增长可扩展性,每一个能够读写

  • 可靠性:
    gossip协议,主从自动故障转移,gossip通讯,从节点发现故障,raft从新选主
  • 扩展性:16384个槽。以槽为单位,从新分片
    指派槽与节点
    key与槽:CRC16(KEY) & 16383
    读写到任意节点, 二次转移
    从新分片:redis-trib
  • 元数据:全部节点保存

codis

扩展性

分片:hash
元数据:codis-proxy中,用codis-dashboard控制,zk保持同步
扩展:固定1024个slot。迁移是按照slot的维度
迁移有两个阶段,第一阶段状态改成pre_m。若proxy都确认,将状态改m。向所在的redis-server发送迁移命

可靠性

codis-proxy的用zookeeper保证。client获取zk节点作负载均衡
codis-group的主从用redis的哨兵模式

一致性

分片信息和元数据由zk保证一致性,group中主从由redis自身负责最终一致性

详细redis与codis见:
https://segmentfault.com/a/11...

2.mysql、proxy

性能

磁盘,B+树索引(搜索性能高稳定,节点不包含数据能够包含更多地址,层高少,叶节点链表扫面呢)/主键聚簇索引,内存buffer(二级索引change buffer)
buffer到磁盘过程当中问题:
刷脏 flush
二次写 脏页落盘须要二次写,redolog块对其不须要
清理过时 purge

功能

事务
A undo log,逻辑日志,受redo log保护 。涉及回滚
C (一个事务中间状态可见性一致)MVCC 每一个视图有readviewid。经过undo日志恢复旧视图
I (多个事物之间可见性/操做不干扰)MVCC
D redolog 物理位置+逻辑日志。每一个事务本身buffer=>公共buffer=>磁盘 涉及checkpoint
server和innodb的binlog和redolog的一致性保证:内部二阶段提交
分布式事务

分布式

没有本身的集群管理.须要自行实现
主从 用binlog复制(基于行,语句,混合);采起同步/半同步/异步;全局GTID代替文件名和物理偏移量使得slave在多线程并发复制时崩溃恢复不会重复执行相同事务操做(GTID这种方式能够避免重复发,可是找起来没有文件和物理位置方便,须要记录集合,用GTID-sets有序合并存储xx:1-120这种形式,并发避免不了不能用位置由于多线程不按是顺序执行)。

扩展性

当主库支撑不了。水平扩展。拆表。

可靠性

没法自身保证

一致性

同步策略影响。
XA分为外部和内部。对于外部。要应用程序或proxy做为协调者。(二阶段提交协调者判断全部prepare后commit)。对于内部,binlog控制。
同步和事务失败回滚会有问题,先提交发送网络异常致使主库有从库无。等发送返回后提交失败回滚致使从库有主库无。

详细:
https://segmentfault.com/a/11...

3.fusion

没有开源。基于cache+rocksdb。

性能

介于redis和rocksdb以前。对rocksdb的热key多了一层cache。

功能

想结合redis的性能,mysql的持久化。redis和mysql的集成。支持分布式。

分布式

master读写都在master.slave备份做用。同一个slave和master不在一台机器上
把全部结构都转为纯粹K-V。rocksdb只负责存储kv
分片和redis同样,1024个固定,每一个集群管理部分

扩展性

迁移:rocksdb文件快照,内存快照。slot迁移,增量记录。当迁移结束直接返回false换路由(最后一个增量时间在一个qps就能够),增量后merge.

可靠性

复制:用rocksdb扫描key+多线程发送,同步成功点记录
同步:WAL。采起同步成功删除的方式。同步后用redis命令放入slot中
主备切换:codis的proxy感知切换

一致性

proxy用zk保证一致性
主备一致性WAL同步保证

4.leveldb/rocksdb

详细:https://segmentfault.com/a/11...

性能

SSD+SST。在正常的读写性能写入14M/S(32核),写比读稍好

功能

ACID。
原子:WAL
隔离:版本快,常规的读只会读sequenceid以前的,内存中内容用sid控制,文件中version来组织,每次sid引用的version。

分布式

leveldb没有任何分布式。bigtable是chubby(分布式锁)+单机lebeldb。
rocksdb提供了基本的备份,增量备份,恢复,同步,事务日志,两阶段提交的接口支持。能够本身搭建proxy

5.mongodb

wiredTiger引擎
详细:https://segmentfault.com/a/11...

性能

B树,buffer,文档(磁盘)
修改操做在持久化时在新页中,不会对旧页有影响,成块写入不须要二次写
比较占内存(一个链接一个线程,tcp链接500个就1G,引擎数据cache,新写数据,备节点差别buffer,长事务快照,夸多集合时的排序)
运行模型:每一个链接一个线程,限制栈1M。虽然线程多切换代价大,但后台都是IO操做,代价还好。请求调用引擎层的方法

功能

K-V
单机AD WAL(journal)+checkpoint
CI 未提交事务快照(同mysql,只有读用,写仍是要最新的页)

分布式

全量同步+oplog增量同步
主从同步:oplog 幂等(incr会转为set),循环覆盖,
顺序保证:写入 oplog前,会先加锁给 oplog 分配时间戳,并注册到未提交列表里,正式写入 oplog,在写完后,将对应的 oplog 从未提交列表里移除,在拉取 oplog 时若未提交列表为空,全部 oplog 均可读,不然只能到未提交列表最小值之前的 oplog
Secondary 拉取到一批 oplog 后,在重放这批 oplog 时,会加一个特殊的 Lock::ParallelBatchWriterMode 的锁,这个锁会阻塞全部的读请求,直到这批 oplog 重放完成

扩展性

存储/读写qps
集合分片:分片范围,hash,tag(机房)
hash能够预先分配多个。同一时刻抢锁以后又一个mongos会迁移。迁移期间原请求阻塞排队,返回给mongos从新请求

可靠性

复制集模式:故障检测恢复。成员间心跳,选举primary(Bully算法)。driver与复制集合心跳

一致性

  • 路由(客户端,或mongos或单独的connfig server)
  • 数据:oplog保证

第二部分 衍生数据的处理

原数据的存储介绍了工具,介质,结构等。实际上系统中还包括对源数据处理,好比缓存,计算。计算涉及到分布式要并行处理,流数据计算等。这一章节说下分布式下数据的处理

批处理

1.MPP

并行执行SQL。缺点:仅支持sql,倾向于内存中保存尽可能多数据,最多分钟级别。分布式文件系统的map-reduce或其余优化计算方式,数据多样性,不只关系或文档;查询不限于SQL(HIVE在此之上封装了SQL);文件系统能够包含MPP风格的或OLTP如HBase

2.map/reduce

unix管道处理的思想,sort内存溢出放入磁盘,输入输出与逻辑分离/统一接口/可复用 =》map/reduce
输入输出为分布式上的文件HDFS

clipboard.png
好处:数据网络传输和计算分离。永远数据完备后才执行mapper或reducer;重试,失败回滚都中间态存储。
针对数据倾斜,每次都要map数据+reduce逻辑处理有一些优化。好比
正常都在reduce链接。某些能够在mapper链接优化,好比大数据与小数据的连接,将小数据广播打到mapper内存,mapper和reduce分区相同时,mapper只须要单独单个分区。若分区原本有序,能够直接在能够在此完成reduce的工做
举例:mapper根据须要对文档集合分区,reducer建立该分区的索引,并将索引你文件写入分布式文件系统。增量索引写入新的段,并在后台压缩与合并。就不详细说了。

基于这种处理思想,整个批处理的构建系统hadoop:

clipboard.png
hdfs是存储系统(见https://segmentfault.com/a/11...
yarn是资源管理(见https://segmentfault.com/a/11...
hadoop的高级该工具hive(见https://segmentfault.com/a/11...),能够自动组装多个mapreduce阶段
构建推荐系统等,在线使用,mapreduce入单独数据库。好比HBase(见:https://segmentfault.com/a/11...

3.spark/flink

把整个工做流做为单个做业处理。替代map/reduce用算子,算子之间的链接能够有:记录从新分区排序/分区/广播链接。若中间态丢失,从先前中间态或原始数据从新计算。spark用弹性分布式数据集抽象跟踪数据的谱系,而flink对算子状态存档,容许在执行过程当中你那个遇到错误的算子。许多不用排序的能够流水线方式执行。好比组合分区的map+reduce(groupby,sort等)做为一个subtask,另外一个分区map+reduce(groupby,sort等)+reduce2(sink组合)为subtask2。1与2并行,2的reduce2等1。在map-reduce模式下是map1,map2并行,reduce分为很groupby1,groupby2,而后再map1,map2。再sort1,sort2。
Spark的技术理念是基于批来模拟流的计算。而Flink则彻底相反,它采用的是基于流计算来模拟批计算。
详细见:因为批处理和流处理的总体划分思路(对分布式数据任务的拆分方式)是同样的。所以批处理和流处理的spark,flink写在一块儿了,详细见:spark(https://segmentfault.com/a/11...、flink(https://segmentfault.com/a/11...

流处理

对分布式数据的处理方法和上面同样,只说下流里边特别的

1.流处理的存储和传递

上述基于数据有界=》数据无界,增量处理。批处理输入是数据文件,须要考虑流处理的存储和传递?上述的分布式文件系统再也不行:增量要轮询,轮询的越频繁,能返回新事件的请求比例就越低,而额外开销也就越高。 相比之下,最好能在新事件出现时直接通知消费者(数据库的触发器功能有限)=》

传递事件流——消息系统

设计点:费速度跟不上?丢弃,缓冲,背压。节点故障?
1.1 直接发送:UDP组播,ZeroMQhttps://segmentfault.com/a/11...),HTTP或者RPC(webhooks,一种服务器的url被注册到另外一个服务中)。容错能力有限
1.2 消息代理:负载均衡、扇出,并发确认重传
1)RabbitMQ,代理将单调消息分配给消费者,确认后删除(https://segmentfault.com/a/11...
2)基于日志的消息代理:使用日志消息存储。kafka。每一个分区有序,每一个消息单调递增偏移量,要记录消费取的偏移量(https://segmentfault.com/a/11...
适用于消息吞吐量高,处理迅速,顺序很重要(能够单分区全分能给某个负载均衡的线程)

数据与衍生数据的同步

数据库/缓存/索引/数据仓库
双写:1.两个客户端两个系统相互覆盖=》版本向量检测并发写入;2.一个成功一个失败(原子)
选择一个为领导者,好比数据库,让其余系统做为追随者。

变动数据捕获,将其提取并替换为能够复制到其余系统中的形式的过程
事件溯源,事件仅追加。日志压缩仅保存最新版本。通常的删除都是使数据不能取回.能够链接数据库和衍生数据,使其做为主。
可是解决了覆盖但由于异步,还会有原子问题,使用分布式事务

时间与窗口

1.事件事件仍是处理时间?
处理时间:处理有问题,重启后处理大量堆积的
事件时间:不知道1分钟内的最后到达会在几分钟后=》丢弃或发布更正
若要准确的事件时间(即事件在设备上发起时间,记录该时间,发往服务器时间,服务器收到时间),经过从第三个时间戳中减去第二个时间戳,能够估算设备时钟和服务器时钟之间的偏移(假设网络延迟与所需的时间戳精度相比可忽略不计)。而后能够将该偏移应用于事件时间戳,从而估计事件实际发生的真实时间(假设设备时钟偏移在事件发生时与送往服务器之间没有变化)。在spark中会介绍google基于水位线的事件发生事件
2.窗口
滚动窗口,按分钟,每一个事件仅属于一个窗口。
跳动窗口,有重叠固定。
滑动窗口,5分钟内任意时间开始。
会话窗口,无会话关闭
3.容错
没法等待任务完成后根据输出错误处理=》微批量spark,存档点flink
详细见spark(https://segmentfault.com/a/11...
flink(https://segmentfault.com/a/11...

应用

clipboard.png

  • 离线统计、数据量大(天级别)。kafka=>hive 自行查询出报表,表小分钟级别也出来了
  • 分钟内级别统计。kafka=>HBASE
  • 实时简单搜索

    clipboard.png

  • kafka=>ES(ES算是数据或衍生数据的主要提供索引外加一些聚合,单独篇章详见:https://segmentfault.com/a/11...
  • s级别实时报警: kafka=>spark/flink(要求更快能够加druid)=>实时监控+预测模型
  • odin监控采集:采集,tsdb,druid。
相关文章
相关标签/搜索