!!!分片 – 是将数据分散到不同的数据库中。
!!!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 实际上我更本不想办。从默认的配置文件中可以看出,全局表需要写入同一个实例的每个库中,那是否意味着它不支持同实例的跨库关联???
参考文献