!!!分片 – 是将数据分散到不同的数据库中。
!!!Join绝对是关系型数据库中最常用一个特性,然而在分布式环境中,「跨分片的JOIN」确是最复杂的,最难解决一个问题。
!!!说白了,跨分片就是跨库。
各种「跨分片JOIN」实现
Mycat 目前版本支持跨分片的 join,主要实现的方式有四种:
- 全局表
- ER 分片
- catletT(人工智能)
- ShareJoin,ShareJoin 在开发版中支持
前面三种方式 1.3.0.1 支持。
全局表(避开跨分片问题)
!!!每个库里都放入该表,解决跨分片的问题。注意,是每个库里都放这张表。
就是那种“字典表”,表中的数据都是“分类”类型的数据。“字典表”具有以下特点:
- 变动不频繁
- 数据量总体变化不大
- 数据规模不大,很少有超过数十万条记录。
鉴于此,MyCAT 定义了一种特殊的表,称之为“全局表”。“全局表”具有以下特性:
- 全局表存在于所有的节点上;
- 插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性;
- 当单独在全局表上执行查询操作时,只会从一个节点获取数据;
- 全局表可以跟任何一个表进行JOIN操作;
可以将“字典表”或者符合字典表特性的一些表定义为全局表。
ER JOIN
!!!将相关的数据放在一起,解决跨分片的问题。
需要在XML中描述表的关联关系,如下:
<table name="customer" dataNode="dn1,dn2" rule="sharding-by-intfile"> <childTable name="orders" joinKey="customer_id" parentKey="id"/> </table>
然后MyCat基于此将相关的数据存储到相同节点上。规避关联数据时的跨节点问题。
SHARE JOIN
!!!通过单独查询,解决跨库问题。
目前支持 2 个表的 join,原理就是解析 SQL 语句,拆分成单表的 SQL 语句执行,然后把各个节点的数据汇集。
CATLET(人工智能)
!!!也是单独查询,通过编程解决更加复杂的问题。
解决跨分片的 SQL JOIN 的问题,远比想象的复杂,而且往往无法实现高效的处理。
既然如此,就依靠人工的智力,去编程解决业务系统中特定几个必须跨分片的 SQL 的 JOIN 逻辑。
MyCAT 提供特定的 API 供程序员调用,这就是 MyCAT 创新性的思路 – 人工智能。
总结
说白了,MyCat压根就做跨库的关联,所有的关联都是在同一个库中进行的。
要么规避(Share Join,CATLET),要么放在一起(ER JOIN,全局表)。
有些东西,从默认的配置文件中就可以窥见:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<!-- auto sharding by id (long) -->
<table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long"/>
<!-- global table is auto cloned to all defined data nodes ,so can join
with any table whose sharding node is in the same data node -->
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3"/>
<table name="goods" primaryKey="ID" type="global" dataNode="dn1,dn2"/>
<!-- random sharding using mod sharind rule -->
<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="mod-long"/>
<!-- <table name="dual" primaryKey="ID" dataNode="dnx,dnoracle2" type="global"
needAddLimit="false"/> <table name="worker" primaryKey="ID" dataNode="jdbc_dn1,jdbc_dn2,jdbc_dn3"
rule="mod-long" /> -->
<table name="employee" primaryKey="ID" dataNode="dn1,dn2" rule="sharding-by-intfile"/>
<table name="customer" primaryKey="ID" dataNode="dn1,dn2" rule="sharding-by-intfile">
<childTable name="orders" primaryKey="ID" joinKey="customer_id" parentKey="id">
<childTable name="order_items" joinKey="order_id" parentKey="id"/>
</childTable>
<childTable name="customer_addr" primaryKey="ID" joinKey="customer_id" parentKey="id"/>
</table>
<!-- <table name="oc_call" primaryKey="ID" dataNode="dn1$0-743" rule="latest-month-calldate"
/> -->
</schema>
<!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"
/> -->
<dataNode name="dn1" dataHost="localhost1" database="db1"/>
<dataNode name="dn2" dataHost="localhost1" database="db2"/>
<dataNode name="dn3" dataHost="localhost1" database="db3"/>
<!--<dataNode name="dn4" dataHost="sequoiadb1" database="SAMPLE" />
<dataNode name="jdbc_dn1" dataHost="jdbchost" database="db1" />
<dataNode name="jdbc_dn2" dataHost="jdbchost" database="db2" />
<dataNode name="jdbc_dn3" dataHost="jdbchost" database="db3" /> -->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hostM1" url="localhost:3306" user="root" password="123456">
<!-- can have multi read hosts -->
<readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx"/>
</writeHost>
<writeHost host="hostS1" url="localhost:3316" user="root" password="123456"/>
<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>
<!--
<dataHost name="sequoiadb1" maxCon="1000" minCon="1" balance="0" dbType="sequoiadb" dbDriver="jdbc">
<heartbeat> </heartbeat>
<writeHost host="hostM1" url="sequoiadb://1426587161.dbaas.sequoialab.net:11920/SAMPLE" user="jifeng" password="jifeng"></writeHost>
</dataHost>
<dataHost name="oracle1" maxCon="1000" minCon="1" balance="0" writeType="0" dbType="oracle" dbDriver="jdbc"> <heartbeat>select 1 from dual</heartbeat>
<connectionInitSql>alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'</connectionInitSql>
<writeHost host="hostM1" url="jdbc:oracle:thin:@127.0.0.1:1521:nange" user="base" password="123456" > </writeHost> </dataHost>
<dataHost name="jdbchost" maxCon="1000" minCon="1" balance="0" writeType="0" dbType="mongodb" dbDriver="jdbc">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM" url="mongodb://192.168.0.99/test" user="admin" password="123456" ></writeHost> </dataHost>
<dataHost name="sparksql" maxCon="1000" minCon="1" balance="0" dbType="spark" dbDriver="jdbc">
<heartbeat> </heartbeat>
<writeHost host="hostM1" url="jdbc:hive2://feng01:10000" user="jifeng" password="jifeng"></writeHost> </dataHost> -->
<!-- <dataHost name="jdbchost" maxCon="1000" minCon="10" balance="0" dbType="mysql"
dbDriver="jdbc"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1"
url="jdbc:mysql://localhost:3306" user="root" password="123456"> </writeHost>
</dataHost> -->
</mycat:schema>
-- 写给自己的: -- 所以为什么这个SQL执行不了??? SELECT * FROM post INNER JOIN person_label ON person_label.id = post.id INNER JOIN person_info ON person.user_id = post.id; -- DB1: post -- DB2: person_label, person_info -- (1)首先他们之间没有全局表的概念,全局表是每个分片都存在的,就是说每个库里都要存在。 -- 它们之间最多就是person_label, person_info互为全局表。 -- (2)它们之间已经跨库了,并且三张表的JOIN操作,它完成不了。
待办事项
# NOTTODO 实际上我更本不想办。从默认的配置文件中可以看出,全局表需要写入同一个实例的每个库中,那是否意味着它不支持同实例的跨库关联???
参考文献