本文主要基于 MyCAT 1.6.5 正式版
1. 概述
2. 多分片执行 SQL
3. 合并多分片结果
3.1 记录头(header)
3.2 记录行(row)
- 3.1 AbstractDataNodeMerge
- 3.2 DataNodeMergeManager
- 3.3 UnsafeRow
- 3.4 UnsafeExternalRowSorter
- 3.5 UnsafeRowGrouper
4. 救护中心
RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
您对于源码的疑问每条留言都将得到认真回复甚至不知道如何读源码也可以请教噢
新的源码解析文章实时收到通知每周更新一篇左右
认真的源码交流微信群
1. 概述
相信很多同学看过 MySQL 各种优化的文章, 里面 99% 会提到: 单表数据量大了, 需要进行分片 (水平拆分 or 垂直拆分) 分片之后, 业务上必然面临的场景: 跨分片的数据合并今天我们就一起来瞅瞅 MyCAT 是如何实现分片结果合并
跨分片查询大体流程如下:
和 单库单表查询 不同的两个过程:
2 多分片执行 SQL
4 合并多分片结果
下面, 我们来逐条讲解这两个过程
2. 多分片执行 SQL
经过 SQL 解析后, 计算出需要执行 SQL 的分片节点, 遍历分片节点发送 SQL 进行执行
核心代码:
MultiNodeQueryHandler.java#execute(...)
SQL 解析 详细过程, 我们另开文章, 避免内容过多, 影响大家对 分片结果合并 流程和逻辑的理解
3. 合并多分片结果
和 单库单表查询 不同, 多个分片节点都会分别响应 记录头(header) 和 记录行(row) 在开始分析 MyCAT 是怎么合并多分片结果之前, 我们先来回想下 SQL 的执行顺序
- FROM // [1] 选择表
- WHERE // [2] 过滤表
- GROUP BY // [3] 分组
- SELECT // [4] 普通字段, max / min / avg / sum / count 等函数, distinct
- HAVING // [5] 再过滤表
- ORDER BY // [6] 排序
- LIMIT // [7] 分页
3.1 记录头(header)
多个分片节点响应时, 会响应多次 记录头(header) MyCAT 在实际处理时, 只处理第一个返回的 记录头(header) 因此, 在使用时要保证表的 Schema 相同
分片节点响应的 记录头(header) 可以直接返回 MySQL Client 吗? 答案是不可以 AVG 函数 是特殊情况, MyCAT 需要将 AVG 拆成 SUM + COUNT 进行计算举个例子:
- // [1] MySQL Client => MyCAT :
- SELECT AVG(age) FROM student;
- // [2] MyCAT => MySQL Server :
- SELECT SUM(age) AS AVG0SUM, COUNT(age) AS AVG0COUNT FROM student;
- // [3] 最终: AVG(age) = SUM(age) AS AVG0SUM / COUNT(age)
核心代码:
MultiNodeQueryHandler.java#fieldEofResponse(...)
3.2 记录行(row)
3.1 AbstractDataNodeMerge
MyCAT 对分片结果合并通过
AbstractDataNodeMerge
子类来完成
- AbstractDataNodeMerge
- :
-packs : 待合并记录行 (row) 队列队列尾部插入 END_FLAG_PACK 表示队列已结束
-running : 合并逻辑是否正在执行中的标记
~onRowMetaData(...) : 根据 ** 记录列信息(ColMeta)** 构建对应的排序组件和聚合组件需要子类进行实现
~onNewRecord(...) : 插入记录行(row) 到 packs
~outputMergeResult(...) : 插入 END_FLAG_PACK 到 packs
~run(...) : 执行合并分片结果逻辑, 并将合并结果返回给 MySQL Client 需要子类进行实现
通过 running 标记保证同一条 SQL 同时只有一个线程正在执行, 并且不需要等到每个分片结果都返回就可以执行聚合逻辑当然, 排序逻辑需要等到所有分片结果都返回才可以执行 **
核心代码:
- AbstractDataNodeMerge.java
- DataNodeMergeManager.java#run(...)
- 3.2 DataNodeMergeManager
- AbstractDataNodeMerge
有两种子类实现:
DataMergeService : 基于堆内内存合并分片结果
DataNodeMergeManager
: 基于堆外内存合并分片结果
目前官方默认配置使用
DataNodeMergeManager
主要有如下优点:
可以使用更大的内存空间当并发量大或者数据量大时, 更大的内存空间意味着更好的性能
减少 GC 暂停时间记录行 (row) 对象小且重用性很低, 需要能够进行类似 C / C++ 的自主内存释放
更快的内存复制和读取速度, 对排序和聚合带来很好的提速
- globalSorter :
- UnsafeExternalRowSorter
- globalMergeResult :
- UnsafeExternalRowSorter
- UnsafeRow.java
- BufferHolder.java
- UnsafeRowWriter.java
- 3.4 UnsafeExternalRowSorter
- Collections.sort(students, new Comparator() {
- @Override
- public int compare(Student o1, Student o2) {
- int cmp = compare(o2.age, o1.age);
- return cmp != 0 ? cmp : compare(o1.nickname, o2.nickname);
- }
- }
- });
- UnsafeExternalRowSorter.java
- UnsafeExternalRowSorter.java
- TimSort.java
- 3.5 UnsafeRowGrouper
- Map> map = new HashMap<>();
- // 聚合
- for (student : students) {
- if (map.contains(student.nickname)) {
- map.put(student.nickname, map.get(student.nickname).get(1) + 1);
- } else {
- List
来源: http://www.suo.im/hLRpk