1. 程式人生 > >Dubbo學習系列之十三(Mycat資料庫代理)

Dubbo學習系列之十三(Mycat資料庫代理)

軟體界有隻貓,不用我說,各位看官肯定知道是哪隻,那就是大名鼎鼎的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.可以看到目錄結構如下,兩貓確實略像:

  • bin—Mycat的各種管理命令;
  • catlet—擴充套件功能;
  • conf—配置資訊,這個也是本期重點使用的;
  • lib—jar包庫,因為Mycat是Java開發的;
  • logs—日誌檔案;

5.Mycat融合應用的架構,即本次目標架構:

如果需要做擴充套件高可用,即可變成這樣的:

就是這麼簡單!

 

6.其實Mycat從應用上講,就是做配置,原始碼可按喜好研究,據說很複雜!

主要是三個檔案核心檔案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*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>-->
12     <!--必須帶有MYCATSEQ_或者 mycatseq_進入序列匹配流程 注意MYCATSEQ_有空格的情況-->
13     <property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*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*next\s+value\s+for\s*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錯, 

 

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啟動成功:

如果啟動有問題,使用以下命令檢視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連線:  

為了集中測試程式碼,我只改寫了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來一個!

DB情況,請看數量和ID分佈,紅色數字是IP: 

這裡只測試了兩主一從和一種分片規則,其他請君自測!

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框架的使用,需持謹慎態度,至少目前來看如此。


推薦閱讀:

  • Dubbo學習系列之十二(Quartz任務排程)

  • Linux下Mysql叢集使用

  • Dubbo學習系列之十一(Dashboard+Nacos規則推送)

  • Dubbo學習系列之十(Sentinel之限流與降級)

  • Dubbo學習系列之九(Shiro+JWT許可權管理)

  •