Spark流处理相关知识点(包含:SparkStreaming,Kafka,Flume,HBase)
HBase
- 优势:
- 线性扩展
- 数据存储储在hbase上,备份机制健全
- 通过zookeeper协调查找数据,访问速度快
- 特点:
- 海量存储
- 列式存储
- 极易扩展
- 高并发
- 稀疏
- 数据模型
- ROW KEY
- 时间戳TimeStamp
- 列族
- 列
- CELL单元格
- cell没有类型,全部使用字节码储存
- HLog
- 角色
- HMaster,可以有多个主节点
- 监控 RegionServer
- 处理 RegionServer 故障转移
- 处理元数据的变更
- 处理 region 的分配或移除
- 在空闲时间进⾏数据的负载均衡
- 通过 Zookeeper 发布⾃⼰的位置给客⼾端
- 多个从节点,HregionServer
- 负责存储 HBase 的实际数据
- 处理分配给它的 Region
- 刷新缓存到 HDFS
- 维护 HLog
- 执⾏压缩
- 负责处理 Region 分⽚
- 架构
- 组件说明
- Write-Ahead logs
- HBase的修改记录
- HFile
- 保存在磁盘的原始数据,是实际的存储文件
- Store
- 一个Store对应一个列族,HFile存储在Store中
- MemStore
- 内存存储,用来保存当前的数据操作,当数据存在WAL之后,RegionServer 会在内存中存储键值对。
- Region
- Hbase 表的分⽚,HBase 表会根据 RowKey 值被切分成不同的 region 存储在 RegionServer 中,在⼀个 RegionServer 中可以有多个不同的 region。
- HBase JAVA API
- 表结构操作
- 初始化
conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum","node01:2181,....");
Connection connection = ConnectionFactory.createConnection(conf);
admin = connection.getAdmin();
- 判断表是否存在
TableName tableName = TableName.valueOf("Student");
boolean res = admin.tableExists(tableName);
- 创建表
String tableName = "Test";
String[] columnFamily = {"info","education"};
if(admin.tableExists(TableName.valueOf(tableName))){
System.out.println("表" + tableName + "已存在");
//退出 JVM
System.exit(0);
}else{
//创建表属性对象,表名需要转字节
HTableDescriptor descriptor = new HTableDescriptor(TableName.valueOf(tableName));
//创建多个列族
for(String cf : columnFamily){
descriptor.addFamily(new HColumnDescriptor(cf));
}
//根据对表的配置,创建表
admin.createTable(descriptor);
//修改表结构
//admin.modifyTable(TableName.valueOf(tableName),descriptor);
System.out.println("表" + tableName + "创建成功!");
}
- 删除表
String tableName = "Test";
if(admin.tableExists(TableName.valueOf(tableName))){
//禁用此表
admin.disableTable(TableName.valueOf(tableName));
//启用表
//admin.enableTable(TableName.valueOf(tableName));
//删除表
admin.deleteTable(TableName.valueOf(tableName));
System.out.println("表" + tableName + "删除成功!");
}else{
System.out.println("表" + tableName + "不存在!");
}
- 查询库中所有表
TableName[] ts = admin.listTableNames();
- 表数据操作
- 初始化
String tableName = "Student";
//1. 创建HBase的配置对象
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum","node01:2181,node02:2181,node03:2181");
//2. 创建一个客户端,即连接对象
conn = ConnectionFactory.createConnection(conf);
//3. 创建 Table对象
table = conn.getTable(TableName.valueOf(tableName));
- 添加数据和更新数据
//!!添加单条数据!!
//初始化Put对象,并设置RowKey
Put put = new Put(Bytes.toBytes("10003"));
//2. 增加列和数据【参数分别为:列族名,列名,列的数据】
put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("name"),Bytes.toBytes("wangwu"));
//3. 增加第2列数据
put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("age"),Bytes.toBytes("16"));
//4. 插入数据到表中
table.put(put);
//!!添加多条数据!!
List<Put> puts = new ArrayList<Put>();
Put put1 = new Put(Bytes.toBytes("10002"));
put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("name"),Bytes.toBytes("lisi"));
puts.add(put1);
Put put2 = new Put(Bytes.toBytes("10003"));
put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("name"),Bytes.toBytes("wangwu"));
puts.add(put2);
table.put(puts);
- 删除数据
//1. 创建删除对象,添加 rowKey
Delete del = new Delete(Bytes.toBytes("10001"));
//2. 可以向del中添加列信息,以便删除此行的某一列数据【其实就是某一个单元格的数据】
//如果不添加某一列,则删除该行的全部信息
del.addColumn(Bytes.toBytes("info"),Bytes.toBytes("age"));
//3. 执行删除操作:也可以像put一样同时删除多个Delete实例
table.delete(del)
- 查询数据
//!!GET查询!!
//1. 创建 Get 对象
Get get = new Get(Bytes.toBytes("10001"));
//2. 设置获取的最大的版本数
get.setMaxVersions(3);
//3. 指定获取哪一列的数据
get.addColumn(Bytes.toBytes("info"),Bytes.toBytes("age"));
//4. 按照 get 实例执行查询操作,获取结果集
Result res = table.get(get);
//5. 遍历结果集获取结果 rawCells()获取单元格数组
for(Cell c:res.rawCells()){
System.out.print("RowKey: " + new String(CellUtil.cloneRow(c)) + ", ");
System.out.print("时间戳: " + c.getTimestamp() + ", ");
System.out.print("列名: " + new String(CellUtil.cloneQualifier(c)) + ", ");
System.out.println("值: " + new String(CellUtil.cloneValue(c)));
}
//!!扫描查询!!
String beginRowKey = "10002";//开始行健
String endRowKey = "10004";//结束行健
//1. 创建 Scan 实例
Scan scan = new Scan();
//2. 配置 Scan
//inclusive表示扫描时是否应包括开始行
scan.withStartRow(Bytes.toBytes(beginRowKey),true);
//与上面类似,只不过默认是不包含结束行
scan.withStopRow(Bytes.toBytes(endRowKey));
scan.setMaxVersions(3);//设置扫描的最大版本数
//设置缓存行数:较高的缓存值将启用更快的扫描程序,但将使用更多的内存。
scan.setCaching(20);
//设置批处理数:在分页查询时有用。注意:使用过滤器时不能使用
scan.setBatch(10);
//3. 执行扫描操作
ResultScanner rs = table.getScanner(scan);
//4. 遍历结果输出
for(Result r:rs){
//遍历每一个 Result
for(Cell c:r.rawCells()){
System.out.print("RowKey: " + new String(CellUtil.cloneRow(c)));
System.out.print("时间戳: " + c.getTimestamp());
System.out.print("列名: " + new String(CellUtil.cloneQualifier(c)));
System.out.println("值: " + new String(CellUtil.cloneValue(c)));
}
}
//!!过滤查询!!
//1. 创建一个行健过滤器,指定比较运算符和比较器
//三个参数:运算符,比较器,行健的值
Filter filter1 = new RowFilter(CompareFilter.CompareOp.LESS_OR_EQUAL,new
BinaryComparator(Bytes.toBytes("10002")));
//2. 创建 Scan 对象
Scan scan1 = new Scan();
scan1.setFilter(filter1);
ResultScanner rs1 = table.getScanner(scan1);
for(Result r:rs1){
System.out.println(r);
}
rs1.close();
System.out.println("---------------------------");
//3. 创建一个值过滤器
//三个参数:运算符,比较器,比较的值
Filter filter2 = new ValueFilter(CompareFilter.CompareOp.EQUAL,new
SubstringComparator("16"));
Scan scan2 = new Scan();
scan2.setFilter(filter2);
ResultScanner rs2 = table.getScanner(scan2);
//输出数据中等于 8 的值的结果
for(Result r:rs2){
System.out.println(r);
}
rs2.close();
System.out.println("---------------------------");
//4. 创建一个列过滤器
Filter filter3 = new QualifierFilter(CompareFilter.CompareOp.LESS_OR_EQUAL,new
BinaryComparator(Bytes.toBytes("age")));
Scan scan3 = new Scan();
scan3.setFilter(filter3);
ResultScanner rs3 = table.getScanner(scan3);
//输出数据中等于 8 的值的结果
for(Result r:rs3){
System.out.println(r);
}
rs3.close();
- 协处理器
Flume
- Flume是一个分布式、可靠、高可用的海量日志聚合系统,支持在系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据的简单处理,并写到各种数据接收方的能力。
- 特点
- 可靠性
- 当节点出现故障时,日志能够被传送到其他节点上而不会丢失。
- 可恢复性
- 还是靠Channel。推荐使用FileChannel,事件持久化在本地文件系统里(性能较差)。
- 三个核心组件:
- source
- Source是数据的收集端,负责将数据捕获后进行特殊的格式化,将数据封装到事件(event) 里,然后将事件推入Channel中。
- channel
- Channel是连接Source和Sink的组件,大家可以将它看做一个数据的缓冲区(数据队列),它可以将事件暂存到内存中也可以持久化到本地磁盘上, 直到Sink处理完该事件。
- sink
- Flume Sink取出Channel中的数据,进行相应的存储文件系统,数据库,或者提交到远程服务器。
- 其他组件:
- Agent
- Agent 是一个 JVM 进程,它以事件的形式将数据从源头送至目的地。Agent 由 Source 、Channel、Sink 三部分组成。
- Event
- 数据的传输单元。也就是事件,类似于Java中的bean类。
- 流的设计
- 多Flume设计,串联flume
- 合并设计
- 多输出设计
Kafka
- Apache Kafka®是⼀个分布式流平台,是⼀个基于发布/订阅模式的消息队列,主要应⽤于实时流处理领域。
- 消息队列的两种模式
- 点对点模式
- (⼀对⼀,消费者主动拉取数据,消息收到后消息清除)
- 发布/订阅模式
- (⼀对多,数据⽣产后,推送给所有订阅者)
- 为什么使用消息队列
- 解耦
- 冗余
- 扩展性
- 灵活性
- 可恢复性
- 顺序保证
- 缓冲
- 异步通信
- 基本架构
- Producer
- 消息生产者
- Consumer
- 消息消费者
- Topic
- 队列
- Consumer Group
- 广播
- Broker
- 一个Kafka服务器就是一个broker,一个broker可以容纳多个topic
- Partition
- 分区
- 分区
- 一个topic可以配置多个Partition,produce发送的消息分发到不同的partition中。consumer接受数据的时候是按照group来接受,kafka确保每个partition只能同一个group中的同一个consumer消费,如果想要重复消费,那么需要其他的组来消费。Zookeerper中保存这每个topic下的每个partition在每个group中消费的offset
- 分区策略
- 轮询策略
- 随机策略
- 按Key保存策略
SparkStreaming
- Spark Streaming接收Kafka、Flume、HDFS等各种来源的实时输入数据,进行处理后,处理结构保存在HDFS、DataBase等各种地方。
- Dstream:Spark Streaming提供了表示连续数据流的、高度抽象的被称为离散流的DStream。Dstream可以看做一组RDDs,即RDD的一个序列。
- 架构:
- Master:记录Dstream之间的依赖关系或者血缘关系,并负责任务调度以生成新的RDD
- Worker:从网络接收数据,存储并执行RDD计算
- Client:负责向Spark Streaming中灌入数据
- Spark Streaming 窗口操作:
- 两个参数:
- – 窗口总长度(window length):你想计算多长时间的数据
- – 滑动时间间隔(slide interval):你每多长时间去更新一次
- SparkStreaming累加器和广播变量
- 待补充
- SparkStreaming算子
- 无状态转化算子
- map(func)
- 返回⼀个新的RDD,该RDD由每⼀个输⼊元素经过func函数转换后组成
- flatMap(func)
- 类似于map,但是每⼀个输⼊元素可以被映射为0或多个输出元素(所以func应该返回⼀个序列,⽽不是单⼀元素),⼀对多
- filter(func)
- 返回⼀个新的RDD,该RDD由经过func函数计算后返回值为true的输⼊元素组成,,过滤数据
- repartition(numPartitions)
- 通过创建或减少partition的数量,来改变DStream的并行度
- union(otherStream)
- 将源DStream和另一个DStream内元素联合,生成一个新的DStream并返回
- count()
- 返回DStream中每个RDD包含的元素数量,并返回以氮元素为内容的RDDs的新DStream
- reduce(func)
- countByValue()
- reduceByKey(func,[numTasks]):
- join(otherStram,[numTasks])
- transform(func)
- 可以对DStream中每个RDD执行RDD=>RDD操作
- 有状态转化操作:
- 基于窗口的操作都需要两个参数,窗口时长以及滑动步长,两者必须是StreamContext的批次间隔的整数倍。
- window()
- 最简单的窗口操作,它返回一个新的DStream来表示所请求的窗口操作的结果数据。
- reduceByWindow()&reduceByKeyAndWindow()
- 对每个窗口更高效的进行归约操作
- countByWindow&countByValueAndWindow
- 前者返回一个表示每个窗口中元素个数的DStream
- 后者返回的DStream则包含窗口中每个值得个数
- updateStateByKey
- 解决跨批次维护状态问题
- 返回一个新状态(sate)的DStream,其每个key的状态由给定的func函数根据先前状态的Key和Key的新值计算而来,每个Key的数据可以用来保存任何状态的数据类型(通常情况下流式计算是无状态的顺序计算方式,这个函数为我们提供了一种记录状态的可能,比如累加网站的点击量的实例,就可以使用该函数很方便地实现)。
- 常用输出操作(Action算子)
- print()
- 打印在Driver节点运行的流式应用中前十条元素
- saveAsObjectFiles(prefix,[suffix])
- 将Dstream中的内容以Java序列化对象的序列化文件进行存储
- saveAsHdoopFiles(prefix,[suffix])
- 将Dstream中的内容保存为一个hadoop文件,输出到HDFS上。
- foreachRDD(func)
- 会将传入的func函数应用在Dstream中的每个RDD上,通常func会将RDD种的数据输出到外部系统中。func需要包含Action操作,以此推动RDDs的运算。
写的不太好,欢迎评论补充~