写点什么

极客时间架构师训练营 1 期 - 第 6 周总结

用户头像
Kaven
关注
发布于: 2020 年 11 月 01 日

分布式数据库

Mysql部署

Mysql主从复制

主从复制(也称 AB 复制)允许将来自一个MySQL数据库服务器(主服务器)的数据复制到一个或多个MySQL数据库服务器(从服务器)。复制是异步的 从站不需要永久连接以接收来自主站的更新。



Mysql一主多从复制

  • 分摊负载

  • 专机专用 (多个从库,可以分别作为不同用途使用)

  • 便于冷备

  • 高可用(部分读的高可用)



Mysql主主复制

master节点高可用,但并没有增加写并发能力和存储能力。




Mysql的注意事项

  • 主主复制的两个数据库不能并发写入;

  • 复制只是增加了数据的读并发处理能力,但没有增加写并发能力与存储能力;

  • 更新表结构会导致巨大的同步延迟,推荐运维人员在合适的时机手工同步更新。




数据分片



数据拆分

一般是通过分库分表来实现数据拆分的。



  • 垂直拆分:根据业务的维度,将原本的一个库(表)拆分为多个库(表), 每个库(表)与原来的结构不同。

  • 水平拆分:根据分片(sharding)算法,将一个库(表)拆分为多个库(表),每个库(表)依旧保留原有的结构。



在互联网应用中,通常先进行垂直拆分为多个微服务,然后在微服务中再进行水平库(表)拆分。



数据库进化的阶段

  • 单库单表

  • 单库多表

  • 多库多表(增加数据库实例,增加数据库(schema))



分库分表方案

  • 客户端分片

硬编码分片

映射表外部存储

  1. 在应用层直接实现

通用简单的解决方案,直接在应用层读取分片规则,解析分片规则,根据分片规则实现切分的路由逻辑。应用层直接决定每次操作应该使用哪个数据库实例,数据库以及哪个数据库表等。

优点:实现简单,性能更高,有问题时容易定位与修复。适合快速上线

缺点:业务侵入性,开发人员能力要求高。数据库保持的连接比较多,具体要看应用服务器池的节点数量,提前做好容量评估。

开源项目dbsplit

  1. 通过定制JDBC协议实现

通过定制JDBC协议来实现,针对业务逻辑层提供与JDBC一致的接口,让业务开发人员不必关心分库分表的实现,让分库分表在JDBC的内部实现,对业务层保持透明。

优点:与1差不多,不过,无业务侵入性,让开发与分库分表的配置人员在一定程度上可以分离,更大限度聚集在业务逻辑实现上。

缺点:开发人员需要理解JDBC协议才能实现分库分表逻辑;具体需要考虑SQL语法等,实现比较复杂;

流行的客户端分库分表框架Sharding JDBC则采用该方案

  1. 通过定制ORM框架实现

通过在持久化框架ORM中定制或者扩展实现分库分表方案,一般公司会通过在Mybatis配置文件中的SQL中增加表索引的参数来实现分片的。

  • 代理分片

代理分片就是在应用层与数据库层增加一个代理层,把分片的路由规则配置在代理层。代理层对外提供与JDBC兼容的接口给应用层,应用层的开发人员不用关心分片规则,只需关心业务逻辑的实现,

优点:让应用层的开发人员专注于业务逻辑的实现,把分库分表的配置留给代理层做;

缺点:增加代理层,尽管是轻量级的转发协议,但实现JDBC协议的解析,并通过分片的路由规则来路由请求,对每个数据库操作都增加一层网络传输,性能有影响;有实施与运维成本。需专业人员。



  • 支持事务的分布式数据库



如OceanBase,TiDB等对外提供可伸缩的体系结构,并提供一定的分布式事务支持,将可伸缩的特点与分布式事务的实现包装到分布式数据库内容实现,对使用者透明,使用者不需要直接控制这些特性。



金融交易系统一般比较保守,与钱比较敏感,更偏向选择对事务支持性比较好的关系型数据库。 而分布式事务更适合实现非交易系统,如:大数据日志系统,统计系统,查询系统, 社交网站等。



数据库分片的挑战
  • 需要大量的额外代码,处理逻辑因此变得更加复杂。

  • 无法执行多分片的联合查询。

  • 无法使用数据库的事务。 (跨库事务难以实现、同组数据跨库问题)

  • 随着数据的增长,如何增加更多的服务器。(扩容与迁移问题)




分布式数据库中间件

TDDL

TDDL不同于其它几款产品,并非独立的中间件,只能算作中间层,是以Jar包方式提供给应用调用。属于JDBC Shard的思想,网上也有很多其它类似产品。

社区TDDL处于停滞状态



Amoeba

Amoeba是作为一个真正的独立中间件提供服务,即应用去连接Amoeba操作MySQL集群,就像操作单个MySQL一样。从架构中可以看来,Amoeba算中间件中的早期产品,后端还在使用JDBC Driver。

社区Amoeba处于停滞状态



Cobar

Cobar是在Amoeba基础上进化的版本,一个显著变化是把后端JDBC Driver改为原生的MySQL通信协议层;

后端去掉JDBC Driver后,意味着不再支持JDBC规范,不能支持Oracle、PostgreSQL等数据。但使用原生通信协议代替JDBC Driver,后端的功能增加了很多想象力,比如主备切换、读写分离、异步操作等。

社区Cobar处于停滞状态



MyCat

MyCat又是在Cobar基础上发展的版本,两个显著点是

  1. 后端由BIO改为NIO,并发量有大幅提高

  2. 增加了对Order By、Group By、limit等聚合功能的支持(虽然Cobar也可以支持Order By、Group By、limit语法,但是结果没有进行聚合,只是简单返回给前端,聚合功能还是需要业务系统自己完成)。

MyCAT社区非常活跃

MyCat分片规则

  1. 枚举法,根据枚举ID来配置分片规则

  2. 固定分片的Hash算法

  3. 范围约定

  4. 求模法

  5. 日期列分区法

  6. 通配取模

  7. ASCII码求模通配

  8. 编码指定

  9. 截取数字哈希解析

  10. 一致性哈希(基于虚拟节点) 需要使用到双写方案平滑迁移




题外话:

抛开TDDL不说,Amoeba、Cobar、MyCAT这三者的渊源比较深,若Amoeba能继续下去,Cobar就不会出来;若Cobar那批人不是都走光了的话,MyCAT也不会再另起炉灶。所以说,在中国开源的项目很多,但是能坚持下去的非常难,MyCAT社区现在非常活跃,也真是一件蛮难得的事。



数据库部署方案
  • 单一服务与单一数据库

  • 主从复制实现伸缩

  • 两个Web服务及两个数据库

  • 综合部署(主从复制,分片等结合)




NSQL

CAP原理

  • 一致性Consistency

一致性是说,每次读取的数据都应该是近写入的数据或者返回一个错误(Every read receives the most recent write or an error),而不是过期数据,也就是说,数据是一致 的

  • 可用性Availability

可用性是说,每次请求都应该得到一个响应,而不是返回一个错误或者失去响应,不过 这个响应不需要保证数据是近写入的(Every request receives a (non-error) response, without the guarantee that it contains the most recent write),也就是说系 统需要一直都是可以正常使用的,不会引起调用者的异常,但是并不保证响应的数据是 新的。

  • 分区耐受性Partition tolerance、

分区耐受性说,即使因为网络原因,部分服务器节点之间消息丢失或者延迟了,系统依 然应该是可以操作的(The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes)。

当网络分区失效发生的时候,我们要么取消操作,这样数据就是一致的,但是系统却不可用要么我们继续写入数据,但是数据的一致性就得不到保证

对于一个分布式系统而言,网络失效一定会发生,也就是说,分区耐受性是必须要保证 的,那么在可用性和一致性上就必须二选一。

当网络分区失效,也就是网络不可用的时候,如果选择了一致性,系统就可能返回一个 错误码或者干脆超时,即系统不可用。如果选择了可用性,那么系统总是可以返回一个 数据,但是并不能保证这个数据是新的。

所以,关于CAP 原理,更准确的说法是,在分布式系统必须要满足分区耐受性的前提下, 可用性和一致性无法同时满足。

ACID与BASE(酸与碱)

ACID

  • 原子性(Atomicity): 事务要么全部完成,要么全部取消。如果事务崩溃,状态回到 事务之前(事务回滚)。

  • 隔离性(Isolation): 如果2个事务T1 和T2 同时运行,事务T1 和T2 终的结果是 相同的,不管T1和T2谁先结束,隔离性主要依靠锁实现。

  • 持久性(Durability): 一旦事务提交,不管发生什么(崩溃或者出错),数据要保存 在数据库中。

  • 一致性(Consistency): 只有合法的数据(依照关系约束和函数约束)才能写入数据 库。



BASE

  • 基本可用(BasicallyAvailable)系统在出现不可预知故障时,允许损失部分可用性,如 响应时间上的损失或功能上的损失。

  • Soft state(弱状态)软状态,指允许系统中的数据存在中间状态,并认为该中间状态的 存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步 的过程存在延时。

  • Eventually consistent(终一致性)指系统中所有的数据副本,在经过一段时间的同步 后,终能够达到一个一致的状态,因此终一致性的本质是需要系统保证数据能够达 到一致,而不需要实时保证系统数据的强一致性。



CAP 原理与数据一致性冲突

最终一致性

最终一致写冲突:简单冲突处理策略:根据时间戳,后写入覆盖。

客户端冲突解决:合并处理

投票解决冲突(Cassandra ):投票选举

Hbase中Log Structed Merge Tree(LSM树)

LSM树的核心特点是利用顺序写来提高写性能,但因为分层(此处分层是指的分为内存和文件两部分)的设计会稍微降低读性能,但是通过牺牲小部分读性能换来高性能写,使得LSM树成为非常流行的存储结构。

LSM-tree起源于 1996 年的一篇论文《The Log-Structured Merge-Tree (LSM-Tree)》,今天的内容和图片主要来源于 FAST'16 的《WiscKey: Separating Keys from Values in SSD-conscious Storage》。

先看名字,log-structured,日志结构的,日志是软件系统打出来的,就跟人写日记一样,一页一页往下写,而且系统写日志不会写错,所以不需要更改,只需要在后边追加就好了。各种数据库的写前日志也是追加型的,因此日志结构的基本就指代追加。注意他还是个 “Merge-tree”,也就是“合并-树”,合并就是把多个合成一个。

LSM-tree 最大的特点就是写入速度快,主要利用了磁盘的顺序写,pk掉了需要随机写入的 B-tree。

LSM主要包括写前日志 WAL(Write Ahead Log),memtable(内存中的数据结构),SStable(Sorted String Table,有序字符串表) 三个部分。逐层合并,逐层查找。LSM-tree 的主要劣势是读写放大,关于读写放大可以通过一些其他策略去降低。

LSM树有以下三个重要组成部分:

  • MemTable

  • Immutable MemTable

  • SSTable(Sorted String Table)

ACID与BASE(酸与碱

ACID

  • 原子性(Atomicity): 事务要么全部完成,要么全部取消。如果事务崩溃,状态回到 事务之前(事务回滚)。

  • 隔离性(Isolation): 如果2个事务T1 和T2 同时运行,事务T1 和T2 终的结果是 相同的,不管T1和T2谁先结束,隔离性主要依靠锁实现。

  • 持久性(Durability): 一旦事务提交,不管发生什么(崩溃或者出错),数据要保存 在数据库中。

  • 一致性(Consistency): 只有合法的数据(依照关系约束和函数约束)才能写入数据 库。

BASE

  • 基本可用(BasicallyAvailable)系统在出现不可预知故障时,允许损失部分可用性,如 响应时间上的损失或功能上的损失。

  • Soft state(弱状态)软状态,指允许系统中的数据存在中间状态,并认为该中间状态的 存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步 的过程存在延时。

  • Eventually consistent(终一致性)指系统中所有的数据副本,在经过一段时间的同步 后,终能够达到一个一致的状态,因此终一致性的本质是需要系统保证数据能够达到一致,而不需要实时保证系统数据的强一致性。






分布式一致ZooKeeper

分布式系统脑裂

在一个分布式系统中,不同服务器获得了互相冲突的数据信息或者执行指令,导致整个 集群陷入混乱,数据损坏,本称作分布式系统脑裂。



分布式一致性算法Paxos

  • 角色定义

Proposer 提案者

Acceptor 参与投票者

Learner 学习者

  • 过程

第一阶段:Prepare阶段。Proposer向Acceptors发出Prepare请求,Acceptors针对 收到的Prepare请求进行Promise承诺。

第二阶段:Accept阶段。Proposer收到多数Acceptors承诺的Promise后,向 Acceptors发出Propose请求,Acceptors针对收到的Propose请求进行Accept处理。

第三阶段:Learn阶段。Proposer在收到多数Acceptors的Accept之后,标志着本次 Accept成功,决议形成,将形成的决议发送给所有Learners

Proposer生成全局唯一且递增的Proposal ID (可使用时间戳加Server ID),向所有 Acceptors发送Prepare请求,这里无需携带提案内容,只携带Proposal ID即可。

Acceptors收到Prepare和Propose请求后

  1. 不再接受Proposal ID小于等于当前请求的Prepare请求。

  2. 不再接受Proposal ID小于当前请求的Propose请求。



Zab协议

zab协议 的全称是 Zookeeper Atomic Broadcast (Zookeeper原子广播)。Zookeeper 是通过 Zab 协议来保证分布式事务的最终一致性



Zab协议是为分布式协调服务Zookeeper专门设计的一种 支持崩溃恢复原子广播协议 ,是Zookeeper保证数据一致性的核心算法。Zab借鉴了Paxos算法,但又不像Paxos那样,是一种通用的分布式一致性算法。它是特别为Zookeeper设计的支持崩溃恢复的原子广播协议



在Zookeeper中主要依赖Zab协议来实现数据一致性,基于该协议,zk实现了一种主备模型(即Leader和Follower模型)的系统架构来保证集群中各个副本之间数据的一致性。 这里的主备系统架构模型,就是指只有一台客户端(Leader)负责处理外部的写事务请求,然后Leader客户端将数据同步到其他Follower节点。



Zookeeper 客户端会随机的链接到 zookeeper 集群中的一个节点,如果是读请求,就直接从当前节点中读取数据;如果是写请求,那么节点就会向 Leader 提交事务,Leader 接收到事务提交,会广播该事务,只要超过半数节点写入成功,该事务就会被提交。



ZooKeeper架构

  1. 客户端发起一个写操作请求。

  2. Leader 服务器将客户端的请求转化为事务 Proposal 提案,同时为每个 Proposal 分配一个全局的ID,即zxid。

  3. Leader 服务器为每个 Follower 服务器分配一个单独的队列,然后将需要广播的 Proposal 依次放到队列中取,并且根据 FIFO 策略进行消息发送。

  4. Follower 接收到 Proposal 后,会首先将其以事务日志的方式写入本地磁盘中,写入成功后向 Leader 反馈一个 Ack 响应消息。

  5. Leader 接收到超过半数以上 Follower 的 Ack 响应消息后,即认为消息发送成功,可以发送 commit 消息。

  6. Leader 向所有 Follower 广播 commit 消息,同时自身也会完成事务提交。Follower 接收到 commit 消息后,会将上一条事务提交。



Zookeeper 采用 Zab 协议的核心,就是只要有一台服务器提交了 Proposal,就要确保所有的服务器最终都能正确提交 Proposal。这也是 CAP/BASE 实现最终一致性的一个体现。



Leader 服务器与每一个 Follower 服务器之间都维护了一个单独的 FIFO 消息队列进行收发消息,使用队列消息可以做到异步解耦。 Leader 和 Follower 之间只需要往队列中发消息即可。如果使用同步的方式会引起阻塞,性能要下降很多。



ZooKeeper的树状记录结构

ZooKeeperAPI

String create(path, data, acl, flags)

void delete(path, expectedVersion)

Stat setData(path, data, expectedVersion)

(data, Stat) getData(path, watch)

Stat exists(path, watch)

String[] getChildren(path, watch)

void sync(path)

List multi(ops)



ZK配置管理

  • Administrator

  • Consumer



ZK选master

  1. getdata(“/servers/leader”, true)

  2. if successful follow the leader described in the data and exit

  3. create(“/servers/leader”, hostname, EPHEMERAL)

  4. if successful lead and exit

  5. gotostep 1



ZK集群管理(负载均衡与失效转移)

Monitoring process:

  1. Watch on /nodes

  2. On watch trigger do getChildren(/nodes, true)

  3. Track which nodes have gone away



Each Node:

  1. Create /nodes/node-${i} as ephemeral nodes

  2. Keep updating /nodes/node-${i} periodically for node status changes (status updates could be load/iostat/cpu/others



ZooKeeper性能

  • 读请求比例多大,性能越好

  • 节点越多,写操作性能越差,读性能越好




搜索引擎

互联网搜索引擎整体架构

爬虫系统架构

爬虫禁爬协议

文档矩阵与倒排索引

文档与倒排索引

带词频的倒排索引

带词频与位置的倒排索引

Lucene架构

Lucene专业术语

  • Term:索引中最小的存储与查询单元

  • 词典 Term的集合

  • 倒排表(Posting List):记录某个词在哪些文档中出现过

  • 正向信息: 原始的文档信息

  • 段(segement):索引中最小的独立存储单元,一个索引有一个或者多个段组成。



Lucene底层由词单与倒排序组成,其中词典就是Term集合,词单中Term只想的文档链表的集合叫做倒排表.词典与倒排表是Lucene中最重要的两种数据结构。两者是分两部分存储的。倒排表不但存储了文档编号,还存储了词频等信息。



Lucene索引文件准实时更新

索引有更新,就需要重新全量创建一个索引来替换原来的索引。这种方式在数据量很大 时效率很低,并且由于创建一次索引的成本很高,性能也很差。



Lucene中引入了段的概念,将一个索引文件拆分为多个子文件,每个子文件叫做段,每 个段都是一个独立的可被搜索的数据集,索引的修改针对段进行操作



  • 新增:当有新的数据需要创建索引时,原来的段不变,选择新建一个段来存储新增的数据。

  • 删除:当需要删除数据时,在索引文件新增一个.del的文件,用来专门存储被删除的数据 ID。当查询时,被删除的数据还是可以被查到的,只是在进行文档链表合并时,才把已经删 除的数据过滤掉。被删除的数据在进行段合并时才会被真正被移除。

  • 更新:更新的操作其实就是删除和新增的组合,先在.del文件中记录旧数据,再在新段中添 加一条更新后的数据。



为了控制索引里段的数量,我们必须定期进行段合并操作



ElasticSearch

Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,一个建立在全文搜索引擎 Apache Lucene(TM) 基础上的搜索引擎.当然 Elasticsearch 并不仅仅是 Lucene 那么简单,它不仅包括了全文搜索功能,还可以进行以下工作:



  • 分布式实时文件存储,并将每一个字段都编入索引,使其可以被搜索。

  • 实时分析的分布式搜索引擎

  • 可扩展到上百台服务器,处理PB级别的结构化或非结构化数据,具备高可用与高并发特性



Elasticsearch 是用Java语言开发的,并作为Apache许可条款下的开放源码发布



ElasticSearch架构

  • 索引分片,实现分布式

  • 索引备份,实现高可用

  • API更简单、更高级

ES分片预分配与集群扩容

ElasticSearch事务日志

Lucene:为了加快写索引的速度,采用了延迟写入的策略;这样容易在内存中还没有持久化磁盘上发生类似断电等不可控情况,丢失数据。

ElasticSearch: ES添加了事务日志(Translog),其记录立刻所有还没有被持久化到磁盘的数据。




产品提案总结(根据Doris案例总结)

  1. 当前现状

业务需求与问题

  1. 产品需求

根据业务需求进行产品定位

列出需要解决的问题

  1. 产品目标

功能目标

非功能目标

其他约束

  1. 产品指标衡量

  2. 产品的整体逻辑架构

  3. 产品的概念模型(部署架构)

  4. 产品的关键技术点

  5. 产品的未来规划

  6. 产品的研发计划



用户头像

Kaven

关注

还未添加个人签名 2019.04.13 加入

还未添加个人简介

评论

发布
暂无评论
极客时间架构师训练营1期-第6周总结