­

Dubbo学习系列之十三(Mycat数据库代理)

  • 2019 年 10 月 3 日
  • 筆記

软件界有只猫,不用我说,各位看官肯定知道是哪只,那就是大名鼎鼎的Tomcat,现在又来了一只猫,据说是位东方萌妹子,暂且认作Tom猫的表妹,本来叫OpencloudDB,后又改名为Mycat,或许Cat更亲切?那现在就来认识下这只小猫吧。

数据库的核心地位就不说了,但现在的问题是,各种RDB,各种NoSQL交织,又是分布式、多租户的场景下,心里有没有十足的把握能稳住如此局面呢。有需求,就有市场!自然,相应的技术也应运而生,Mycat作为一款DB中间件,可以作为应用和DB间的“桥梁”,让后台DB的复杂组成对应用透明,处理分库分表、多租户架构和大数据实时查询等都不在话下!

工具:

Idea201902/JDK11/ZK3.5.5/Gradle5.4.1/RabbitMQ3.7.13/Lombok0.26/Erlang21.2/RocketMQ4.5.2/Sentinel1.6.3/SpringBoot2.1.6/RHEL7.6/VMware15.0.4/Mysql8.0.17/Mycat1.6.7.3/MysqlWorkbench6.3

难度: 新手–战士–老兵–大师

目标:

1.Linux下使用Mycat连接Mysql集群(两主一从),读写分离式应用


步骤:

1.建立Mysql集群,步骤参考往期文章(Linux下Mysql集群使用)。

2.下载Mycat,放到Linux中/usr/mycat下,记得先建立此目录。

3.进入该目录,解压:

[root@localhost mycat]# tar -zxvf Mycat-server-1.6.7.3-release-20190828135747-linux.tar.gz

4.可以看到目录结构如下,两猫确实略像:

1201

  • bin—Mycat的各种管理命令;
  • catlet—扩展功能;
  • conf—配置信息,这个也是本期重点使用的;
  • lib—jar包库,因为Mycat是Java开发的;
  • logs—日志文件;

5.Mycat融合应用的架构,即本次目标架构:

1204

如果需要做扩展高可用,即可变成这样的:

1203

就是这么简单!

 

6.其实Mycat从应用上讲,就是做配置,源码可按喜好研究,据说很复杂!

1202

主要是三个文件核心文件rule.xml、schema.xml、server.xml的配置:

  • server.xml:Mycat的配置文件,可以将Mycat视为DBServer的代理,
  • schema.xml:逻辑表与物理DB/分片分库的映射配置,
  • rule.xml:分库分表规则,

 

7.挨个看看长啥样,参数的含义注释上基本有说明,这里都是全局配置参数:

  • <system>中设置Mycat全局属性;

  • <firewall>设置黑白名单;

  • <user>设置用户登录Mycat的账号信息;

  • <privileges>单独设置表的DML权限;

server.xml原版样例:

 1 <?xml version="1.0" encoding="UTF-8"?>   2 <!DOCTYPE mycat:server SYSTEM "server.dtd">   3 <mycat:server xmlns:mycat="http://io.mycat/">   4   <system>   5     <property name="nonePasswordLogin">0</property> <!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户-->   6     <property name="useHandshakeV10">1</property>   7     <property name="useSqlStat">0</property>  <!-- 1为开启实时统计、0为关闭 -->   8     <property name="useGlobleTableCheck">0</property>  <!-- 1为开启全加班一致性检测、0为关闭 -->   9     <property name="sqlExecuteTimeout">300</property>  <!-- SQL 执行超时 单位:秒-->  10     <property name="sequnceHandlerType">2</property>  11     <!--<property name="sequnceHandlerPattern">(?:(s*nexts+values+fors*MYCATSEQ_(w+))(,|)|s)*)+</property>-->  12     <!--必须带有MYCATSEQ_或者 mycatseq_进入序列匹配流程 注意MYCATSEQ_有空格的情况-->  13     <property name="sequnceHandlerPattern">(?:(s*nexts+values+fors*MYCATSEQ_(w+))(,|)|s)*)+</property>  14     <property name="subqueryRelationshipCheck">false</property> <!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false -->  15       <!--  <property name="useCompression">1</property>--> <!--1为开启mysql压缩协议-->  16         <!--  <property name="fakeMySQLVersion">5.6.20</property>--> <!--设置模拟的MySQL版本号-->  17   <!-- <property name="processorBufferChunk">40960</property> -->  18   <!--  19   <property name="processors">1</property>  20   <property name="processorExecutor">32</property>  21    -->  22         <!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->  23     <property name="processorBufferPoolType">0</property>  24     <!--默认是65535 64K 用于sql解析时最大文本长度 -->  25     <!--<property name="maxStringLiteralLength">65535</property>-->  26     <!--<property name="sequnceHandlerType">0</property>-->  27     <!--<property name="backSocketNoDelay">1</property>-->  28     <!--<property name="frontSocketNoDelay">1</property>-->  29     <!--<property name="processorExecutor">16</property>-->  30     <!--  31       <property name="serverPort">8066</property> <property name="managerPort">9066</property>  32       <property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property>  33       <property name="dataNodeIdleCheckPeriod">300000</property> 5 * 60 * 1000L; //连接空闲检查  34       <property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->  35     <!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->  36     <property name="handleDistributedTransactions">0</property>  37       <!--off heap for merge/order/group/limit      1开启   0关闭-->  38     <property name="useOffHeapForMerge">0</property>  39     <!--单位为m-->  40     <property name="memoryPageSize">64k</property>  41     <!--单位为k-->  42     <property name="spillsFileBufferSize">1k</property>  43     <property name="useStreamOutput">0</property>  44     <!--单位为m-->  45     <property name="systemReserveMemorySize">384m</property>  46     <!--是否采用zookeeper协调切换  -->  47     <property name="useZKSwitch">false</property>  48     <!-- XA Recovery Log日志路径 -->  49     <!--<property name="XARecoveryLogBaseDir">./</property>-->  50     <!-- XA Recovery Log日志名称 -->  51     <!--<property name="XARecoveryLogBaseName">tmlog</property>-->  52     <!--如果为 true的话 严格遵守隔离级别,不会在仅仅只有select语句的时候在事务中切换连接-->  53     <property name="strictTxIsolation">false</property>  54     <property name="useZKSwitch">true</property>  55   </system>  56  57   <!-- 全局SQL防火墙设置 -->  58   <!--白名单可以使用通配符%或着*-->  59   <!--例如<host host="127.0.0.*" user="root"/>-->  60   <!--例如<host host="127.0.*" user="root"/>-->  61   <!--例如<host host="127.*" user="root"/>-->  62   <!--例如<host host="1*7.*" user="root"/>-->  63   <!--这些配置情况下对于127.0.0.1都能以root账户登录-->  64   <!--  65   <firewall>  66      <whitehost>  67         <host host="1*7.0.0.*" user="root"/>  68      </whitehost>  69        <blacklist check="false">  70        </blacklist>  71   </firewall>  72   -->  73   <user name="root" defaultAccount="true">  74     <property name="password">123456</property>  75     <property name="schemas">TESTDB</property>  76     <!-- 表级 DML 权限设置 -->  77     <!--  78     <privileges check="false">  79       <schema name="TESTDB" dml="0110" >  80         <table name="tb01" dml="0000"></table>  81         <table name="tb02" dml="1111"></table>  82       </schema>  83     </privileges>  84      -->  85   </user>  86   <user name="user">  87     <property name="password">user</property>  88     <property name="schemas">TESTDB</property>  89     <property name="readOnly">true</property>  90   </user>  91 </mycat:server>

 

8.schema.xml,配置schema下各个table的分片/分库,以及物理DB:

  • <schema>+<table>租户和子表配置,

  • <dataNode>分片,

  • <dataHost>物理DB,

原版样例:

 1 <?xml version="1.0"?>   2 <!DOCTYPE mycat:schema SYSTEM "schema.dtd">   3 <mycat:schema xmlns:mycat="http://io.mycat/">   4   <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100">   5     <!-- auto sharding by id (long) -->   6     <table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />   7     <!-- global table is auto cloned to all defined data nodes ,so can join   8       with any table whose sharding node is in the same data node -->   9     <table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />  10     <table name="goods" primaryKey="ID" type="global" dataNode="dn1,dn2" />  11     <!-- random sharding using mod sharind rule -->  12     <table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3"  13          rule="mod-long" />  14     <!-- <table name="dual" primaryKey="ID" dataNode="dnx,dnoracle2" type="global"  15       needAddLimit="false"/> <table name="worker" primaryKey="ID" dataNode="jdbc_dn1,jdbc_dn2,jdbc_dn3"  16       rule="mod-long" /> -->  17     <table name="employee" primaryKey="ID" dataNode="dn1,dn2"  18          rule="sharding-by-intfile" />  19     <table name="customer" primaryKey="ID" dataNode="dn1,dn2"  20          rule="sharding-by-intfile">  21       <childTable name="orders" primaryKey="ID" joinKey="customer_id"  22             parentKey="id">  23         <childTable name="order_items" joinKey="order_id"  24               parentKey="id" />  25       </childTable>  26       <childTable name="customer_addr" primaryKey="ID" joinKey="customer_id"  27             parentKey="id" />  28     </table>  29     <!-- <table name="oc_call" primaryKey="ID" dataNode="dn1$0-743" rule="latest-month-calldate"  30       /> -->  31   </schema>  32   <!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"  33     /> -->  34   <dataNode name="dn1" dataHost="localhost1" database="db1" />  35   <dataNode name="dn2" dataHost="localhost1" database="db2" />  36   <dataNode name="dn3" dataHost="localhost1" database="db3" />  37   <!--<dataNode name="dn4" dataHost="sequoiadb1" database="SAMPLE" />  38    <dataNode name="jdbc_dn1" dataHost="jdbchost" database="db1" />  39   <dataNode  name="jdbc_dn2" dataHost="jdbchost" database="db2" />  40   <dataNode name="jdbc_dn3"   dataHost="jdbchost" database="db3" /> -->  41   <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"  42         writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">  43     <heartbeat>select user()</heartbeat>  44     <!-- can have multi write hosts -->  45     <writeHost host="hostM1" url="localhost:3306" user="root"  46            password="123456">  47       <!-- can have multi read hosts -->  48       <readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" />  49     </writeHost>  50     <writeHost host="hostS1" url="localhost:3316" user="root"  51            password="123456" />  52     <!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->  53   </dataHost>  54   <!--  55     <dataHost name="sequoiadb1" maxCon="1000" minCon="1" balance="0" dbType="sequoiadb" dbDriver="jdbc">  56     <heartbeat>     </heartbeat>  57      <writeHost host="hostM1" url="sequoiadb://1426587161.dbaas.sequoialab.net:11920/SAMPLE" user="jifeng"   password="jifeng"></writeHost>  58      </dataHost>  59 ​  60     <dataHost name="oracle1" maxCon="1000" minCon="1" balance="0" writeType="0"   dbType="oracle" dbDriver="jdbc"> <heartbeat>select 1 from dual</heartbeat>  61     <connectionInitSql>alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'</connectionInitSql>  62     <writeHost host="hostM1" url="jdbc:oracle:thin:@127.0.0.1:1521:nange" user="base"   password="123456" > </writeHost> </dataHost>  63 ​  64     <dataHost name="jdbchost" maxCon="1000"   minCon="1" balance="0" writeType="0" dbType="mongodb" dbDriver="jdbc">  65     <heartbeat>select   user()</heartbeat>  66     <writeHost host="hostM" url="mongodb://192.168.0.99/test" user="admin" password="123456" ></writeHost> </dataHost>  67 ​  68     <dataHost name="sparksql" maxCon="1000" minCon="1" balance="0" dbType="spark" dbDriver="jdbc">  69     <heartbeat> </heartbeat>  70      <writeHost host="hostM1" url="jdbc:hive2://feng01:10000" user="jifeng"   password="jifeng"></writeHost> </dataHost> -->  71 ​  72   <!-- <dataHost name="jdbchost" maxCon="1000" minCon="10" balance="0" dbType="mysql"  73     dbDriver="jdbc"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1"  74     url="jdbc:mysql://localhost:3306" user="root" password="123456"> </writeHost>  75     </dataHost> -->  76 </mycat:schema>

 

9.rule.xml详细描述表的分片规则,格式如下:

1   <tableRule name="分片规则名">  2         <rule>  3               <columns>分片的列</columns>  4               <algorithm>分片算法名</algorithm>  5         </rule>  6   </tableRule>  7   <function name="分片算法名" class="算法实现类">  8         <property name="算法参数">参数值</property>  9   </function>

 

原版样例:

  1 <?xml version="1.0" encoding="UTF-8"?>    2 <!DOCTYPE mycat:rule SYSTEM "rule.dtd">    3 <mycat:rule xmlns:mycat="http://io.mycat/">    4   <tableRule name="rule1">    5     <rule>    6       <columns>id</columns>    7       <algorithm>func1</algorithm>    8     </rule>    9   </tableRule>   10   <tableRule name="rule2">   11     <rule>   12       <columns>user_id</columns>   13       <algorithm>func1</algorithm>   14     </rule>   15   </tableRule>   16   <tableRule name="sharding-by-intfile">   17     <rule>   18       <columns>sharding_id</columns>   19       <algorithm>hash-int</algorithm>   20     </rule>   21   </tableRule>   22   <tableRule name="auto-sharding-long">   23     <rule>   24       <columns>id</columns>   25       <algorithm>rang-long</algorithm>   26     </rule>   27   </tableRule>   28   <tableRule name="mod-long">   29     <rule>   30       <columns>id</columns>   31       <algorithm>mod-long</algorithm>   32     </rule>   33   </tableRule>   34   <tableRule name="sharding-by-murmur">   35     <rule>   36       <columns>id</columns>   37       <algorithm>murmur</algorithm>   38     </rule>   39   </tableRule>   40   <tableRule name="crc32slot">   41     <rule>   42       <columns>id</columns>   43       <algorithm>crc32slot</algorithm>   44     </rule>   45   </tableRule>   46   <tableRule name="sharding-by-month">   47     <rule>   48       <columns>create_time</columns>   49       <algorithm>partbymonth</algorithm>   50     </rule>   51   </tableRule>   52   <tableRule name="latest-month-calldate">   53     <rule>   54       <columns>calldate</columns>   55       <algorithm>latestMonth</algorithm>   56     </rule>   57   </tableRule>   58   <tableRule name="auto-sharding-rang-mod">   59     <rule>   60       <columns>id</columns>   61       <algorithm>rang-mod</algorithm>   62     </rule>   63   </tableRule>   64   <tableRule name="jch">   65     <rule>   66       <columns>id</columns>   67       <algorithm>jump-consistent-hash</algorithm>   68     </rule>   69   </tableRule>   70   <function name="murmur"   71     class="io.mycat.route.function.PartitionByMurmurHash">   72     <property name="seed">0</property><!-- 默认是0 -->   73     <property name="count">2</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->   74     <property name="virtualBucketTimes">160</property><!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍 -->   75     <!-- <property name="weightMapFile">weightMapFile</property> 节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1代替 -->   76     <!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property>   77       用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur hash值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西 -->   78   </function>   79   <function name="crc32slot"   80         class="io.mycat.route.function.PartitionByCRC32PreSlot">   81   </function>   82   <function name="hash-int"   83     class="io.mycat.route.function.PartitionByFileMap">   84     <property name="mapFile">partition-hash-int.txt</property>   85   </function>   86   <function name="rang-long"   87     class="io.mycat.route.function.AutoPartitionByLong">   88     <property name="mapFile">autopartition-long.txt</property>   89   </function>   90   <function name="mod-long" class="io.mycat.route.function.PartitionByMod">   91     <!-- how many data nodes -->   92     <property name="count">3</property>   93   </function>   94 ​   95   <function name="func1" class="io.mycat.route.function.PartitionByLong">   96     <property name="partitionCount">8</property>   97     <property name="partitionLength">128</property>   98   </function>   99   <function name="latestMonth"  100     class="io.mycat.route.function.LatestMonthPartion">  101     <property name="splitOneDay">24</property>  102   </function>  103   <function name="partbymonth"  104     class="io.mycat.route.function.PartitionByMonth">  105     <property name="dateFormat">yyyy-MM-dd</property>  106     <property name="sBeginDate">2015-01-01</property>  107   </function>  108   <function name="rang-mod" class="io.mycat.route.function.PartitionByRangeMod">  109     <property name="mapFile">partition-range-mod.txt</property>  110   </function>  111   <function name="jump-consistent-hash" class="io.mycat.route.function.PartitionByJumpConsistentHash">  112     <property name="totalBuckets">3</property>  113   </function>  114 </mycat:rule>

 

部分常用的分片规则算法说明:

  • PartitionByMurmurHash(一致性hash):将物理节点虚拟并映射为一个“一致性hash环”;

  • PartitionByCRC32PreSlot(crc32slot 算法):crc32(key)%102400=slot,slot 按照范围均匀分布在 dataNode 上;

  • LatestMonthPartion(单月小时拆分):单月内按照小时拆分,最小粒度是小时,可以一天最多 24 个分片,最少 1 个分片,一个月完后下月 从头开始循环;

  • PartitionByMonth(自然月):按自然月分片;

  • PartitionByRangeMod(范围求模):先进行范围分片计算出分片组,组内再求模;

  • PartitionByJumpConsistentHash(一致性hash):另一种一致性hash算法;

  • PartitionByFileMap(枚举):通过在配置文件中配置可能的枚举 id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存,而全国省份区县是固定的;

  • PartitionByLong(固定分片 hash 算法):取 id 的二进制低 10 位取模运算,即( id 二进制) &1111111111,partitionCount分片个数,partitionLength分片长度,默认这两个参数的向量积为1024;

  • AutoPartitionByLong(范围约定):按照提前规划好分片字段范围计算属于哪个分片,start <= range <= end;

  • PartitionByMod(求模):即根据 id 进行十进制求模预算,相比固定分片 hash,此种在批量插入时可能存在批量插入单事务插入多数据分片,增大事务一致性难度;

  • PartitionByDate(按天分片):即根据指定的格式,起止日期,按日期划分,如果配置了 sEndDate 则代表数据达到了这个日期的分片后后循环从开始分片插入;

10.情况一:如果DB是一主一从:需注意这里的主从复制由Mysql实现,Mycat不负责数据复制功能。只需配置server.xml和schema.xml即可: 本次server.xml实例:

 1 <?xml version="1.0" encoding="UTF-8"?>   2 <!DOCTYPE mycat:server SYSTEM "server.dtd">   3 <mycat:server xmlns:mycat="http://io.mycat/">   4   <system>   5     <property name="nonePasswordLogin">0</property>   6     <property name="useHandshakeV10">1</property>   7     <property name="useSqlStat">0</property>   8     <property name="useGlobleTableCheck">0</property>   9     <property name="sqlExecuteTimeout">300</property>  10     <property name="sequnceHandlerType">2</property>  11     <property name="sequnceHandlerPattern">(?:(s*nexts+values+fors*MYCATSEQ_(w+))(,|)|s)*)+</property>  12     <property name="subqueryRelationshipCheck">false</property>  13     <property name="processorBufferPoolType">0</property>  14     <property name="handleDistributedTransactions">0</property>  15     <property name="useOffHeapForMerge">0</property>  16     <property name="memoryPageSize">64k</property>  17     <property name="spillsFileBufferSize">1k</property>  18     <property name="useStreamOutput">0</property>  19     <property name="systemReserveMemorySize">384m</property>  20     <property name="useZKSwitch">false</property>  21     <property name="strictTxIsolation">false</property>  22     <property name="useZKSwitch">true</property>  23   </system>  24   <user name="mycat" defaultAccount="true">  25     <property name="password">12345678</property>  26     <property name="schemas">dubbo_db</property>  27   </user>  28 </mycat:server>

 

schema.xml示例:

 1 <?xml version="1.0"?>   2 <!DOCTYPE mycat:schema SYSTEM "schema.dtd">   3 <!-- 数据库配置,与server.xml中的数据库对应 -->   4 <mycat:schema xmlns:mycat="http://io.mycat/">   5     <schema name="dubbo_db" checkSQLschema="true" sqlMaxLimit="100">   6       <table name="dubbo_delivery" primaryKey="ID" dataNode="dn1"/>   7       <table name="dubbo_finance" primaryKey="ID" dataNode="dn1 "/>   8       <table name="dubbo_item" primaryKey="ID" dataNode="dn1 " />   9       <table name="dubbo_order" primaryKey="ID" dataNode="dn1"/>  10       <table name="dubbo_order_detail" primaryKey="ID" dataNode="dn1 "/>  11       <table name="dubbo_stock" primaryKey="ID" dataNode="dn1 " />  12   </schema>  13   <!-- 分片配置 -->  14   <dataNode name="dn1" dataHost="localhost1" database="dubbo_db" />  15   <!-- 物理数据库配置 -->  16   <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"  17         writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">  18       <heartbeat>select user()</heartbeat>  19       <writeHost host="hostM1" url="192.168.1.204:3306" user="root" password="abcd@1234">  20           <readHost host="hostS2" url="192.168.1.205:3306" user="root" password="abcd@1234" />  21       </writeHost>  22   </dataHost>  23 </mycat:schema>

 

11.情况二,即本期目标架构,DB是两主一从:server.xml不变, 本次schema.xml实例:

 1 <?xml version="1.0"?>   2 <!DOCTYPE mycat:schema SYSTEM "schema.dtd">   3 <!-- 数据库配置,与server.xml中的数据库对应 -->   4 <mycat:schema xmlns:mycat="http://io.mycat/">   5     <schema name="dubbo_db" checkSQLschema="true" sqlMaxLimit="100">   6       <table name="dubbo_delivery" primaryKey="ID" dataNode="dn1"/>   7       <table name="dubbo_finance" primaryKey="ID" dataNode="dn1,dn2" rule="rule1"/>   8       <table name="dubbo_item" primaryKey="ID" dataNode="dn1,dn2" rule="rule2"/>   9       <table name="dubbo_order" primaryKey="ID" dataNode="dn1,dn2" rule="sharding-by-murmur"/>  10       <table name="dubbo_order_detail" primaryKey="ID" dataNode="dn1,dn2" rule="sharding-by-month"/>  11       <table name="dubbo_stock" primaryKey="ID" dataNode="dn1" />  12     </schema>  13   <!-- 分片配置 -->  14   <dataNode name="dn1" dataHost="localhost1" database="dubbo_db" />  15   <dataNode name="dn2" dataHost="localhost2" database="dubbo_db" />  16   <!-- 物理数据库配置 -->  17   <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"  18         writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">  19       <heartbeat>select user()</heartbeat>  20       <writeHost host="hostM1" url="192.168.1.204:3306" user="root" password="abcd@1234">  21           <readHost host="hostS2" url="192.168.1.205:3306" user="root" password="abcd@1234" />  22       </writeHost>  23   </dataHost>  24   <dataHost name="localhost2" maxCon="1000" minCon="10" balance="0"  25         writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">  26       <heartbeat>select user()</heartbeat>  27       <writeHost host="hostM2" url="192.168.1.206:3306" user="root" password="abcd@1234" />  28   </dataHost>  29 </mycat:schema>

 

本次rule.xml实例:只有使用了分片模式时,才需要配置rule规则,这里写了三种rule,其实也没全部用上:

 1 <?xml version="1.0" encoding="UTF-8"?>   2 <!DOCTYPE mycat:rule SYSTEM "rule.dtd">   3 <mycat:rule xmlns:mycat="http://io.mycat/">   4 <!--规则定义-->   5   <tableRule name="sharding-by-murmur">   6     <rule>   7       <columns>id</columns>   8       <algorithm>murmur</algorithm>   9     </rule>  10   </tableRule>  11     <!--自定义规则-->  12   <tableRule name="rule1">  13     <rule>  14       <columns>userr_id</columns>  15       <algorithm>func1</algorithm>  16     </rule>  17   </tableRule>  18     <tableRule name="rule2">  19     <rule>  20       <columns>id</columns>  21       <algorithm>func2</algorithm>  22     </rule>  23   </tableRule>  24   <tableRule name="sharding-by-month">  25     <rule>  26       <columns>create_time</columns>  27       <algorithm>partbymonth</algorithm>  28     </rule>  29   </tableRule>  30 <!--规则算法实现-->  31   <function name="murmur"  32     class="io.mycat.route.function.PartitionByMurmurHash">  33     <property name="seed">0</property><!-- 默认是0 -->  34     <property name="count">2</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->  35     <property name="virtualBucketTimes">160</property><!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍 -->  36     <!-- <property name="weightMapFile">weightMapFile</property> 节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1代替 -->  37     <!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property>  38       用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur hash值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西 -->  39   </function>  40   <function name="func1" class="io.mycat.route.function.PartitionByLong">  41     <!--分片数量,partitionCount*partitionLength=1024-->  42     <property name="partitionCount">2</property>  43     <property name="partitionLength">512</property>  44   </function>  45     <function name="func2" class="io.mycat.route.function.PartitionByLong">  46     <property name="partitionCount">8</property>  47     <property name="partitionLength">128</property>  48   </function>  49   <function name="partbymonth"  50     class="io.mycat.route.function.PartitionByMonth">  51     <property name="dateFormat">yyyy-MM-dd</property>  52     <property name="sBeginDate">2015-01-01</property>  53   </function>  54 </mycat:rule>

 

12.测试:配置好mycat/conf/下的3个xml文件,即配置好了Mycat与物理DB的连接,应用端连接仅需修改连接串端口为Mycat的IP+端口,账号为server.xml中user信息,注意:要写上默认schema,否则启动应用报Mycat no chose错, 

1205

 

13.此处有坑!如果Mysql是独立安装在linux上,需要对远程访问打开,否则访问默认仅限本地,导致远程连接一直报错,以开放root用户远程连接为例:

mysql> use mysql;  mysql> update user set Host='%' where User='root';  mysql> quit;

 

再重启mysql:

[root@localhost ~]# systemctl restart mysqld

 

启动Mycat:

[root@localhost ~]# cd /usr/mycat/mycat/bin  [root@localhost bin]# ./mycat start

 

mycat启动成功:

1208

如果启动有问题,使用以下命令查看log:

[root@localhost conf]# tail -F  /usr/mycat/mycat/logs/wrapper.log  [root@localhost conf]# tail -F  /usr/mycat/mycat/logs/mycat.log

 

然后可以在window上使用如MysqlWorkbench,Navicat测试下是否连接正常,并测试下Mycat连接: 1207 

为了集中测试代码,我只改写了finance模块,写个service方法:com.biao.mall.service.DubboFinanceServiceImpl中:

 1  //插入1000条数据,看data分布   2 @Override   3  public void testMycat(){   4      DubboFinanceEntity financeEntity = new DubboFinanceEntity();   5        for (int i = 0; i < 1000; i++) {   6           financeEntity.setUserId(String.valueOf(i+100));   7           financeDao.insert(financeEntity);   8           }   9        return "testMycat successfully";  10         }

 

写个controller方法跑一跑:

@RestController  @RequestMapping("/finance")  public class DubboFinanceController {      private DubboFinanceServiceImpl  financeService;      @Autowired      public DubboFinanceController(DubboFinanceServiceImpl  financeService) {          this.financeService = financeService;      }        @RequestMapping("/mycat")      public void testMycat(){         return financeService.testMycat();      }  }

 

启动:ZK—> business –>  finance, URI来一个!

1211

DB情况,请看数量和ID分布,红色数字是IP: 1210

这里只测试了两主一从和一种分片规则,其他请君自测!

13.代码地址:其中的day16,https://github.com/xiexiaobiao/dubbo-project.git


后记

1.认识Mycat的关键特性:

  • 支持Mysql原生协议,跨语言,跨平台,跨数据库的通用中间件代理;
  • 基于心跳的自动故障切换,支持读写分离,支持MySQL主从;
  • 基于Nio实现,有效管理线程,高并发问题;
  • 支持数据的多片自动路由与聚合,支持sum,count,max等常用的聚合函数,支持跨库分页;
  • 支持通过全局表,ER关系的分片策略,实现了高效的单库多表join查询;
  • 支持多租户方案,即同DB下多schema模式;
  • 支持全局序列号,解决分布式下的主键生成问题;
  • 分片规则丰富,插件化开发,易于扩展,可自定义;
  • 集群基于ZooKeeper管理,在线升级,扩容,智能优化,大数据处理(V2.0dev);
  • 引入Mycat的无痕切换,我觉得这是最大的优势;

2.认清Mycat的局限性

  • 目前只支持跨库join2个表,不支持3 表及其以上跨库 join ;

  • Mycat并没有根据二阶段提交协议实现 XA事务,而是只保证 prepare 阶段数据一致性的弱XA事务,分布式事务场景下,强一致性无法保证;

  • 分页排序场景下,会一次查询所有分片,再集中排序分页,有性能瓶颈;

  • 不同类型DB适配一般,如Oracle/SQLServer等,由于SQL语法差异,须做彻底的语句兼容测试;

  • 没有API配置方法,只有XML方式配置,十分过时;

3.Mycat作为DB上一层的重量级中间件,统一了入口,实际上也破坏了分布式的定义,未能充分发挥DB层的效能,所以也有很多不看好的声音,DB独立使用,更能发挥灵活自由配置,直接对接应用层更为高效。

4.总结:Mycat框架的使用,需持谨慎态度,至少目前来看如此。


推荐阅读: