Finnnnnnn 2019-07-01
存储——性能(存储介质,数据格式,数据组织,索引,cache) ——扩展性(对于scale up共享内存和磁盘部分忽略) ——功能(索引,事务) ——一致性 ——可靠性 ——成本(物理,维护)
存储介质 | 特点 | 数据组织 |
---|---|---|
磁带 | ||
内存 | map,set,list,skip-list,memory-table,stm(支持内存事务) | |
磁盘 | 顺序读写强,随机读写差,block | -Tress =>B+ 层数一样,性能稳定,中间节点只有索引,容易缓存,数据只在子节点,数据可以扫描 |
SSD | 随机性能高,并行度高,擦除影响寿命 | SST(sst为何适合SSD) |
PCM |
散列表必须要内存中放得下
,否则需要大量随机访问,不支持范围查询2.内存放不下,hash索引=》稀疏索引+有序
SSTables:sorting string table 要求每个键只在每个段中出现一次且排序可以超出内存(随加载随merge),不需要保存所有键的索引(排序),范围查找
如何让数据有序:磁盘上B树,内存中更容易:红黑树/AVL树
LSM,日志合并,保存一系列在后台合并的SSTable,写入时,添加到内存的红黑树中。作为SSTable写入磁盘。关键在于会如何压缩和合并
LevelDB 水平,HBase 大小分层
3.从日志追加到就地更新=》B树
写操作用新数据覆盖磁盘页面,并发控制复杂。需要用哪个额外的磁盘(redo log)放着覆盖失败。LSM只是追加
一些优化:修改页面写入不同位置,父页面创建新版本(mongodb)。B+树等
比较B树与LSM
B 读取速度快。写面redolog和本身,页面维度覆盖
LSM 写入速度快,压缩分段检查不同SSTables。SSTbales的复写在合并(有时应为磁盘写入带宽等有限,有影响,压缩和写入速率控制)。LSM更小由于压缩
4.其他索引:多列索引(B树和LSM都不能很好的支持);空间,二维填充曲线转为单个数字再用B树索引,或者用特殊化的空间索引R树;全文索引/模糊索引,lucene
5.内存中存储一切:性能+实现特殊的磁盘和索引难以实现的数据模型比如队列。=》反缓存
多活多数据中心与单活对比:单活每个写入都要进入主库数据中心,增加写入时间;通过网络中心的写是同步的,对网络容忍度比多中心异步复制低很多;故障一个主从切换,一个换主。
处理写入冲突:写入检测两个主违背多主目的,写入都成功后同步时检测冲突用户无法补救。
并发写入:哪些需要覆盖(比如依赖关系的B知道A先发生,只需要覆盖即可),哪些是并发(B不知道A的发生,并发需要按版本解决或者允许丢失用时间或序号。)
=》
预防冲突:同一写入只入一个,在故障等时特殊处理(需要切换,保证不了同一个请求不丢失或不重复)。
冲突合并:每个写入或副本一个唯一ID,覆盖丢弃,维护多版本,自动处理的数据类型:集合等,带多版本等。
版本向量:返回时将读取自的版本返回,再次写入时该版本的就是依赖,可覆;否则是并发保留保本。
3.无主:全部多读多写,返回数量足够成功。(读写法定人数的确认)
单个落后如何恢复:读取时选择版本高的反更新(读修复)或 异步不断同步差异(反熵,不保证顺序)
写入冲突和多主一样
困难:丢包/延迟,时钟不同步。会导致设计上再延时(同步,半同步,异步)和一致性(线性一致性,原子事务提交)上有一定取舍,现实中模型:同步模型,假设网络延迟,进程暂停,时钟误差都有界限;部分同步:大多数同步,偶尔变得相当大;异步模型:不能用超时,没有时钟。崩溃-停止故障:停止后永远不回来;崩溃恢复故障:未知时间后再次开始响应,节点具有稳定的存储且会保留内存会丢失。拜占庭故障:包括试图戏弄和欺骗其他节点。可以实现不同等级的一致性:如ACID,最终一致性,sessioin一致性,单调一致性
CAP是针对一条数据的。一致性的定义应该是广泛的(不只是副本之间数据相同),可以理解为对一条数据获取的一致性,多个人多同一条数据读结果一致,一个事务对同一数据读结果一致,唯一性读取一致(唯一被获取其他读应该失败)
写入顺序与实际一致 不属于一致性范畴但应尽量保证才使得读有意义,也归到这里讨论。叫因果一致性
最强的一致性是线性一致性(一旦写入成功读取的就是该值,直到再次覆盖)
因此涉及到问题大概有:因果一致性保证,线性一致性保证,事务一致性保证,唯一性约束
全序广播
顺序在消息传递时被固化,不允许将消息插入到顺序中较早位置。全序广播保证以固定顺序可靠的发送,不保证消息何时被送达.
全序广播实现线性一致性存储
向全序日志中追加一个唯一抢的用户名 读日志,等待消息被送回 写一致性:若该用户所有权第一条消息是自己的,确认提交。若有并发写入,所有节点会对最先到达者达成一致 读取一致性:当有读取时追加一条消息,消息回送时读取日志,执行实际的读取。其他等待直到该位置前的所有消息都传达到你,再执行读取。或者可以从哪个同步更新的副本中读取
如果协调者在准备好后失败,不得不等待他重新恢复。协调者上的常规单点提交。
协调者有问题,锁无法释放棘手问题
2.三阶段分布式原子提交
所有消息处理要保证可重试
缺陷:协调者单点,引入协调者可能使得服务器不再是无状态的不能随意扩容,当夸各种数据系统时,需要时所有系统的最小集不能检测系统间死锁无法SSI等,数据库内部的分布式事务(其实非XA)没有3问题但是系统任何部分失败都会失败扩大失效=》改用共识
redis单机基础内存
跳表-list
无索引,基本不支持事务
1,代码中写;
2,redis Cluster。请求不在的key要两次,先返回ip再请求一次
3,代理分片,比如tuemproxy,codis
1.主从模式。一主多从
可靠性:无法自动故障转移
无扩展性
复制:
主服务器BGSAVE命令生成一个RDB文件
,并使用缓冲区开始记录写命令
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.创建,根据给定的配置文件,初始化sentinel的监视主服务器列表,创建连向主服务器的网络连接命令连接,订阅连接(在建立后发送SUBSCRIBE __sentinel__:hello,sentinel需求通过接收其他服务器发来的频道信息发现未知的sentinel)
2.sentinel默认10s一次向主/从服务器发INFO命令
3.sentinel默认2s/次用命令连接向所有服务器(发现其他哨兵)发送 PUBLISH __sentinel__:hello
4.sentinel默认1s/次的频率向所有主/从/sentinel服务器发送PING命令,有效回复为+PONG,-LOADING,-MASTERDOWN。当一个实例在down-after-milliseconds内,连续向sentinel返回无效回复,sentinel修改实例中flags加入|SRI_S_DOWN标识主观下线
5.当接收到认为下线的sentinel数量超过quorum(sentinel moniter 127.0.0.1 6379 2中2设置)则flags加|SRI_O_DOWN
6.通过SENTINEL is-master-down-by-addr 看来是要分开进行,带runid。
每个发现主服务器进入客观下线的sentinel向其他sentinal发送命令
在一个配置epoch中将先到的设为局部领头,不能再更改。
接收回复检查epoch的值和自己的相同就取出leader_runid,如果发现自己被半数以上选择,则成为领头,epoch+1
如果在规定时间内未选举成功,epoch+1重新选举
7.故障转移
领头进行故障转移
1) 选出新的主服务器
在线的,5s内回复过INFO的,与主服务器断开连接时间足够短,优先级高,复制偏移量大,runid最小的,发送SLAVEOF no one,以1s/次(其他是10s/次)的频率向该服务器发送INFO。当role变为master时继续2
2) 向下线的主服务的其他从服务器发送SLAVEOF命令
3) 向旧的主服务器发送SLAVEOF命令
无扩展性
集群模式。去中心化,每个可以读写,
可靠性:gossip协议,主从自动故障转移,gossip通信,从节点发现故障,raft重新选主
扩展性:16384个槽。以槽为单位,重新分片
指派槽与节点
key与槽:CRC16(KEY) & 16383读写到任意节点, 二次转移
重新分片:redis-trib
架构
图片描述
分片: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...
磁盘,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在换主复制时不会重复执行相同事务操作。
当主库支撑不了。水平扩展。拆表。
无法自身保证
同步策略影响。
XA分为外部和内部。对于外部。要应用程序或proxy作为协调者。(二阶段提交协调者判断所有prepare后commit)。对于内部,binlog控制。
同步和事务失败回滚会有问题,先提交发送网络异常导致主库有从库无。等发送返回后提交失败回滚导致从库有主库无。
详细:
https://segmentfault.com/a/11...
没有开源。基于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同步保证
SSD+SST。在正常的读写性能写入14M/S(32核),写比读稍好
ACID。常规的读只会读sequenceid之前的,内存中内容用sid控制,文件中version来组织,每次sid引用的version。写会抢锁再写入成功后才更新seq+1,其他写隔离,并且本次写入内seqid也不会读到之前的。并发写入会批量分配sid段。每个事务一个writebatch的sid,只能读到最原始sid前的
leveldb没有任何分布式。bigtable是chubby(分布式锁)+单机lebeldb。
rocksdb提供了基本的备份,增量备份,恢复,同步,两阶段提交的接口支持。可以自己搭建proxy
WAL
事务日志。
详细:https://segmentfault.com/a/11...
使用文档的好处是:
文档(即对象)对应于许多编程语言中的本机数据类型。
嵌入式文档和数组减少了对昂贵连接的需求。//减少join的IO
动态模式支持流畅的多态性。
运行模型:每个连接一个线程,限制栈1M。虽然线程多切换代价大,但后台都是IO操作,代价还好。请求调用引擎层的方法
引擎
wiredTiger(简称WT)支持行存储、列存储以及LSM等3种存储形式,Mongodb使用时,只是将其作为普通的KV存储引擎来使用,mongodb的每个集合对应一个WT的table,table里包含多个Key-value pairs
B树,buffer,文档(磁盘)
修改操作在持久化时在新页中,不会对旧页有影响,成块写入不需要二次写
比较占内存(一个连接一个线程,tcp连接500个就1G,引擎数据cache,新写数据,备节点差异buffer,长事务快照,夸多集合时的排序)
K-V
单机AD WAL(journal)+checkpoint
CI 未提交事务快照,只有读用,写还是要最新的页
全量同步+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保证
原数据的存储介绍了工具,介质,结构等。实际上系统中还包括对源数据处理,比如缓存,计算。计算涉及到分布式要并行处理,流数据计算等。这一章节说下分布式下数据的处理
并行执行SQL。缺点:仅支持sql,倾向于内存中保存尽量多数据,最多分钟级别。分布式文件系统的map-reduce或其他优化计算方式,数据多样性,不仅关系或文档;查询不限于SQL(HIVE在此之上封装了SQL);文件系统可以包含MPP风格的或OLTP如HBase
unix管道处理的思想,sort内存溢出放入磁盘,输入输出与逻辑分离/统一接口/可复用 =》map/reduce
输入输出为分布式上的文件HDFS
好处:数据网络传输和计算分离。永远数据完备后才执行mapper或reducer;重试,失败回滚都中间态存储。
针对数据倾斜,每次都要map数据+reduce逻辑处理有一些优化。比如
正常都在reduce连接。某些可以在mapper连接优化,比如大数据与小数据的链接,将小数据广播打到mapper内存,mapper和reduce分区相同时,mapper只需要单独单个分区。若分区本来有序,可以直接在可以在此完成reduce的工作
举例:mapper根据需要对文档集合分区,reducer创建该分区的索引,并将索引你文件写入分布式文件系统。增量索引写入新的段,并在后台压缩与合并。就不详细说了。
基于这种处理思想,整个批处理的构建系统hadoop:
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...)
把整个工作流作为单个作业处理。替代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 直接发送:UDP组播,ZeroMQ
(https://segmentfault.com/a/11...),HTTP或者RPC(webhooks,一种服务器的url被注册到另一个服务中)。容错能力有限
1.2 消息代理:负载均衡、扇出,并发确认重传
1)RabbitMQ
,代理将单调消息分配给消费者,确认后删除(https://segmentfault.com/a/11...
2)基于日志的消息代理:使用日志消息存储。kafaka
。每个分区有序,每个消息单调递增偏移量,要记录消费取的偏移量(https://segmentfault.com/a/11...)
适用于消息吞吐量高,处理迅速,顺序很重要(可以单分区全分能给某个负载均衡的线程)
数据库/缓存/索引/数据仓库
双写:
1.两个客户端两个系统相互覆盖=》版本向量检测并发写入;一个成功一个失败(原子)
2.选择一个为领导者,比如数据库,让其他系统作为追随者。
变更数据捕获,将其提取并替换为可以复制到其他系统中的形式的过程 事件溯源,事件仅追加。日志压缩仅保存最新版本。一般的删除都是使数据不能取回.可以连接数据库和衍生数据,使其作为主。 但是解决了覆盖但因为异步,还会有原子问题,使用分布式事务
1.事件事件还是处理时间?
处理时间:处理有问题,重启后处理大量堆积的
事件时间:不知道1分钟内的最后到达会在几分钟后=》丢弃或发布更正
若要准确的事件时间(即事件在设备上发起时间,记录该时间,发往服务器时间,服务器收到时间),通过从第三个时间戳中减去第二个时间戳,可以估算设备时钟和服务器时钟之间的偏移(假
设网络延迟与所需的时间戳精度相比可忽略不计)。然后可以将该偏移应用于事件时间戳,
从而估计事件实际发生的真实时间(假设设备时钟偏移在事件发生时与送往服务器之间没有
变化)
2.窗口
滚动窗口,按分钟,每个事件仅属于一个窗口。跳动窗口,有重叠固定。滑动窗口,5分钟内任意时间开始。会话窗口,无会话关闭
容错
无法等待任务完成后根据输出错误处理=》微批量spark,存档点flink
详细见spark(https://segmentfault.com/a/11...、flink(https://segmentfault.com/a/11...
天级别离线统计。kafaka=>hive 自行查询出报表
分钟级别。kafaka=>hive/mapreduce或直接入HBASE
实时简单搜索
kafka=>ES(ES算是数据或衍生数据的主要提供索引外加一些聚合,单独篇章详见:https://segmentfault.com/a/11...)
woator: kafaka=>spark/flink=>实时监控+预测模型
odin监控采集:采集,tsdb,druid。