通过6. sharding-jdbc源码之group by结果合并(1)的分析可知,如果要走GroupByMemoryResultSetMerger,那么需要这样的SQL:SELECT o.status, count(o.user_id) count_user_id FROM t_order o where o.user_id=10 group by o.status order by count_user_id asc,即group by和order by的字段不一样;接下来的分析都是基于这条SQL;
ExecutorEngine.build()方法中通过return new GroupByMemoryResultSetMerger(columnLabelIndexMap, resultSets, selectStatement);调用GroupByMemoryResultSetMerger,GroupByMemoryResultSetMerger的构造方法源码如下:
在实际表t_order_0和t_order_1上执行SQL返回的结果如下:
知道实际表的返回结果后,后面的分析更容易理解;假定这些返回结果用json表示为:{[{"status":"NEW", "count_user_id":1},{"status":"VALID", "count_user_id":1},{"status":INIT, "count_user_id":2}],[{"status":"VALID", "count_user_id":1},{"status":"INIT", "count_user_id":1},{"status":""NEW, "count_user_id":3}]}
init()方法源码如下:
initForFirstGroupByValue()源码如下:
该方法都是为了接下来的聚合计算做准备工作;
aggregate()源码如下–即在内存中将多个实际表中返回的结果进行聚合:
经过for (ResultSet each : resultSets) { while (each.next()) { … 遍历所有结果并聚合计算后,aggregationMap这个map中已经聚合计算完后的结果,如下所示:
再将aggregationMap中的结果封装到Map<GroupByValue, MemoryResultSetRow> dataMap这个map中,结果形式如下所示:
{ "VALID": ["VALID", 2], "INIT": ["INIT", 5], "NEW": ["NEW", 3] }
MemoryResultSetRow的本质就是一个Object[] data,所以其值是["VALID", 2],["INIT", 5]这种形式
将结果转成List<MemoryResultSetRow>,并且排序–如果有order by,那么根据order by的值进行排序,否则根据group by的值排序:
private List<MemoryResultSetRow> getMemoryResultSetRows(final Map<GroupByValue, MemoryResultSetRow> dataMap) { List<MemoryResultSetRow> result = new ArrayList<>(dataMap.values()); Collections.sort(result, new GroupByRowComparator(selectStatement)); return result; }
到这里,GroupByMemoryResultSetMerger即内存GROUP聚合计算已经分析完成,依旧通过运行过程图解加深对GroupByMemoryResultSetMerger的理解,运行过程图如下图所示:
总结
正如GroupByMemoryResultSetMerger的名字一样,其实现原理是把所有结果加载到内存中,在内存中进行计算,而GroupByMemoryResultSetMerger是流式计算方法,并不需要加载所有实际表返回的结果到内存中。这样的话,如果SQL返回的总结果数比较多,GroupByMemoryResultSetMerger的处理方式就可能会撑爆内存;这个是使用sharding-jdbc一个非常需要注意的地方;