分布式架构101

[TOC]

分布式架构

arch

缓存架构:如何减少不必要的计算?

缓存分为通读缓存旁路缓存

通读缓存

通读(read-through)缓存,应用程序访问通读缓存获取数据的时候,如果通读缓存有应用程序需要的数据,那么就返回这个数据;如果没有,那么通读缓存就自己负责访问数据源,从数据源获取数据返回给应用程序,并将这个数据缓存在自己的缓存中。这样,下次应用程序需要数据的时候,就可以通过通读缓存直接获得数据了。

read-through

互联网应用中主要使用的通读缓存是 CDN 和反向代理缓存。

cdn

reversed proxy

旁路缓存

旁路(cache-aside)缓存,应用程序访问旁路缓存获取数据的时候,如果旁路缓存中有应用程序需要的数据,那么就返回这个数据;如果没有,就返回空(null)。应用程序需要自己从数据源读取数据,然后将这个数据写入到旁路缓存中。这样,下次应用程序需要数据的时候,就可以通过旁路缓存直接获得数据了。

cache-aside

CDN 和反向代理缓存通常会作为系统架构的一部分,很多时候对应用程序是透明的。而应用程序在代码中主要使用的是对象缓存,对象缓存是一种旁路缓存

缓存脏读

数据脏读的问题,缓存的数据来自数据源,如果数据源中的数据被修改了,那么缓存中的数据就变成脏数据了。

解决方法有两个:

  1. 过期失效:每次写入缓存中的数据都标记其失效时间,在读取缓存的时候,检查数据是否已经过期失效,如果失效,就重新从数据源获取数据。缓存失效依然可能会在未失效时间内读到脏数据,但是一般的应用都可以容忍较短时间的数据不一致,比如淘宝卖家更新了商品信息,那么几分钟数据没有更新到缓存,买家看到的还是旧数据,这种情况通常是可以接受的,这时候,就可以设置缓存失效时间为几分钟。
  2. 失效通知,应用程序更新数据源的数据,同时发送通知,将该数据从缓存中清除。失效通知看起来数据更新更加及时,但是实践中,更多使用的还是过期失效。

缓存集群扩容

集群扩容使用一致性hash算法。

异步架构:如何避免互相依赖的系统间耦合?

缓存只能改善系统的读操作性能,对于写操作,缓存是无能为力的。主要手段就是使用消息队列的异步架构,有时候也被称为事件驱动架构。

同步架构:当应用程序调用服务的时候,当前程序需要阻塞等待服务完成,返回服务结果后才能继续向下执行。

典型的消息队列异步架构如下:

msg-queue

根据消息消费方式又分为点对点模式发布订阅模式两种。

点对点模式中,多个消息生产者向消息队列发送消息,多个消息消费者消费消息,每个消息只会被一个消息消费者消费。

发布订阅模式中,开发者可以在消息队列中设置主题,消息生产者的消息按照主题进行发送,多个消息消费者可以订阅同一个主题,每个消费者都可以收到这个主题的消息拷贝,然后按照自己的业务逻辑分别进行处理计算。发布订阅模式下,一个主题可以被重复订阅,所以如果需要扩展功能,可以在对当前的生产者和消费者都没有影响的前提下,增加新的消费者订阅同一个主题即可。

product-consumer

消息队列异步架构的好处

  1. 改善写操作请求的响应时间
  2. 更容易进行伸缩:应用程序也可以通过负载均衡实现集群伸缩,但是这种集群伸缩是以整个应用服务器为单位的。如果只是其中某些功能有负载压力,那么就可以单独针对消费者集群进行伸缩。
  3. 削峰填谷
  4. 隔离失败
  5. 降低耦合

负载均衡架构:如何用10行代码实现一个负载均衡服务?

负载均衡硬件:F5

HTTP 重定向负载均衡

302跳转。

http lb

HTTP 重定向负载均衡的优点是设计比较简单,但是它的缺点也比较明显,一方面用户完成一次访问,就需要请求两次数据中心,一次请求负载均衡服务器,一次是请求应用服务器,请求处理性能会受很大的影响。另一个问题是因为响应要重定向到真正的应用服务器,所以需要把应用服务器的 IP 地址暴露给外部用户,这样可能会带来安全性的问题。

DNS 负载均衡

dns lb

和 HTTP 重定向不同,用户不需要每次请求都进行 DNS 域名解析,第一次解析后,域名缓存在本机,后面较长一段时间都不会再进行域名解析了,因此性能方面不会是问题。

如果如图中所示,域名解析直接得到应用服务器的 IP 地址,确实会存在安全性问题。但是大型互联网应用通常并不直接通过 DNS 解析得到应用服务器 IP 地址,而是解析得到负载均衡服务器的 IP 地址。

反向代理负载均衡

反向代理服务器查找本机是否有请求的资源,如果有就直接返回资源数据,如果没有,就将请求发送给后面的应用服务器继续处理。

反向代理服务器是工作在 HTTP 协议层之上的,所以它代理的也是 HTTP 的请求和响应。作为互联网应用层的一个协议,HTTP 协议相对说来比较重,效率比较低,所以反向代理负载均衡通常用在小规模的互联网系统上,只有几台或者十几台服务器的规模。

reversed proxy

IP 负载均衡

反向代理负载均衡是工作在应用层网络协议上的负载均衡,因此也叫应用层负载均衡。应用层负载均衡之下的负载均衡方法是在 TCP/IP 协议的 IP 层进行负载均衡,IP 层是网络通讯协议的网络层,所以有时候叫网络层负载均衡。它的主要工作原理是当用户的请求到达负载均衡服务器以后,负载均衡服务器会对网络层的数据包的 IP 地址进行转换,修改 IP 地址,将其修改为应用服务器的 IP 地址,然后把数据包重新发送出去,请求数据就会到达应用服务器。

ip lb

IP 负载均衡不需要在 HTTP 协议层工作,可以在操作系统内核直接修改 IP 数据包的地址,因此,效率比应用层的反向代理负载均衡高得多。但是它依然有一个缺陷,不管是请求还是响应的数据包,都要通过负载均衡服务器进行 IP 地址转换,才能够正确地把请求数据分发到应用服务器,或者正确地将响应数据包发送到用户端程序。请求的数据通常比较小,一个 URL 或者是一个简单的表单,但是响应的数据不管是 HTML 还是图片,或者是 JS、CSS 这样的资源文件通常都会比较大,因此负载均衡服务器会成为响应数据的流量瓶颈。

数据链路层负载均衡

数据链路层负载均衡可以解决响应数据量大而导致的负载均衡服务器输出带宽不足的问题。也就是说,负载均衡服务器并不修改数据包的 IP 地址,而是修改数据链路层里的网卡 mac 地址,在数据链路层实现负载均衡。而应用服务器和负载均衡服务器都使用相同的虚拟 IP 地址,这样 IP 路由就不会受到影响,但是网卡会根据自己的 mac 地址,选择负载均衡服务器发送到自己网卡的数据包,交给对应的应用程序去处理,处理结束以后,当把响应的数据包发送到网络上的时候,因为 IP 地址没有修改过,所以这个响应会直接到达用户的浏览器,而不会再经过负载均衡服务器。

dl lb

链路层负载均衡避免响应数据再经过负载均衡服务器,因而可以承受较大的数据传输压力,所以,目前大型互联网应用基本都使用链路层负载均衡。

Linux 上实现 IP 负载均衡和链路层负载均衡的技术是 LVS,目前 LVS 的功能已经集成到 Linux 中了,通过 Linux 可以直接配置实现这两种负载均衡。

负载均衡算法

目前主要的负载均衡算法有轮询、随机、最少连接几种。

  1. 轮询就是将请求轮流发给应用服务器
  2. 随机就是将请求随机发送给任一台应用服务器
  3. 最少连接则是根据应用服务器当前正在处理的连接数,将请求分发给最少连接的服务器。

数据存储架构:如何改善系统的数据存储能力?

改善数据库存储的主要手段有:数据库主从复制、数据库分片与NoSQL数据库。

数据库主从复制与主主复制

主从复制的原理:

主要的复制原理是,当应用程序客户端发送一条更新命令到主服务器数据库的时候,数据库会把这条更新命令同步记录到 Binlog 中,然后由另外一个线程从 Binlog 中读取这条日志,通过远程通讯的方式将它复制到从服务器上面去。

从服务器获得这条更新日志后,将其加入到自己的 Relay Log 中,然后由另外一个 SQL 执行线程从 Relay log 中读取这条新的日志,并把它在本地的数据库中重新执行一遍,这样当客户端应用程序执行一个 update 命令的时候,这个命令会同时在主数据库和从数据库上执行,从而实现了主数据库向从数据库的复制,让从数据库和主数据库保持一样的数据。

database master slave

通过数据库主从复制的方式,我们可以实现数据库读写分离。现实中多用一主多从的数据复制方案。从数据库分担读的压力,承担不同的角色,比如有的从数据库用来做实时数据分析,有的从数据库用来做批任务报表计算,有的单纯做数据备份。

主主复制:两台服务器互相备份,任何一台服务器都会将自己的 Binlog 复制到另一台机器的 Relay Log 中,以保持两台服务器的数据一致。

database master-master

使用主主复制需要注意的是,主主复制仅仅用来提升数据写操作的可用性,并不能用来提高写操作的性能。任何时候,系统中都只能有一个数据库作为主数据库,也就是说,所有的应用程序都必须连接到同一个主数据库进行写操作。只有当该数据库宕机失效的时候,才会将写操作切换到另一台主数据库上。这样才能够保证数据库数据的一致性,不会出现数据冲突。

不管是主从复制还是主主复制,都无法提升数据库的存储能力,也就是说,不管增加多少服务器,这些服务器存储的数据都是一样的。如果数据量太大,数据库无法存下这么多的数据,通过数据库复制是无法解决问题的。

数据库分片

将一张表的数据分成若干片,每一片都包含了数据表中一部分的行记录,然后每一片存储在不同的服务器上,这样一张表就存储在多台服务器上了。

最简单的数据库分片存储可以采用硬编码的方式,但是硬编码方式的缺点比较明显。首先,如果要增加服务器,那么就必须修改分片逻辑代码,这样程序代码就会因为非业务需求产生不必要的变更;其次,分片逻辑耦合在处理业务逻辑的程序代码中,修改分片逻辑或者修改业务逻辑都可能使另一部分代码因为不小心的改动而出现 Bug。

database slice with hard code

可以通过使用分布式关系数据库中间件解决这个问题,将数据的分片逻辑在中间件中完成,对应用程序透明,如MyCAT。

database slice with MyCAT

实践中,更常见的数据库分片算法是我们所熟悉的余数 Hash 算法,根据主键 ID 和服务器的数目进行取模计算,根据余数连接相对应的服务器。

关系数据库的混合部署

随着业务复杂以及数据存储和访问压力的增加,这时候可以选择业务分库。将不同业务相关的数据库表,部署在不同的服务器上,比如类目数据和用户数据相对关联关系不大,服务的应用也不一样,那么就可以将这两类数据库部署在不同的服务器上。而每一类数据库还可以继续选择使用主从复制,或者主主复制。

database mix

不同的业务数据库,其数据库存储的数据和访问压力也是不同的,比如用户数据库的数据量和访问量就可能是类目数据库的几十倍,甚至上百倍。那么这时候就可以针对用户数据库进行数据分片,而每个分片数据库还可以继续进行主从复制或者主主复制。

database mix

NoSQL数据库

NoSQL 数据库面临的挑战之一是数据一致性问题。

关于分布式存储系统有一个著名的 CAP 原理,CAP 原理说:一个提供数据服务的分布式系统无法同时满足数据一致性(Consistency)、可用性(Availability)和分区耐受性(Partition Tolerance)这三个条件。

一致性是说,每次读取的数据都应该是最近写入的数据或者返回一个错误,而不是过期数据,也就是说,数据是一致的。

可用性是说,每次请求都应该得到一个响应,而不是返回一个错误或者失去响应,不过这个响应不需要保证数据是最近写入的。也就是说,系统需要一直都是可以正常使用的,不会引起调用者的异常,但是并不保证响应的数据是最新的。

分区耐受性说,即使因为网络原因,网络分区失效的时候,部分服务器节点之间消息丢失或者延迟了,系统依然应该是可以操作的。

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

对于一个分布式系统而言,网络失效一定会发生,也就是说,分区耐受性是必须要保证的,而对于互联网应用来说,可用性也是需要保证的,分布式存储系统通常需要在一致性上做一些妥协和增强。

Apache Cassandra 解决数据一致性的方案是,在用户写入数据的时候,将一个数据写入集群中的三个服务器节点,等待至少两个节点响应写入成功。用户读取数据的时候,从三个节点尝试读取数据,至少等到两个节点返回数据,并根据返回数据的时间戳,选取最新版本的数据。这样,即使服务器中的数据不一致,但是最终用户还是能得到一个一致的数据,这种方案也被称为最终一致性

nosql

NoSQL为什么比RMDB性能高?

主要区别可用RDBMS的ACID和NoSQL的BASE概括。

数据库集群扩容问题

分布式架构的一个最大特点是可以动态伸缩,可以随着需求变化,动态增加或者减少服务器。对于支持分片的分布式关系数据库而言,比如我们使用 MYCAT 进行数据分片,那么随着数据量逐渐增大,如何增加服务器以存储更多的数据呢?如果增加一台服务器,如何调整数据库分片,使部分数据迁移到新的服务器上?如何保证整个迁移过程快速、安全?

1、创建远超实际节点数的分区,每个节点分配多个分区。当集群增加新节点时,该新节点从现有的节点中匀走几个分区,直到达到全局平衡。请求到来时 、由于分区总数不变,所以不会改变关键词到分区的映射关系,唯一调整的是分区与节点的对应关系。这种策略目前在ElasticSearch等系统使用
2、动态分区策略。当分区数据增长超过阈值(如Hbase 为10G),那么拆分为两个分区,每个承担一半的数据量,数据转移到其他节点。如Hbase借助了 HDFS 实现分区文件的传输
3、分区数与节点数成正比。当加入新的节点时,分区数增加。如Cassandra 采用了这样的方式。

搜索引擎架构:如何瞬间完成海量数据检索?

倒排索引

首先选择一些种子 URL,然后通过爬虫将这些 URL 对应的页面爬下来。其实,所谓的爬虫,就是发送 URL 请求,下载相应的 HTML 页面,然后将这些 Web 页面存储在自己的服务器上,并解析这些页面的 HTML 内容,当解析到网页里超链接 URL 的时候,再检查这个超链接是否已经在前面爬取过了,如果没有,就把这个超链接放到一个队列中,后面会请求这个 URL,得到对应的 HTML 页面并解析其包含的超链接……如此不断重复,就可以将全世界的 Web 页面存储到自己的服务器中。

爬虫系统架构如下:

scrawler

得到了全部网页以后,需要对每个网页进行编号,得到全部网页的文档集合。然后再解析每个页面,提取文档里的每个单词,如果是英文,那么每个单词都用空格分隔,比较容易;如果是中文,需要使用中文分词器才能提取到每个单词,比如“后端技术”,使用中文分词器得到的就是“后端”、“技术”两个词。

然后考察每个词在哪些文档中出现,比如“后端”在文档 2、4、5、7 中出现,“技术”在文档 1、2、4 中出现,这样我们就可以得到一个单词、文档矩阵:

index

把这个单词、文档矩阵按照单词→文档列表的方式组织起来,就是倒排索引了:

index

搜索单词的时候,我们可以将所有单词构成一个 Hash 表,根据搜索词直接查找 Hash 表,就可以得到单词了。

搜索引擎结果排序

PageRank 算法认为,如果一个网页里包含了某个网页的超链接,那么就表示该网页认可某个网页,或者说,该网页给某个网页投了一票。如下 A、B、C、D 四个网页,箭头指向的方向就是表示超链接的方向,B 的箭头指向 A,表示 B 网页包含 A 网页的超链接,也就是 B 网页给 A 网页投了一票。

link

开始的时候,所有网页都初始化权重值为 1,然后根据超链接关系计算新的权重。比如 B 页面包含了 A 和 D 两个页面的超链接,那么自己的权重 1 就被分成两个 1/2 分别投给 A 和 D。而 A 页面的超链接包含在 B、C、D 三个页面中,那么 A 页面新的权重值就是这个三个页面投给它的权重值之和:1/2 + 1/3 + 1 = 11/6。经过一轮 PageRank 计算后,每个页面都有了新的权重,然后基于这个新的权重再继续一轮计算,直到所有的网页权重稳定下来,就得到最终所有网页的权重,即最终的 PageRank 值。

PageRank 算法对于互联网网页排序效果很好,但是,对于那些用户生成内容(UGC)的网站而言,比如豆瓣、知乎,或者我们的InfoQ,如果想在这些网站内部进行搜索,PageRank 算法就没什么效果了。因为豆瓣的影评,知乎的回答,InfoQ 的技术文章之间很少通过超链接进行推荐。这种可以用点赞数排序。

还有一种是分词,计算词频进行排序。

微服务架构:微服务究竟是灵丹还是毒药?

单体架构的困难与挑战

  • 编译、部署困难
  • 代码分支管理困难
  • 数据库连接耗尽
  • 新增业务困难
  • 发布困难

微服务框架原理

SOA架构方案,即面向服务的体系架构:

link

Dubbo:

link

微服务架构的落地实践

事实微服务的关注点应遵循以下一个倒三角模型:

link

高性能架构:除了代码,还可以在哪些地方优化

进行性能优化的前提:先知道系统当前的性能情况

性能指标

响应时间:从发出请求开始到最后响应数据所需要的时间。

并发数:系统同时处理的请求数,体现的是系统的负载压力情况。

吞吐量:单位时间内系统处理请求的数量,体现的是系统的处理能力。

吞吐量、响应时间和并发数三者之间是有关联性的。并发数不变,响应时间越快,单位时间的吞吐量越高。

性能计数器:指的是服务器或者操作系统性能的一些指标数据,包括系统负载 System Load、对象和线程数、内存使用、CPU 使用、磁盘和网络 I/O 使用等指标,这些指标是系统监控的重要参数,反映系统负载和处理能力的一些关键指标,通常这些指标和性能是强相关的。这些指标很高,成为瓶颈,通常也预示着性能可能会出现问题。

性能测试

性能测试是指以系统设计初期规划的性能指标为预期目标,对系统不断地施加压力,验证系统在资源可接受的范围内是否达到了性能的预期目标。这个过程中,随着并发数的增加,吞入量也在增加,但是响应时间变化不大。系统正常情况下的并发访问压力应该都在这个范围内。

负载测试则是对系统不断地施加并发请求,增加系统的压力,直到系统的某项或多项指标达到安全临界值。这个过程中,随着并发数的增加,吞吐量只有小幅的增加,达到最大值后,吞吐量还会下降,而响应时间则会不断增加。

压力测试是指在超过安全负载的情况下,增加并发请求数,对系统继续施加压力,直到系统崩溃,或者不再处理任何请求,此时的并发数就是系统的最大压力承受能力。这个过程中,吞吐量迅速下降,响应时间迅速增加,到了系统崩溃点,吞吐量为 0,响应时间无穷大。

link

性能优化

用户体验优化:这是一种主观性优化,比如显示进度条。

客观优化有下面几个:

  1. 数据中心优化:就近部署,在多个区域部署自己的数据中心。
  2. 硬件优化:如优化网卡从1G到10G
  3. 操作系统优化:比如在某些Linux中,transparent huge page 这个参数是默认打开的,导致系统占用 CPU 过高。关闭这个参数后,系统 CPU 占用下降,整个计算时间也大幅缩短了
  4. 虚拟机优化:JVM优化
  5. 基础组件优化,如Web容器、数据库连接池、MVC框架等
  6. 架构优化
    • 缓存
    • 消息队列
    • 集群
  7. 代码优化

高可用架构

高可用的度量:

1
可用性=(1-年度不可用时间/年度总时间)*100%

一般说来,两个 9 表示系统基本可用,年度不可用时间小于 88 小时;3 个 9 是较高可用,年度不可用时间小于 9 个小时;4 个 9 是具有自动恢复能力的高可用,年度不可用时间小于 53 分钟;5 个 9 指极高的可用性,年度不可用时间小于 5 分钟。我们熟悉的互联网产品的可用性大多是 4 个 9。淘宝、百度、微信,差不多都是这样。

link

冗余备份

负载均衡还可以实现系统的高可用。

link

数据库主主复制,也是一种冗余备份。

失败隔离

失败隔离的主要架构技术是消息队列。

link

消息队列还使得程序解耦,将程序的调用和依赖隔离开来。

限流降级

限流是指对进入系统的用户请求进行流量限制,如果访问量超过了系统的最大处理能力,就会丢弃一部分的用户请求,保证整个系统可用,保证大部分用户是可以访问系统的。

降级是保护系统的另一种手段。有一些系统功能是非核心的,但是它也给系统产生了非常大的压力,比如说在电商系统中有确认收货这个功能,即便我们不去确认收货,系统也会超时自动确认收货。

异地多活

将数据中心分布在多个不同地点的机房里,这些机房都可以对外提供服务,用户可以连接任何一个机房进行访问,这样每个机房都可以提供完整的系统服务,即使某一个机房不可使用,系统也不会宕机,依然保持可用。

异地多活的架构考虑的重点就是,用户请求如何分发到不同的机房去。这个主要可以在域名解析的时候完成,也就是用户进行域名解析的时候,会根据就近原则或者其他一些策略,完成用户请求的分发。另一个至关重要的技术点是,因为是多个机房都可以独立对外提供服务,所以也就意味着每个机房都要有完整的数据记录。用户在任何一个机房完成的数据操作,都必须同步传输给其他的机房,进行数据实时同步。

数据库实时同步最需要关注的就是数据冲突问题。同一条数据,同时在两个数据中心被修改了,该如何解决?为了解决这种数据冲突的问题,某些容易引起数据冲突的服务采用类似 MySQL 的主主模式,也就是说多个机房在某个时刻是有一个主机房的,某些请求只能到达主机房才能被处理,其他的机房不处理这一类请求,以此来避免关键数据的冲突。

安全性架构:为什么说用户密码泄漏是程序员的锅

  • 数据加解密:单向散列加密
  • 对称加密
  • 非对称加密
  • xss
  • SQL注入

大数据架构

分布式文件存储HDFS架构

HDFS 可以将数千台服务器组成一个统一的文件存储系统,其中 NameNode 服务器充当文件控制块的角色,进行文件元数据管理,即记录文件名、访问权限、数据存储地址等信息,而真正的文件数据则存储在 DataNode 服务器上。

DataNode 以块为单位存储数据,所有的块信息,比如块 ID、块所在的服务器 IP 地址等,都记录在 NameNode,而具体的块数据则存储在 DataNode 上。

HDFS 为了保证不会因为硬盘或者服务器损坏而导致文件损坏,还会对数据块进行复制,每个数据块都会存储在多台服务器上,甚至多个机架上。

大数据计算MapReduce

大数据处理的经典计算框架是 MapReduce。MapReduce 的核心思想是对数据进行分片计算。

MapReduce 将计算过程分成两个部分,一个是 map 过程,每个服务器上会启动多个 map 进程,map 优先读取本地数据进行计算,计算后输出一个
<key, value> 集合。另一个是 reduce 过程,MapReduce 在每个服务器上都启动多个 reduce 进程,然后对所有 map 输出的 <key, value> 集合进行 shuffle 操作。所谓 shuffle 就是将相同的 key 发送到同一个 reduce 进程,在 reduce 中完成数据关联计算。

link

word count: 假设原始数据有两个数据块,MapReduce 框架启动两个 map 进程进行处理,分别读入数据。map 函数对输入数据进行分词处理,然后针对每个单词输出 < 单词, 1> 这样的 <key, value> 结果。然后,MapReduce 框架进行 shuffle 操作,相同的 key 发送给同一个 reduce 进程,reduce 的输入就是 <key, value 列表 > 这样的结构,即相同 key 的 value 合并成一个 value 列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class WordCount {

public static class TokenizerMapper
extends Mapper<Object, Text, Text, IntWritable>{

private final static IntWritable one = new IntWritable(1);
private Text word = new Text();

public void map(Object key, Text value, Context context
) throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
}

public static class IntSumReducer
extends Reducer<Text,IntWritable,Text,IntWritable> {
private IntWritable result = new IntWritable();

public void reduce(Text key, Iterable<IntWritable> values,
Context context
) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
context.write(key, result);
}

这些进程是如何在分布式的服务器集群上启动的呢?数据是如何流动,最终完成计算的呢?

link

MapReduce1 主要有 JobTracker 和 TaskTracker 两种进程角色,JobTracker 在 MapReduce 集群中只有一个,而 TaskTracker 则和 DataNode 一起,启动在集群的所有服务器上。

MapReduce 应用程序 JobClient 启动后,会向 JobTracker 提交作业,JobTracker 根据作业中输入文件路径分析,需要在哪些服务器上启动 map 进程,然后就向这些服务器上的 TaskTracker 发送任务命令。

TaskTracker 收到任务后,启动一个 TaskRunner 进程下载任务对应的程序,然后反射加载程序中的 map 函数,读取任务中分配的数据块,进行 map 计算。map 计算结束后,TaskTracker 会对 map 输出进行 shuffle 操作,然后 TaskRunner 加载 reduce 函数进行后续计算。HDFS 和 MapReduce 都是 Hadoop 的组成部分。

HDFS 和 MapReduce 都是 Hadoop 的组成部分。

大数据仓库 Hive 架构

MapReduce写起来较为复杂,Hive是一个根据SQL生成MapReduce的工具:

1
SELECT pageid, age, count(1) FROM pv_users GROUP BY pageid, age;

Hive 要做的就是将 SQL 翻译成 MapReduce 程序代码,实际上,Hive 内置了很多 Operator,每个 Operator 完成一个特定的计算过程,Hive 将这些 Operator 构造成一个有向无环图 DAG,然后根据这些 Operator 之间是否存在 shuffle 将其封装到 map 或者 reduce 函数,就可以提交给 MapReduce 执行了。Operator 组成的 DAG 图示例如下,这是一个包含 where 查询条件的 SQL,where 查询条件对应一个 FilterOperator。
link

Hive 整体架构如下,Hive 的表数据存储在 HDFS。表的结构,比如表名、字段名、字段之间的分隔符等存储在 Metastore。用户通过 Client 提交 SQL 到 Driver,Driver 请求 Compiler 将 SQL 编译成如上示例的 DAG 执行计划,然后交给 Hadoop 执行。

link

快速大数据计算Spark架构

MapReduce 主要使用硬盘存储计算过程中的数据,这样虽然可靠性比较高,但是性能其实比较差。

Spark 在 MapReduce 基础上进行改进,主要使用内存进行中间计算数据存储,加快了计算执行时间,在某些情况下,性能可以提升上百倍。

Spark 的主要编程模型是 RDD,弹性数据集。在 RDD 上定义了许多常见的大数据计算函数,利用这些函数,可以用极少的代码完成较为复杂的大数据计算。前面举例的 WorkCount,如果用 Spark 编程,只需要三行代码:

1
2
3
4
5
val textFile = sc.textFile("hdfs://...")
val counts = textFile.flatMap(line => line.split(" "))
.map(word => (word, 1))
.reduceByKey(_ + _)
counts.saveAsTextFile("hdfs://...")

首先,从 HDFS 读取数据,构建出一个 RDD textFile。然后,在这个 RDD 上执行三个操作:将输入数据的每一行文本用空格拆分成单词;将每个单词进行转换,word→(word, 1),生成 <Key, Value> 的结构;相同的 Key 进行统计,统计方式是对 Value 求和。最后,将 RDD counts 写入到 HDFS,完成结果输出。

Spark DAG 示例如下:

link

如上所示,A、C 和 E 是从 HDFS 上加载的 RDD,A 经过 groupBy 分组统计转换函数后得到 RDD B,C 经过 map 转换函数后得到 RDD D,D 和 E 经过 union 合并转换函数后得到 RDD F,B 和 F 经过 join 连接转换函数后得到最终结果 RDD G。

大数据流计算架构

Spark 虽然比 MapReduce 快很多,但是大多数场景下,计算耗时依然是分钟级别的,这种计算一般被称为大数据批处理计算。

Spark Streaming 的架构原理是将实时流入的数据切分成小的一批一批的数据,然后将这些小的一批数据交给 Spark 执行。由于数据量比较小,Spark Streaming 又常驻系统,不需要重新启动,因此可以毫秒级完成计算,看起来像是实时计算一样。

link

最近几年比较流行的大数据引擎 Flink 其架构原理其实和 Spark Streaming 很相似,随着数据源的不同,根据数据量和计算场景的要求,可以灵活适应流计算和批处理计算。

小结

大数据技术可以说是分布式技术的一个分支,都是面临大量的计算压力,采用分布式服务器集群的方案解决问题。差别是大数据技术要处理的数据具有关联性,所以需要有个中心服务器进行管理,NameNode、JobTracker 都是这样的中心服务器。

AI与物联网架构:从智能引擎到物联网平台

大数据平台架构

link

数据同步系统实际上承担的是传统数据仓库 ETL 的职责,即数据的抽取(Extract)、转换(Transform)、载入(Load).

智能推荐算法

基于人口统计的推荐:

link

基于商品属性的推荐:

link

基于用户的协同过滤推荐:

用户 A 和用户 C 都喜欢商品 A 和商品 B,根据他们的喜好可以分为同类。然后用户 A 还喜欢商品 D,那么将商品 D 推荐给用户 C,他可能也会喜欢。

link

基于商品的协同过滤推荐

根据用户的喜好对商品进行分类,然后根据商品分类进行推荐。

link

物联网大数据

link

区块链技术架构:区块链到底能做什么?

link

联盟链

Hyperledger Fabric

link