Flink流处理查询外部数据源的解决方法
在流处理
中,常涉及到与外部数据源的交互
。例如在实时数仓的设计中通常是流式采集用户的浏览、下单等行为,但是用户信息,SKU、SPU等信息因为是维度表不适合通过流的方式读取(流中无法进行随意消费),而是存储在外部数据库例如Hbase、ClieckHouse的实时性能比较高的分布式数据库中。但是与外部数据源的查询常常是流式计算的瓶颈
,下面给出了两种优化方案:旁路缓存技术
和Flink提供的异步查询
方式。
方案1: 加入旁路缓存模式 (cache-aside-pattern)
旁路缓存模式是一种非常常见的按需分配缓存的模式。
如下图,任何请求优先访问缓存,缓存命中,直接获得数据返回请求
。如果未命中则查询数据库,同时把结果写入缓存以备后续请求使用
。
这种缓存策略有几个注意点
- 缓存要设置
过期时间
,不然冷数据会常驻缓存浪费资源。 - 要考虑
维度数据是否会发生变化
,如果发生变化要主动更新缓存(防止使用了缓存中的旧数据而产生问题)。
缓存的选型
一般两种:堆缓存(应用程序内存)或者独立缓存(redis,memcache)
。
- 堆缓存:从性能角度考虑更加高效,但是可管理性差,其他进程无法维护缓存中数据,比较消耗内存。
补充:Flink状态内存也属于这一种,如果缓存大量维度表信息会消耗大量内存,并且状态管理只能在同一并行度同一key下访问
- 独立缓存:性能较堆缓存弱一点,但是当数据发生变化时,独立缓存服务管理性更强,如果数据量特别大,独立缓存更容易扩展。缺点是会有创建连接、网络IO等消耗。
方案2: 异步查询
在Flink 流处理过程中,经常需要和外部系统进行交互
,用维度表补全事实表中的字段。
例如:在电商场景中,需要一个商品的skuid去关联商品的一些属性,例如商品所属行业、商品的生产厂家、生产厂家的一些情况;在物流场景中,知道包裹id,需要去关联包裹的行业属性、发货信息、收货信息等等。
默认情况下,在Flink的MapFunction中,单个并行只能用同步方式去交互: 将请求发送到外部存储
,IO阻塞,等待请求返回,然后继续发送下一个请求。这种同步交互的方式往往在网络等待上就耗费了大量时间
。为了提高处理效率,可以增加MapFunction的并行度,但增加并行度就意味着更多的资源
,并不是一种非常好的解决方式。
Flink 在1.2中引入了Async I/O,在异步模式下,将IO操作异步化,单个并行可以连续发送多个请求
,哪个请求先返回就先处理,从而在连续的请求间不需要阻塞式等待,大大提高了流处理效率。Async I/O 是阿里巴巴贡献给社区的一个呼声非常高的特性,解决与外部系统交互时网络延迟成为了系统瓶颈的问题。
异步查询实际上是把维表的查询操作托管给单独的线程池完成,这样不会因为某一个查询造成阻塞
,单个并行可以连续发送多个请求,提高并发效率。这种方式特别针对涉及网络IO的操作,减少因为请求等待带来的消耗。
使用异步API的先决条件
- 正确实现数据库(或键/值存储)的异步I/O交互需要
支持异步请求的数据库客户端
。许多主流数据库都提供了这样的客户端。 - 如果没有这样的客户端,可以通过
创建多个客户端并使用线程池处理同步调用的方法
,将同步客户端转换为有限并发的客户端。这种方法通常比正规的异步客户端效率稍低。
phoenix目前没有提供异步客户端
Flink的异步I/O API
Flink的异步I/O API 允许用户在流处理中使用异步请求客户端。API处理与数据流的集成,同时还能处理好顺序、事件时间和容错等
。
在具备异步数据库客户端的基础上,实现数据流转换操作与数据库I/O交互需要具备以下三部分:
- 实现分发请求的 AsyncFunction
- 获取数据库交互结果并发送给 ResultFuture 的回调函数
- 将异步 I/O 操作应用于 DataStream 作为 DataStream 的一次转换操作。