大数据组件各种流程总结

1.hbase

1.1 hbase的读数据流程:

 

读数据:

(0.98版本以前,0.98及以后没有-ROOT-表)

1、客户端通过 zookeeper 以及-root-表和.meta.表找到目标数据所在的 regionserver(就是数据所在的 region 的主机地址)
2、联系 regionserver 查询目标数据
3、 regionserver 定位到目标数据所在的 region,发出查询请求
4、 region 先在 memstore 中查找,命中则返回
5 、 如果在 memstore 中找不到,则在storefile 中扫描 (可能会扫描到很多的storefile----BloomFilter)

1.2 hbase的写数据流程:

 

写数据:

1、 client 先根据 rowkey 找到对应的 region 所在的 regionserver
2、 client 向 regionserver 提交写请求
3、 regionserver 找到目标 region
4、 region 检查数据是否与 schema 一致
5、如果客户端没有指定版本,则获取当前系统时间作为数据版本
6、将更新写入 WAL log
7、将更新写入 Memstore

8、判断 Memstore 的是否需要 flush 为 Store 文件。

1.3 hbase底层原理

 

regionServer  其实是hbase的服务,部署在一台物理服务器上。region有一点像关系型数据的分区,数据存放在region中,当然region下面还有很多结构,确切来说数据存放在memstore和hfile中。我们访问hbase的时候,先去hbase 系统表查找定位这条记录属于哪个region,然后定位到这个region属于哪个服务器,然后就到哪个服务器里面查找对应region中的数据

Region是Hbase种分布式存储和负载均衡的最小单元

物理存储的最小单元是HFile

逻辑最小单元是ceil

Store是一个列簇,包含了一个列簇的所有数据,包括位于内存的一个memstore和位于硬盘的多个storefile组成

hbase table中每个列簇都对应着region中的一个store,在hdfs系统中则对应着一个目录,如果列簇中尚无数据,也就是该store下还没有storefile。

一个表有多个Region,但是一个Region是一张表的部分数据

每个HStore对应了Table中的一个column family的存储,可以看出每个columnfamily其实就是一个集中的存储单元,因此最好将具备共同IO特性的column放在一个column family中,这样最高效。

写操作先写入memstore,当memstore中的数据量达到某个阈值时,HregionServer启动flushcache进程写入storefile,每次写入形成单独一个Hfile,当总的storefile大小超过一定阈值后,会把当前的region分割成两个,并由HMaster分配给相应的region服务器,实现负载均衡,客户端检索数据时,现在memtore找,找不到再找storefile

多个rowkey保存在一个region里面

Table 在行的方向上分割为多个 HRegion。HRegion 按大小分割的(默认 10G),每个表一开始只有一个 HRegion,随着数据不断插入 表,HRegion 不断增大,当增大到一个阀值的时候,HRegion 就会等分会两个新的 HRegion。 当表中的行不断增多,就会有越来越多的 HRegion。

HStore存储是HBase存储的核心,由两部分组成,一部分是MemStore,一 部分是StoreFile。MemStore是 Sorted Memory Buffer,用户写入的数据首先会放入MemStore,当MemStore满了以后会Flush成一个StoreFile(底层实现是HFile)。

 

HBase的rowkey的设计原则

HBase是三维有序存储的,通过rowkey(行键),column key(column family和qualifier)和TimeStamp(时间戳)这个三个维度可以对HBase中的数据进行快速定位。

HBase中rowkey可以唯一标识一行记录,在HBase查询的时候,有两种方式:

1、通过get方式,指定rowkey获取唯一一条记录 
2、通过scan方式,设置startRow和stopRow参数进行范围匹配 

3、全表扫描,即直接扫描整张表中所有行记录

rowkey长度原则:

rowkey是一个二进制码流,可以是任意字符串,最大长度64kb,实际应用中一般为10-100bytes,以byte[]形式保存,一般设计成定长。建议越短越好,不要超过16个字节,原因如下:

数据的持久化文件HFile中是按照KeyValue存储的,如果rowkey过长,比如超过100字节,1000w行数据,光rowkey就要占用100*1000w=10亿个字节,将近1G数据,这样会极大影响HFile的存储效率; 
MemStore将缓存部分数据到内存,如果rowkey字段过长,内存的有效利用率就会降低,系统不能缓存更多的数据,这样会降低检索效率。 

目前操作系统都是64位系统,内存8字节对齐,控制在16个字节,8字节的整数倍利用了操作系统的最佳特性。

 

rowkey散列原则:

如果rowkey按照时间戳的方式递增,不要将时间放在二进制码的前面,建议将rowkey的高位作为散列字段,由程序随机生成,低位放时间字段,这样将提高数据均衡分布在每个RegionServer,以实现负载均衡的几率。如果没有散列字段,首字段直接是时间信息,所有的数据都会集中在一个RegionServer上,这样在数据检索的时候负载会集中在个别的RegionServer上,造成热点问题,会降低查询效率。

 

rowkey唯一原则:

必须在设计上保证其唯一性,rowkey是按照字典顺序排序存储的,因此,设计rowkey的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。

 

什么是热点:

HBase中的行是按照rowkey的字典顺序排序的,这种设计优化了scan操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于scan。然而糟糕的rowkey设计是热点的源头。热点发生在大量的client直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)。大量访问会使热点region所在的单个机器超出自身承受能力,引起性能下降甚至region不可用,这也会影响同一个RegionServer上的其他region,由于主机无法服务其他region的请求。设计良好的数据访问模式以使集群被充分,均衡的利用。

为了避免写热点,设计rowkey使得不同行在同一个region,但是在更多数据情况下,数据应该被写入集群的多个region,而不是一个。

 

下面是一些常见的避免热点的方法以及它们的优缺点:

  • 加盐

这里所说的加盐不是密码学中的加盐,而是在rowkey的前面增加随机数,具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的region的数量一致。加盐之后的rowkey就会根据随机生成的前缀分散到各个region上,以避免热点。

  • 哈希

哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey,可以使用get操作准确获取某一个行数据

  • 反转

第三种防止热点的方法时反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey,但是牺牲了rowkey的有序性。

反转rowkey的例子 

以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,这样的就避免了以手机号那样比较固定开头导致热点问题

  • 时间戳反转

一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用,可以用Long.Max_Value - timestamp追加到key的末尾,例如[key][reverse_timestamp],[key]的最新值可以通过scan [key]获得[key]的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。

比如需要保存一个用户的操作记录,按照操作时间倒序排序,在设计rowkey的时候,可以这样设计 
[userId反转][Long.Max_Value - timestamp],在查询用户的所有操作记录数据的时候,直接指定反转后的userId,startRow是[userId反转][000000000000],stopRow是[userId反转][Long.Max_Value - timestamp] 

如果需要查询某段时间的操作记录,startRow是[user反转][Long.Max_Value - 起始时间],stopRow是[userId反转][Long.Max_Value - 结束时间]

 

 

在hbase的文件组织形式上,一定要记住:
一个集群会有多个regionserver

每一个regionserver就是用来去管理master分配给它的region
每一个regionserver中管理的所有的region是有可能来自于多个不同的table的

每一个regionserver中的所有region在进行数据插入的时候是会被记录日志的。

Hbase是WAL的

2.zookeeper选举

 

当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的 Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另外一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。先介绍basic paxos流程:

1 .选举线程由当前Server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的Server;

2 .选举线程首先向所有Server发起一次询问(包括自己);

3 .选举线程收到回复后,验证是否是自己发起的询问(验证zxid是否一致),然后获取对方的id(myid),并存储到当前询问对象列表中,最后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中;

4. 收到所有Server回复以后,就计算出zxid最大的那个Server,并将这个Server相关信息设置成下一次要投票的Server;

5. 线程将当前zxid最大的Server设置为当前Server要推荐的Leader,如果此时获胜的Server获得n/2 + 1的Server票数, 设置当前推荐的leader为获胜的Server,将根据获胜的Server相关信息设置自己的状态,否则,继续这个过程,直到leader被选举出来。

通过流程分析我们可以得出:要使Leader获得多数Server的支持,则Server总数必须是奇数2n+1,且存活的Server的数目不得少于n+1.

每个Server启动后都会重复以上流程。在恢复模式下,如果是刚从崩溃状态恢复的或者刚启动的server还会从磁盘快照中恢复数据和会话信息,zk会记录事务日志并定期进行快照,方便在恢复时进行状态恢复。

 

3.HDFS

 

3.1 心跳机制

datanode启动的时候会寻找对应的namenode,进行汇报。

汇报的数据包括:

    1.当前这个datanode的状态信息

    2.当前这个datanode所持有的所有的数据块的信息(有哪些数据块的副本),datanode是不会知道自己保存的所有数据块副本到底是属于哪个文件的.

datanode会启动一个线程,默认每隔三秒向namenode进行汇报,

HDFS的标准:如果连续10次没有收到datanode的汇报,那么namenode就会认为该datanode存在宕机的可能,并没有断定死了。

如果datanode发送心跳的线程死了,namenode会发送命令向这个datanode进行确认,查看这个发送心跳数据包的服务是否还能正常运行,先查看一次,如果第一次查看没有返回结果或者没有接收到任何结果,会再发送一次命令,第二次如果还是这样,就判断死了。

最终namenode判断datanode死亡的时间计算公式:timeout= 10 *心跳间隔时间 + 2 * 检查一次消耗的时间

默认心跳时间 dfs.heartbeat.interval:3s  注意单位是秒

检查时间(checktime) heartbeat.recheck.interval:5min 就是300000   注意单位是毫秒

所以是10分半钟没反应就判断datanode死了。

查看心跳时间的命令:hdfs getconf -confKey dfs.heartbeat.interval

查看检查时间命令:hdfs getconf -confKey heartbeat.recheck.interval

3.2 HDFS的安全模式

决定HDFS集群启动时长会有两个因素:

    1、磁盘元数据的大小

    2、datanode的节点个数

当元数据很大,或者节点个数很多的时候,那么HDFS的启动,会需要一段很长的时间,所以不能让还没有完全启动的时间就让HDFS对外提供服务。所以HDFS提供了一种安全模式,在HDFS启动命令start-dfs.sh执行的时候会自动进入安全模式。要等待datanode陆陆续续的进行汇报,安全模式是保护自己就是保护数据。 直到是所有的数据都是齐全的时候会自动退出安全模式

在安全模式下:

    如果一个涉及到元数据的修改的话,都不能

    如果一个操作仅仅只是查询,那么是被允许的。

    所谓的安全模式仅仅只是保护namenode不是保护datanode

3.3 副本存放策略

    1,数据的安全

    2,数据块的负载均衡

概念:是说将一个数据块的三个副本放在三个不同的节点上

具体实现:

负载均衡:

    1.节点均衡

    2.机架均衡

    3.磁盘均衡

sbin/start-balancer.sh

sbin/start-balancer.sh -threshold 5

当前这个操作在集群中的任意两个节点的磁盘使用占比不超过5%的时候结束

sbin/start-balancer.sh -t 10%

机器容量最高的那个值和最低的那个值得差距不能超过10%

HDFS集群默认不允许balance操作占用很大的网络带宽,这个带宽是可以调整的

hdfs dfsadmin -setBalanacerBandwidth 10485760

上面的配置是10M/s,默认是1M/s

3.4 namenode和datanode的职责

namenode的职责:

    1.负责客户端请求(读写数据请求)的响应

    2.维护目录树结构(元数据的管理:查询,修改)

    3.配置和应用副本存放策略

    4.管理集群数据块负载均衡问题

 

datanode负责管理用户的文件数据块,并且通过心跳机制汇报给namenode

文件会按照固定的大小(dfs.blocksize)切成若干块后分布式存储在若干台datanode上

每一个文件块可以有多个副本,并存放在不同的datanode上

datanode会定期向namenode汇报自身所保存的文件block信息,而namenode则会负责保持文件的副本数量

HDFS的内部工作机制对客户端保持透明,客户端请求访问HDFS都是想namenode申请来进行的

3.5 HDFS的上传和下载流程

    1.HDFS的写流程

  

 

 2.HDFS的读流程

 

 

3.6 SecondaryNamenode 的作用

NameNode

NameNode主要是用来保存HDFS的元数据信息,比如命名空间信息,块信息等。当它运行的时候,这些信息是存在内存中的。但是这些信息也可以持久化到磁盘上。

上面的这张图片展示了NameNode怎么把元数据保存到磁盘上的。这里有两个不同的文件:

  1. fsimage - 它是在NameNode启动时对整个文件系统的快照
  2. edit logs - 它是在NameNode启动后,对文件系统的改动序列

只有在NameNode重启时,edit logs才会合并到fsimage文件中,从而得到一个文件系统的最新快照。但是在产品集群中NameNode是很少重启的,这也意味着当NameNode运行了很长时间后,edit logs文件会变得很大。在这种情况下就会出现下面一些问题:

  1. edit logs文件会变的很大,怎么去管理这个文件是一个挑战。
  2. NameNode的重启会花费很长时间,因为有很多改动[笔者注:在edit logs中]要合并到fsimage文件上。
  3. 如果NameNode挂掉了,那我们就丢失了很多改动因为此时的fsimage文件非常旧。[笔者注: 笔者认为在这个情况下丢失的改动不会很多, 因为丢失的改动应该是还在内存中但是没有写到edit logs的这部分。]

因此为了克服这个问题,我们需要一个易于管理的机制来帮助我们减小edit logs文件的大小和得到一个最新的fsimage文件,这样也会减小在NameNode上的压力。这跟Windows的恢复点是非常像的,Windows的恢复点机制允许我们对OS进行快照,这样当系统发生问题时,我们能够回滚到最新的一次恢复点上。

现在我们明白了NameNode的功能和所面临的挑战 - 保持文件系统最新的元数据

Secondary NameNode

SecondaryNameNode就是来帮助解决上述问题的,它的职责是合并NameNode的edit logs到fsimage文件中。

上面的图片展示了Secondary NameNode是怎么工作的。

  1. 首先,它定时到NameNode去获取edit logs,并更新到fsimage上。[笔者注:Secondary NameNode自己的fsimage]
  2. 一旦它有了新的fsimage文件,它将其拷贝回NameNode中。
  3. NameNode在下次重启时会使用这个新的fsimage文件,从而减少重启的时间。

Secondary NameNode的整个目的是在HDFS中提供一个检查点。它只是NameNode的一个助手节点。这也是它在社区内被认为是检查点节点的原因。

现在,我们明白了Secondary NameNode所做的不过是在文件系统中设置一个检查点来帮助NameNode更好的工作。它不是要取代掉NameNode也不是NameNode的备份。所以从现在起,让我们养成一个习惯,称呼它为检查点节点吧。

Secondarynamenode作用

SecondaryNameNode有两个作用,一是镜像备份,二是日志与镜像的定期合并。两个过程同时进行,称为checkpoint. 镜像备份的作用:备份fsimage(fsimage是元数据发送检查点时写入文件);日志与镜像的定期合并的作用:将Namenode中edits日志和fsimage合并,防止(如果Namenode节点故障,namenode下次启动的时候,会把fsimage加载到内存中,应用edit log,edit log往往很大,导致操作往往很耗时。)

Secondarynamenode工作原理

日志与镜像的定期合并总共分五步:

  1. SecondaryNameNode通知NameNode准备提交edits文件,此时主节点产生edits.new
  2. SecondaryNameNode通过http get方式获取NameNode的fsimage与edits文件(在SecondaryNameNode的current同级目录下可见到 temp.check-point或者previous-checkpoint目录,这些目录中存储着从namenode拷贝来的镜像文件)
  3. SecondaryNameNode开始合并获取的上述两个文件,产生一个新的fsimage文件fsimage.ckpt
  4. SecondaryNameNode用http post方式发送fsimage.ckpt至NameNode
  5. NameNode将fsimage.ckpt与edits.new文件分别重命名为fsimage与edits,然后更新fstime,整个checkpoint过程到此结束。 在新版本的hadoop中(hadoop0.21.0),SecondaryNameNode两个作用被两个节点替换, checkpoint node与backup node. SecondaryNameNode备份由三个参数控制fs.checkpoint.period控制周期,fs.checkpoint.size控制日志文件超过多少大小时合并, dfs.http.address表示http地址,这个参数在SecondaryNameNode为单独节点时需要设置。

Import Checkpoint(恢复数据)

如果主节点namenode挂掉了,硬盘数据需要时间恢复或者不能恢复了,现在又想立刻恢复HDFS,这个时候就可以import checkpoint。步骤如下:

  1. 准备原来机器一样的机器,包括配置和文件
  2. 创建一个空的文件夹,该文件夹就是配置文件中dfs.name.dir所指向的文件夹。
  3. 拷贝你的secondary NameNode checkpoint出来的文件,到某个文件夹,该文件夹为fs.checkpoint.dir指向的文件夹(例如:/home/hadadm/clusterdir/tmp/dfs/namesecondary)
  4. 执行命令bin/hadoop namenode –importCheckpoint
  5. 这样NameNode会读取checkpoint文件,保存到dfs.name.dir。但是如果你的dfs.name.dir包含合法的 fsimage,是会执行失败的。因为NameNode会检查fs.checkpoint.dir目录下镜像的一致性,但是不会去改动它。

一般建议给maste配置多台机器,让namesecondary与namenode不在同一台机器上值得推荐的是,你要注意备份你的dfs.name.dir和 ${hadoop.tmp.dir}/dfs/namesecondary。

 

4.MapReduce

4.1 MR编程套路图

4.2 MapTask并行度决定机制

 

并行度就是个数

对应关系:

    一个Container -- 一个Task  -- 一个JVM进程

    一个ReduceTask -- 一组key-value -- reduce方法的调用次数

默认情况下:一个数据块 -- 一个MapTask -- 一个FileSplit(文件切片)

FileSplit:封装了一个数据块的各种必要信息:副本个数,副本存放地,blocksize,文件名

对应关系:splits.size( )==一个mapReduce程序的总mapTask数

其实每次启动给一个mapTask,那么当前这个节点,都会远程的去获取到MRAppMaster主控程序解析出来的对应的
FileSplit对象, 每个mapTask的启动都需要一个FileSplit对象

如果每个节点都要启动10个mapTask,那就相当于要申请10个container,启动10个JVM进程。
但是每个进程都只处理了1个FileSplit
优化:可以让多个mapTask放在一个container中用一个JVM去执行
JVM重用   mapred.job.reuse.jvm.num.tasks = 1

4.3 ReduceTask并行度决定机制

4.4 Task并行度经验之谈

 

4.5 combiner 组件

combiner组件的父类就是Reducer

combiner组件是在每一个maptask所在的节点运行

combiner组件的意义就是对每一个maptask的输出进行局部汇总,以减少网络传输量

4.6 Map阶段流程

5.7 Reduce阶段流程

5.8 shuffle阶段流程

4.9 MR调优

 

5.Spark相关

 

数据放在哪现在不知道,当反向注册的时候我们就认为new SparkContext这段代码时候初始化已经完成的差不多了。我们开始执行transformation的代码。但是代码不会真的运行,直到我们遇到一个action操作。然后生成一个job任务,进行stage的划分。

Stage的划分是由DAGScheduler来操作的。Stage里面封装的就是一些Task,这些Task统一用TaskSet来封装的,这些Task逻辑是一样的,只是针对的数据不一样。DAGScheduler然后把TaskSet发送给TaskScheduler,TaskScheduler在发送task给Executor通过Akka把消息和信息发送,就需要用到网络,就需要对task进行序列化,这个发送的过程不是瞎发的,需要考虑数据稳定性,根据Task的分配算法,分配task。

 

 

5.1sparkStreaming运行流程

 

6.Flume

6.1 Flume运行机制

 

7.yarn的相关流程

7.1 yarn的三大组件

7.2 yarn的运行流程

7.3 yarn的资源调度器

8.zookeeper的相关

8.1 有哪几种节点类型

    1)持久化目录节点   PERSISTENT

    2)持久化顺序编号目录节点   PERSISTENT_SEQUENTIAL

    3)临时目录节点   EPHEMERAL

    4)临时顺序编号目录节点   EPHEMERAL_SEQUENTIAL

8.2 zookeeper如何保证事务的顺序一致性

zookeeper采用递增的事务ID来标识

为了保证事务的顺序一致性,zookeeper 采用了递增的事务 id 号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了 zxid。实现中 zxid 是一个 64 位的数字,它高32 位是 epoch 用来标识 leader 关系是否改变,每次一个 leader 被选出来,它都会有一个新的 epoch,标识当前属于那个 leader 的统治时期。低 32 位用于递增计数。

8.3 ZK监听机制(通知机制)

client端会对某个znode建立一个watcher事件,当该znode发生变化时,这些client会收到zk的通知,然后client可根据znode的变化做出业务上的改变。

客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、节点删除、子目录节点增加删除)时,ZooKeeper 会通知客户端。监听机制保证 ZooKeeper 保存的任何的数据的任何改变都能快速的响应到监听了该节点的应用程序监听器的工作机制,其实是在客户端会专门创建一个监听线程,在本机的一个端口上等待zk 集群发送过来事件

注意:监听只生效一次

8.4 ZK提供了什么?

    1)文件系统

    2)监听机制(通知机制)

8.5 zk能做什么?或者zk是什么?

    1)命名服务: 通过命名服务,客户端可以根据指定名字来获取资源的实体、服务地址和提供者的信息。Zookeeper 也可帮助应用系统通过资源引用的方式来实现对资源的定位和使用

    2)配置管理: 程序总是需要配置的,如果程序分散部署在多台机器上,要逐个改变配置就变得困难。现在把这些配置全部放到 ZooKeeper 上去,保存在 ZooKeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到ZooKeeper 的通知,然后从 ZooKeeper 获取新的配置信息应用到系统中就好

    3)集群管理:是否有机器加入和退出、选举master

    4)分布式锁: 锁服务可以分为两三类。一个是写锁,对写加锁,保持独占,或者叫做排它锁,独占锁。一个是读锁,对读加锁,可共享访问,释放锁之后才可进行事务操作,也叫共享锁。一个是控制时序,叫时序锁

    5)队列管理: 两种类型的队列:1、同步队列:当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。2、先进先出队列:队列按照 FIFO 方式进行入队和出队操作。

8.6 zk的工作原理

zookeeper的核心是原子广播,这个机制保证了各个server之间的同步,实现这个机制的协议叫做zab协议,zab协议有两种模式,分别是崩溃恢复模式(选主)和广播模式(同步)

1、当服务启动或者在领导者崩溃后,ZAB 就进入了恢复模式,当领导者被选举出来,且大多数 Server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 leader和 follower 之间具有相同的系统状态。

2、当 ZooKeeper 集群选举出 leader 同步完状态退出恢复模式之后,便进入了原子广播模式。所有的写请求都被转发给 leader,再由 leader 将更新 proposal 广播给 follower

8.7 Paxos 算法概述(ZAB 协议)

一种基于消息传递且具有高度容错特性的一致性算法。(也叫分布式一致性算法)

分布式系统中的节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messagespassing)。

 

 

基于消息传递通信模型的分布式系统,不可避免的会发生以下错误:进程可能会慢、被杀死或者重启,消息可能会延迟、丢失、重复,在基础 Paxos 场景中,先不考虑可能出现消息篡改即拜占庭错误(Byzantine failure,即虽然有可能一个消息被传递了两次,但是绝对不会出现错误的消息)的情况。Paxos 算法解决的问题是在一个可能发生上述异常的分布式系统中如何就某个值达成一致,保证不论发生以上任何异常,都不会破坏决议一致性。

Paxos 算法使用一个希腊故事来描述,在 Paxos 中,存在三种角色,分别为Proposer(提议者,用来发出提案 proposal),Acceptor(接受者,可以接受或拒绝提案),Learner(学习者,学习被选定的提案,当提案被超过半数的 Acceptor 接受后为被批准)。

ZooKeeper 的选举算法有两种:一种是基于 Basic Paxos(Google Chubby 采用)实现的,另外一种是基于 Fast Paxos(ZooKeeper 采用)算法实现的。系统默认的选举算法为 Fast Paxos。并且 ZooKeeper 在 3.4.0 版本后只保留了 FastLeaderElection 算法。

 

9. JVM相关原理

9.1 JVM架构

 

我们java虚拟机只认识.class的文件

当我们执行java Test的时候用类加载器加载具有main方法的.class文件

并创建一个 运行时区并在里面创建一个 方法区(存静态变量,常量,线程共享的)和 (存数组,对象,线程共享的,这是垃圾回收所涉及的区域), (存变量,栈是线程私有的,当我们程序运行完成栈也就消失了,没涉及到垃圾回收的事), 程序计数器(用来维持代码的先后执行顺序,就是维持我们的代码逻辑,属于线程私有的,当线程运行完事也就消失了。),本地方法栈(配合本地接口,java是不能操作操作系统的,但是C语言是可以操作硬件的,我们的java IO流就是操作硬件,我们java干不了就需要本地方法栈调用本地接口来操作硬件),

我们每调用一个方法就会创建一个栈帧,就会入栈,运行方法的时候就会弹栈帧,使用递归的方式不断的调用方法,就会报栈内存溢出。

a)        堆内存代表的意思就是完全不接受java虚拟机的管理,设置堆内存2g就是使用的操作系统的2g内存

b)       非堆内存(也叫非堆)存在方法区里面的

c)        代码能正常运行就需要程序计算机,变量要存储起来,调用方法的时候要出栈入栈就需要栈。

d)       本地方法栈:操作某些事的时候java做不了就需要它,它就调用本地接口。

e)         

9.2 代码运行流程解读

 

1)首先IDEA工具会帮我们进行编译,生成两个class文件:HelloJVM.class         Student.class文件

2)开始运行代码,就会启动JVM,这个时候,我们的类加载器会根据类加载的路径去加载具有main方法的.class文件。就我们的整个例子而言,那么加载的就是HelloJVM.class,就会把这个文件信息记载到方法区里面

3)开始运行我们的main方法。

首先执行的就是这段代码

Student aura =new Student(“aura”);

a. 这段代码运行,那么我们的JVM就会去方法去里面去加载Student的类信息。但是这个时候发现没有这个类的信息。接着就会使用类加载器去加载

b. Student的类的信息,然后加载到方法去里面。接着就会在堆内存里面会给这个aura分配内存,用于存储这个实例对象。

并且这个实例中会持有指向方法区中的Student的引用。(不然我们后面怎么可能进行方法的调用呢?)

c. 上面我们在内存区域里面开辟了空间存储了Student实例(具有了内存地址),会把这个引用赋值给这个变量aura,这个变量就存在栈内存里面。

d. 接下来执行的是 aura.sayHello();

首先在该线程里面创建frame 存入栈里面

方法调用完了以后就是出栈。就没了。

9.3 垃圾回收过程

 

堆:(一般分为三个区域,年轻带,年老带,永久带(不关心,也可以不归到堆中。单独去分的64M或者100M))

在年轻带中又分为一等区(Eden区)和Survivor区(又分区Survivor s1和Survivor s2)

 

我们回收了好几次还回收不了的对象就会放到老年带中。

老年带也存不下来,就会发生FullGC影响就比较大了。会伴随了minorGC的发生,还会有Stop the world,此时我们的代码会停止运行(工作线程停止),只运行GC的线程。我们也不能说FullGC是坏事,因为FullGC完成就有很多空间了(可以创建更多的对象)。这是个正常的生理过程,但是频繁的发生就是有问题了。我们的程序就有可能跑不了了。小GC对我们的影响不大。

我们需要调优的就是FullGC。如果我们年轻带设置的过小,就会存不下,就会到年老带。我们提前让年轻带的对象进入到了年老带,这样就会频繁发生FullGC。

一般是不用调JVM的参数的。企业一般是有专业的人来管理的。因为JVM是很敏感的。

9.4 JAVA8 的内存模型

为什么要改变设计:因为我们类多了可能不够用,指定多了不好。少了也不好。不好控制。直接使用本地内存。你愿意怎么用就怎么用。但是很勉强。最根本的原因是HotSport(我们现在用的jvm虚拟机)和JRockit合二为一。因为oracle收购了别的公司的jvm虚拟机。为了跟以后的java虚拟机进行合并。别的java虚拟机是有元空间的。所以要这么设置。

 

插曲:hbase读数据的过程:

我们的数据在Memsore(内存)里面有一部分。还有一部分在StoreFile(磁盘上面),这两个相加一定是完整的数据,但是我们在缓存中还有一点数据。

缓存从某种意义上来说也是属于内存。hbase的设计是先去Memsore去读。没有再去缓存中去读,没有就是StoreFile中去读。

缓冲的设计是:有0.25的空间用来存第一次访问的数据,还有0.5是用来存数据被访问了两次或者两次以上的数据。这样级别上升了,第一次访问的数据就挪到了这里。当我们的第一次访问的数据越来越多的时候,采用了LRU来淘汰数据。剩下的0.25用来存(in_memory=false(默认),如果改成true就是代表hbase的数据永远都会在内存里面。),hbase的设计是模仿java虚拟机的时候。FullGC这类的问题是hbase更底层的问题。

hbase解决的就是简单的查询,但是数据量大还很快,就需要依赖于内存,所以就需要设置这三个参数。

9.5 常见的垃圾回收算法

 

简单来说就是有引用就+1,取消引用就-1,回收为0的。

使用引用计数法,如果两个对象相互引用就计算不了了。

使用引用计数法:

 

 

 

 

 

 

 

  • 6
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值