绝对于单例数据库的查问操作,分布式数据查问会有很多技术难题。
本文记录 Mysql 分库分表 和 Elasticsearch Join 查问的实现思路,理解分布式场景数据处理的设计方案。
文章从罕用的关系型数据库 MySQL 的分库分表 Join 剖析,再到非关系型 ElasticSearch 来剖析 Join 实现策略。逐渐深刻 Join 的实现机制。
①Mysql 分库分表 Join 查问场景
分库分表场景下,查问语句如何散发,数据如何组织。相较于 NoSQL 数据库,Mysql 在 SQL 标准的范畴内,绝对比拟容易适配分布式场景。
基于 sharding-jdbc 中间件的计划,理解整个设计思路。
sharding-jdbc
- sharding-jdbc 代理了原始的 datasource, 实现 jdbc 标准来实现分库分表的散发和组装,应用层无感知。
- 执行流程:SQL 解析 => 执行器优化 => SQL 路由 =\> SQL 改写 => SQL 执行 => 后果归并
io.shardingsphere.core.executor.ExecutorEngine#execute
- Join 语句的解析,决定了要散发 SQL 到哪些实例节点上。对应 SQL 路由。
- SQL 改写就是要把原始(逻辑)表名,改为理论分片的表名。
- 简单状况下,Join 查问散发的最多执行的次数 = 数据库实例 × 表 A 分片数 × 表 B 分片数
Code Insight
示例代码工程:git@github.com:cluoHeadon/sharding-jdbc-demo.git
/**
* 执行查问 SQL 切入点,从这里能够残缺 debug 执行流程
* @see ShardingPreparedStatement#execute()
* @see ParsingSQLRouter#route(String, List, SQLStatement) Join 查问理论波及哪些表,就是在路由规定里匹配得进去的。*/
public boolean execute() throws SQLException {
try {
// 依据参数(决定分片)和具体的 SQL 来匹配相干的理论 Table。Collection<PreparedStatementUnit> preparedStatementUnits = route();
// 应用线程池,散发执行和后果归并。return new PreparedStatementExecutor(getConnection().getShardingContext().getExecutorEngine(), routeResult.getSqlStatement().getType(), preparedStatementUnits).execute();} finally {JDBCShardingRefreshHandler.build(routeResult, connection).execute();
clearBatch();}
}
SQL 路由策略
启用 sql 打印,直观看到理论散发执行的 SQL
# 打印的代码,就是在上述 route 得出 ExecutionUnits 后,打印的
sharding.jdbc.config.sharding.props.sql.show=true
sharding-jdbc 依据不同的 SQL 语句,会有不同的路由策略。咱们关注的 Join 查问,理论相干就是以下两种策略。
- StandardRoutingEngine binding-tables 模式
- ComplexRoutingEngine 最简单的状况,笛卡尔组合关联关系。
-- 参数不明,不能定位分片的状况
select * from order o inner join order_item oi on o.order_id = oi.order_id
-- 路由后果
-- Actual SQL: db1 ::: select * from order_1 o inner join order_item_1 oi on o.order_id = oi.order_id
-- Actual SQL: db1 ::: select * from order_1 o inner join order_item_0 oi on o.order_id = oi.order_id
-- Actual SQL: db1 ::: select * from order_0 o inner join order_item_1 oi on o.order_id = oi.order_id
-- Actual SQL: db1 ::: select * from order_0 o inner join order_item_0 oi on o.order_id = oi.order_id
-- Actual SQL: db0 ::: select * from order_1 o inner join order_item_1 oi on o.order_id = oi.order_id
-- Actual SQL: db0 ::: select * from order_1 o inner join order_item_0 oi on o.order_id = oi.order_id
-- Actual SQL: db0 ::: select * from order_0 o inner join order_item_1 oi on o.order_id = oi.order_id
-- Actual SQL: db0 ::: select * from order_0 o inner join order_item_0 oi on o.order_id = oi.order_id
②Elasticsearch Join 查问场景
首先,对于 NoSQL 数据库,要求 Join 查问,能够思考是不是应用场景和用法有问题。
而后,不可避免的,有些场景须要这个性能。Join 查问的实现更贴近 SQL 引擎。
基于 elasticsearch-sql 组件的计划,理解大略的实现思路。
elasticsearch-sql
- 这是个 elasticsearch 插件,通过提供 http 服务实现类 SQL 查问的性能,高版本的 elasticsearch 曾经具备该性能⭐
- 因为 elasticsearch 没有 Join 查问的个性,所以实现 SQL Join 性能,须要提供更加底层的性能,波及到 Join 算法。
Code Insight
源码地址:git@github.com:NLPchina/elasticsearch-sql.git
/**
* Execute the ActionRequest and returns the REST response using the channel.
* @see ElasticDefaultRestExecutor#execute
* @see ESJoinQueryActionFactory#createJoinAction Join 算法抉择
*/
@Override
public void execute(Client client, Map<String, String> params, QueryAction queryAction, RestChannel channel) throws Exception{
// sql parse
SqlElasticRequestBuilder requestBuilder = queryAction.explain();
// join 查问
if(requestBuilder instanceof JoinRequestBuilder){
// join 算法抉择。包含:HashJoinElasticExecutor、NestedLoopsElasticExecutor
// 如果关联条件为等值(Condition.OPEAR.EQ), 则应用 HashJoinElasticExecutor
ElasticJoinExecutor executor = ElasticJoinExecutor.createJoinExecutor(client,requestBuilder);
executor.run();
executor.sendResponse(channel);
}
// 其余类型查问 ...
}
③More Than Join
Join 算法
- 罕用三种 Join 算法:Nested Loop Join,Hash Join、Merge Join
- MySQL 只反对 NLJ 或其变种,8.0.18 版本后反对 Hash Join
- NLJ 相当于两个嵌套循环,用第一张表做 Outter Loop,第二张表做 Inner Loop,Outter Loop 的每一条记录跟 Inner Loop 的记录作比拟,最终符合条件的就将该数据记录。
- Hash Join 分为两个阶段;
build
构建阶段和probe
探测阶段。 - 能够应用 Explain 查看 MySQL 应用哪种 Join 算法。须要的语法关键字:FORMAT=JSON or FORMAT=Tree
EXPLAIN FORMAT=JSON
SELECT * FROM
sale_line_info u
JOIN sale_line_manager o ON u.sale_line_code = o.sale_line_code;
{
"query_block": {
"select_id": 1,
// 应用的 join 算法:nested_loop
"nested_loop": [
// 波及 join 的表以及对应的 key, 其余的信息与罕用 explain 相似
{
"table": {
"table_name": "o",
"access_type": "ALL"
}
},
{
"table": {
"table_name": "u",
"access_type": "ref"
}
}
]
}
}
Elasticsearch Nested 类型
剖析 Elasticsearch 业务数据以及应用场景,还有一种抉择是间接存储关联信息的文档。在 Elasticsearch 中,是以残缺文档模式提供查问和检索,彻底避开应用 Join 相干的技术。
这样就牵扯到关联是归属类型的数据还是专用类型的数据、关联数据量的大小、关联数据的更新频率等。这些都是应用 Nested 类型须要思考的因素。
更多的应用办法,能够从网上和官网找到,不做赘述。
咱们当初有个业务性能正好应用到 Nested 类型,在查问和优化过程中,解决了十分大的难题。
总结
通过运行原理剖析,对于运行流程有了清晰和深刻的认知。
对于中间件的优化和技术选型更加有目的性,应用上会更加审慎和小心。
明确的筛选条件,更小的筛选范畴,limit 取值数据,都能够缩小计算陈本,进步性能。
参考
- 如何在分布式数据库中实现 Hash Join
- 一文详解 MySQL——Join 的应用优化 – 掘金
作者:京东物流 杨攀
起源:京东云开发者社区