「MyCat」- Join

  CREATED BY JENKINSBOT

!!!分片 – 是将数据分散到不同的数据库中。

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

参考文献

MyCat权威指南