写这个博客主要是看了袋鼠云的flinkStreamSQL git地址:https://github.com/DTStack/flinkStreamSQL 自己还往上提交了kudu的sink和side 第一次commit到github上面还是很开心的。
这里重点说的是flinkStreamSQL是如何完成side的,主要其实是两种缓存方案LRU和ALL。这里忽略所有的sql解析 注册等等,单纯的从流的角度完成维表join。
ALL相对比较简单先介绍ALL:
从KuduAllReqRow开始,这里是维表的加载以及如何join。先查看继承关系,只看需要关心的。先看AllReqRow,AllReqRow只需要关心open()方法。这里是同步缓存,缓存完毕后数据才继续往下走。
@Override
public void open(Configuration parameters) throws Exception {
super.open(parameters);
//初始化全量维表数据
initCache();
System.out.println("----- all cacheRef init end-----");
//start reload cache thread
SideTableInfo sideTableInfo = sideInfo.getSideTableInfo();
//创建线程池 开启调度线程 每隔一段时间查询全量数据 覆盖原有数据
es = Executors.newSingleThreadScheduledExecutor(new DTThreadFactory("cache-all-reload"));
es.scheduleAtFixedRate(() -> reloadCache(), sideTableInfo.getCacheTimeout(), sideTableInfo.getCacheTimeout(), TimeUnit.MILLISECONDS);
}
这里其实就是主要的思想了,开启一个调度线程不停的去查询数据然后缓存。这里缓存用的是
AtomicReference<Map<String, List<Map<String, Object>>>> cacheRef = new AtomicReference<>();
第一个string: join的条件 List:维表中本条数据的 字段名 字段值
以此条sql为例
insert into MyResult
select m.id,m.title,m.amount,s.tablename1
from MyTable m left join sideTable s on m.id=s.id ;
从streamAPI角度就是 MyTable流来了根据m.id的值去Map中查找是否存在对应的List,如果存在则根据字段名取出对应的值拼接到MyTable流中。
LRU: 主要是在SideAsyncOperator类中 主要方法getSideJoinDataStream()
public static DataStream getSideJoinDataStream(DataStream inputStream, String sideType, String sqlRootDir, RowTypeInfo rowTypeInfo, JoinInfo joinInfo,
List<FieldInfo> outFieldInfoList, SideTableInfo sideTableInfo) throws Exception {
AsyncReqRow asyncDbReq = loadAsyncReq(sideType, sqlRootDir, rowTypeInfo, joinInfo, outFieldInfoList, sideTableInfo);
//TODO How much should be set for the degree of parallelism? Timeout? capacity settings?
return AsyncDataStream.orderedWait(inputStream, asyncDbReq, 10000, TimeUnit.MILLISECONDS, asyncCapacity)
.setParallelism(sideTableInfo.getParallelism());
}
依然只看需要关注的类:
可以看到这里是用的异步IO查询,这里的查询是根据on后的条件 ,还是上面的逻辑代码 传入m.id=3 此时就会到数据库中查询s.id=3的数据 缓存到Cache<String, CacheObj> cache中 String是存入的值 如 3,CacheObj= new CacheObj(type, content) type为枚举字段MissVal(为查询到数据),SingleLine(查询的数据只会有一条(1对1)),MultiLine(查询的数据有多条(1对多)) 。content为Object,如果是MultiLine 则表明content中需要存入多个数据。
缓存时间:
cache = CacheBuilder.newBuilder()
.maximumSize(sideTableInfo.getCacheSize())
.expireAfterWrite(sideTableInfo.getCacheTimeout(), TimeUnit.MILLISECONDS)
.build();
在初始化Cache<String, CacheObj> cache时已经决定了缓存的大小和时间。
从streamAPI角度就是 MyTable流来了根据m.id的值去数据库中查询,如果存在放入SingleLine或MultiLine,不存在则放入MissVal。然后缓存起来,这里调用的是异步IO查询。当有重复的key到来时就直接从缓存中获取数据往下发送。