MyCat基本使用

一、概述

官方文檔,是中文的:http://www.mycat.io/document/mycat-definitive-guide.pdf

Mycat是一個開源數據庫中間件,是一個實現了MySQL協議的的數據庫中間件服務器,我們可以把它看作是一個數據庫代理,用MySQL客戶端工具和命令行訪問Mycat,而Mycat再使用用MySQL原生(Native)協議與多個MySQL服務器通信,也可以用JDBC協議與大多數主流數據庫服務器通信,包括SQL Server、Oracle、DB2、PostgreSQL等主流數據庫,也支持MongoDB這種新型NoSQL方式的存儲,未來還會支持更多類型的存儲;

一般地,Mycat主要用於代理MySQL數據庫,雖然它也支持去訪問其他類型的數據庫;Mycat的默認端口是8066,一般地,我們可以使用常見的對象映射框架比如MyBatis操作Mycat。

簡單來說:

  • 一個徹底開源的,面向企業應用開發的大數據庫集羣
  • 支持事務、ACID、可以替代MySQL的加強版數據庫
  • 一個可以視爲MySQL集羣的企業級數據庫,用來替代昂貴的Oracle集羣
  • 一個融合內存緩存技術、NoSQL技術、HDFS大數據的新型SQL Server
  • 結合傳統數據庫和新型分佈式數據倉庫的新一代企業級數據庫產品
  • 一個新穎的數據庫中間件產品

Mycat主要能做什麼

(1)數據庫的讀寫分離

通過Mycat可以自動實現寫數據時操作主數據庫,讀數據時操作從數據庫,這樣能有效地減輕數據庫壓力,也能減輕IO壓力。

實現讀寫分離,當主出現故障後,Mycat自動切換到另一個主上,進而提供高可用的數據庫服務,當然我需要部署多主多從的模式

在這裏插入圖片描述
如果有了Mycat,客戶端直接連接Mycat,可以實現讀寫分離,如果主出現問題,會自動切換到從服務器上

(2)數據庫分庫分表

分庫分表指的是對數據庫數據的拆分,分爲兩種:水平拆分和垂直拆分

  1. 水平切分(橫向切分)
    根據表中數據的邏輯關係,將同一個表中的數據按照某種條件拆分到多臺數據庫服務器上面
  2. 垂直切分(縱向切分)
    按照不同的表來切分到不同的數據庫服務器之上

MyCAT通過定義表的分片規則來實現分片,每個表格可以捆綁一個分片規則,每個分片規則指定一個分片字段並綁定一個函數,來實現動態分片算法(後面會講到)
在這裏插入圖片描述

  • Schema:邏輯庫,與MySQL中的Database(數據庫)對應,一個邏輯庫中定義了所包括的Table。

  • Table:邏輯表,即物理數據庫中存儲的某一張表,與傳統數據庫不同,這裏的表格需要聲明其所存儲的邏輯數據節點DataNode。在此可以指定表的分片規則。

  • DataNode:MyCAT的邏輯數據節點,是存放table的具體物理節點,也稱之爲分片節點,通過DataSource來關聯到後端某個具體數據庫上

  • DataSource:定義某個物理庫的訪問地址,用於捆綁到Datanode上

性能有瓶頸了,可以讀寫分離
數據庫容量有瓶頸了,可以分庫分表

二、環境搭建

下載地址:http://dl.mycat.io/1.6-RELEASE/

將下載好的Mycat直接上傳到Linux上

1. 解壓

Java語言開發的,直接解壓即可使用

tar -zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz -C /usr/local

2. 常用命令

  • Mycat啓動
    切換到mycat的bin路徑下,執行

    ./mycat start
    
  • Mycat關閉
    切換到mycat的bin路徑下,執行

    ./mycat stop
    
  • Mycat命令行
    登錄mycat命令行,使用mysql的命令行工具來操作的,在mysql的bin路徑下執行:

    • mycat默認數據訪問端口是8066
    ./mysql -umycat -p -P8066 -h127.0.0.1
    

三、配置文件詳解

1. server.xml

主要用於配置mycat需要的服務器信息,常用的配置有:

  • 配置序列生成方式
  • 配置mycat邏輯數據庫
  • 配置mycat的訪問賬戶和密碼
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
	<system>
	<property name="useSqlStat">0</property>  <!-- 1爲開啓實時統計、0爲關閉 -->
	<property name="useGlobleTableCheck">0</property>  <!-- 1爲開啓全加班一致性檢測、0爲關閉 -->

		<property name="sequnceHandlerType">2</property><!-- 主鍵的生成策略 -->
      	<!--<property name="useCompression">1</property>--> <!--1爲開啓mysql壓縮協議-->
     	<!--<property name="fakeMySQLVersion">5.6.20</property>--> <!--設置模擬的MySQL版本號-->
	  	<!--<property name="processorBufferChunk">40960</property> -->
	  	<!-- 
		    <property name="processors">1</property> 
		    <property name="processorExecutor">32</property> 
	   	-->
		<!--默認爲type 0: DirectByteBufferPool | type 1 ByteBufferArena-->
		<property name="processorBufferPoolType">0</property>
		<!--默認是65535 64K 用於sql解析時最大文本長度 -->
		<!--<property name="maxStringLiteralLength">65535</property>-->
		<!--<property name="sequnceHandlerType">0</property>-->
		<!--<property name="backSocketNoDelay">1</property>-->
		<!--<property name="frontSocketNoDelay">1</property>-->
		<!--<property name="processorExecutor">16</property>-->
		
		<property name="serverPort">8066</property> <property name="managerPort">9066</property> <!--設置端口號-->
		<!--
			<property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property> 
			<property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
		<!--分佈式事務開關,0爲不過濾分佈式事務,1爲過濾分佈式事務(如果分佈式事務內只涉及全局表,則不過濾),2爲不過濾分佈式事務,但是記錄分佈式事務日誌-->
		<property name="handleDistributedTransactions">0</property>
		
		<!--
			off heap for merge/order/group/limit      1開啓   0關閉
		-->
		<property name="useOffHeapForMerge">1</property>

		<!--單位爲m-->
		<property name="memoryPageSize">1m</property>

		<!--單位爲k-->
		<property name="spillsFileBufferSize">1k</property>

		<property name="useStreamOutput">0</property>

		<!--單位爲m-->
		<property name="systemReserveMemorySize">384m</property>

		<!--是否採用zookeeper協調切換  -->
		<property name="useZKSwitch">true</property>
	</system>
	
	<!-- 全局SQL防火牆設置 -->
	<firewall> 
	   <whitehost><!--白名單-->
	      <host host="127.0.0.1" user="mycat"/><!--允許ip爲host的主機使用user進行訪問-->
	   </whitehost>
       <blacklist check="false"><!--黑名單-->
       </blacklist>
	</firewall>

	<user name="root"><!--用戶名-->
		<property name="password">123456</property><!--密碼-->
		<property name="schemas">mycatdb</property><!--邏輯庫的名字-->
		
		<!-- 表級 DML 權限設置 -->		
		<privileges check="false">
			<!--每位分別代表:增、刪、改、查,1表示可用,0表示不可用-->
			<schema name="TESTDB" dml="0110" >
				<table name="tb01" dml="0000"></table>
				<table name="tb02" dml="1111"></table>
			</schema>
		</privileges>		
	</user>

	<user name="user"><!--用戶名-->
		<property name="password">user</property>
		<property name="schemas">mycatdb</property>
		<property name="readOnly">true</property><!--只讀-->
	</user>

</mycat:server>

(1)sequnceHandlerType

指定使用Mycat全局序列的類型:

  • 0爲本地文件方式
  • 1爲數據庫方式
  • 2爲時間戳序列方式

2. schema.xml

用於配置的邏輯數據庫的映射、表、分片規則、數據結點及真實的數據庫信息;常用的配置有:

  • 配置邏輯庫映射
  • 配置垂直切分的表
  • 配置真實的數據庫
  • 配置讀寫結點
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

	<!--配置邏輯庫
		name:邏輯庫的名字,一個schema是一個邏輯庫
		checkSQLschema:是否檢查sql語句語法規則
		sqlMaxLimit:最大分頁數量,每個查詢語句最多返回的條數(100條已經很多了)
	-->
	<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
		<!-- 邏輯表,將三個分片節點dn1,dn2,dn3整合到同一個邏輯表中
			name:邏輯表的名字
			dataNode:該邏輯表下的分片節點
			rule:水平分表的主鍵規則
		-->
		<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>
	
	<!--定義Mycat中的數據節點/分片,一個dataNode標籤就是一個獨立的數據分片。通俗理解,一個分片就是一個物理數據庫
		name:定義數據節點的名字,這個名字需要是唯一的
		dataHost:用於定義該分片屬於哪個數據庫實例的,屬性值是引用dataHost標籤上定義的name屬性
		database:用於對應真實的數據庫名,必須是真實存在的
	-->
	<!-- <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" />

	<!--定義具體的數據庫實例、讀寫分離配置和心跳語句;
		name:物理庫的名字
		maxCon/minCon:最大/最小連接數量
		balance:負載均衡策略,詳見後面
		writeType:已過時,1.6版本就不用了
		dbType:數據庫類型
		dbDriver:數據庫驅動類型,只有dbType爲mysql時,纔可以使用native,表示原生驅動;
			其它數據庫類型這裏只能寫jdbc
		switchType:故障切換類型,詳見後面
		slaveThreshold:最多擁有多少個子節點
	-->
	<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
			  writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
		<!--心跳機制,判斷服務器是否正常運行,需要配合switchType一起進行配置-->
		<heartbeat>select user()</heartbeat>
		<!--寫節點,主從複製中的主服務器-->
		<writeHost host="hostM1" url="localhost:3306" user="root"
				   password="123456">
			<!--從節點,主從複製中的從服務器-->
			<readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" />
		</writeHost>
		<writeHost host="hostS1" url="localhost:3316" user="root"
				   password="123456" />
	</dataHost>
</mycat:schema>

(1)dataHost配置詳解

balance (dataHost 標籤中)屬性是指負載均衡類型,目前的取值有4種(推薦設置爲1):

  • balance="0":不開啓讀寫分離機制,所有讀操作都發送到當前可用的writeHost上;
  • balance="1":全部的readHost與stand by writeHost參與select語句的負載均衡,簡單的說,當雙主雙從模式(M1->S1,M2->S2,並且M1與 M2互爲主備),正常情況下,M2、S1、S2都參與select語句的負載均衡。
  • balance="2":所有讀操作都隨機的在writeHost、readhost上分發
  • balance="3":所有讀請求隨機的分發到wiriterHost對應的readhost執行,writerHost不負擔讀壓力

switchType屬性,用於指定主服務器發生故障後的切換類型

  • -1:表示不自動切換
  • 1:默認值,自動切換(推薦)
  • 2:基於MySQL主從同步的狀態決定是否切換
  • 3:基於MySQL galary cluster的切換機制(適合集羣)(1.4.1)

通常情況下,我們MySQL採用雙主雙從的模式下,switchType爲1即可。因爲雙主從模式下,主從同步關係很複雜,不能根據MySQL的狀態來切換。只需要在一個主出問題後,切換到另外的主。

heartbeat標籤

用於和後端數據庫進行心跳檢查的語句,檢測MySQL數據庫是否正常運行

  • 當switchType爲1時,mysql心跳檢查語句是select user()
  • 當switchType爲2時,mysql心跳檢查語句是show slave status
  • 當switchType爲3時,mysql心跳檢查語句是show status like 'wsrep%'

writeHost與readHost標籤

這兩個標籤都指定後端數據庫的相關配置給mycat,用於實例化後端連接池。唯一不同的是,writeHost指定寫實例、readHost指定讀實例,組合這些讀寫實例來滿足系統的要求。

在一個dataHost內可以定義多個writeHost和readHost。但是,如果writeHost指定的後端數據庫宕機,那麼這個writeHost綁定的所有readHost都將不可用。另一方面,由於這個writeHost宕機系統會自動的檢測到,並切換到備用的writeHost上去。

3. rule.xml

用於定義分片規則; 文件中有兩種標籤:

  • <tableRule>:定義分片規則
    • <columns>:根據哪個列(表字段)進行分片計算
    • <algorithm>:function標籤中定義的分片函數
  • <function>:指定相應的類和參數
    • name屬性:指定分片函數的名字
    • class屬性:完成該功能對應的類

(1)分片枚舉

通過在配置文件中配置可能的枚舉 id,自己配置分片,本規則適用於特定的場景,比如有些業務需要按照省份或區縣來做保存,而全國省份區縣固定的,這類業務使用本條規則,配置如下:

<tableRule name="sharding-by-intfile">
	<rule>
		<columns>user_id</columns>
		<algorithm>hash-int</algorithm>
	</rule>
</tableRule>
<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
	<property name="mapFile">partition-hash-int.txt</property>
	<property name="type">0</property>
	<property name="defaultNode">0</property>
</function>

partition-hash-int.txt 配置:

10000=0
10010=1
DEFAULT_NODE=1

其中分片函數配置中:

  • mapFile:標識配置文件名稱,配置文件使用的是鍵值對(類似properties文件)
  • type:默認值爲 0,0 表示 Integer,非零表示 String
  • defaultNode:默認節點,小於 0 表示不設置默認節點,大於等於 0 表示設置默認節點
    默認節點的作用:枚舉分片時,如果碰到不識別的枚舉值,就讓它路由到默認節點,如果不配置默認節點(defaultNode 值小於 0 表示不配置默認節點),碰到不識別的枚舉值就會報錯,can’t find datanode for sharding column
  • 配置文件作用:
    • 枚舉各個id,並指定對應的節點,所有的節點配置都是從 0 開始,及 0 代表節點 1

(2)固定分片 hash 算法

本條規則類似於十進制的求模運算,區別在於是二進制的操作,是取 id 的二進制低 10 位,即 id 二進制&1111111111。

此算法的優點在於如果按照 10 進製取模運算,在連續插入 1-10 時候 1-10 會被分到 1-10 個分片,增大了插入的事務控制難度,而此算法根據二進制則可能會分到連續的分片,減少插入事務事務控制難度。

<tableRule name="rule1">
	<rule>
		<columns>id</columns>
		<algorithm>func1</algorithm>
	</rule>
</tableRule>
<function name="func1" class="io.mycat.route.function.PartitionByLong">
	<property name="partitionCount">2,1</property>
	<property name="partitionLength">256,512</property>
</function>
  • partitionCount:分片個數列表

  • partitionLength:分片範圍列表

  • 上面的配置表示:共分三個區,兩個分區爲25%(256/1024),一個分區爲50%(512/1024),

    • 表字段id的hash值的二進制低10位,如果在0-255的範圍內,被分到第一個分區,如果在256-511的範圍內,被分到第二個分區,如果在252-1023的範圍內,被分到第三個分區
  • 約 束:

    • 分區長度:默認爲最大 2^n=1024,即最大支持 1024 分區
    • count,length 兩個數組的長度必須是一致的。
    • 1024 = sum((count[i]*length[i]))

(3)範圍約定

此分片適用於,提前規劃好分片字段某個範圍屬於哪個分片

<tableRule name="auto-sharding-long">
	<rule>
		<columns>id</columns>
		<algorithm>rang-long</algorithm>
	</rule>
</tableRule>
<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
	<property name="mapFile">autopartition-long.txt</property>
	<property name="defaultNode">0</property>
</function>
  • mapFile:代表配置文件路徑

  • defaultNode:超過範圍後的默認節點。

  • 配置文件:所有的節點配置都是從 0 開始,及 0 代表節點 1,此配置非常簡單,即預先制定可能的 id 範圍到某個分片

    0-500M=0
    500M-1000M=1
    1000M-1500M=2
    

    0-10000000=0
    10000001-20000000=1
    

(4)求模法

此規則爲對分片字段求摸運算。

<tableRule name="mod-long">
	<rule>
		<columns>id</columns>
		<algorithm>mod-long</algorithm>
	</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
	<!-- how many data nodes -->
	<property name="count">3</property>
</function>

此種配置非常明確,即根據id與count(你的結點數)進行十進制求模運算,相比固定分片 hash,此種在批量插入時需要切換數據源,增大事務一致性難度。

(5)按日期(天)分片

<tableRule name="sharding-by-date">
	<rule>
		<columns>create_time</columns>
		<algorithm>sharding-by-date</algorithm>
	</rule>
</tableRule>
<function name="sharding-by-date" class="io.mycat.route.function.PartitionByDate">
	<property name="dateFormat">yyyy-MM-dd</property>
	<property name="sBeginDate">2014-01-01</property>
	<property name="sPartionDay">10</property>
</function>
  • dateFormat:日期格式
  • sBeginDate:開始日期
  • sPartionDay:分區天數(步長),上面的配置表示:從開始日期算起,分隔 10 天一個分區
    例如:2014-01-01分區爲0,2014-01-02分區爲0,2014-01-10分區爲0,2014-01-11分區爲1……依此類推

(6)取模範圍約束

此種規則是取模運算與範圍約束的結合,主要爲了後續數據遷移做準備,即可以自主決定取模後數據的節點分佈。

<tableRule name="sharding-by-pattern">
	<rule>
		<columns>user_id</columns>
		<algorithm>sharding-by-pattern</algorithm>
	</rule>
</tableRule>
<function name="sharding-by-pattern" class="io.mycat.route.function.PartitionByPattern">
	<property name="patternValue">256</property>
	<property name="defaultNode">2</property>
	<property name="mapFile">partition-pattern.txt</property>
</function>

partition-pattern.txt:

###### first host configuration
1-32=0
33-64=1
65-96=2
97-128=3
######## second host configuration
129-160=4
161-192=5
193-224=6
225-256=7
0-0=7
  • patternValue:即求模基數
  • defaoultNode:默認節點,如果沒有配置,則默認爲第0個節點
  • mapFile 配置文件路徑
    • 配置文件中,1-32 即代表 id%256 後分布的範圍,如果在 1-32 則在分區 1,其他類推,如果 id 非數據,則會分配在 defaoultNode 默認節點

(7) 應用指定

此規則是在運行階段有應用自主決定路由到那個分片。

<tableRule name="sharding-by-substring">
	<rule>
		<columns>user_id</columns>
		<algorithm>sharding-by-substring</algorithm>
	</rule>
</tableRule>
<function name="sharding-by-substring" class="io.mycat.route.function.PartitionDirectBySubString">
	<property name="startIndex">0</property><!-- zero-based -->
	<property name="size">2</property>
	<property name="partitionCount">8</property>
	<property name="defaultPartition">0</property>
</function>

此方法爲直接根據字符子串(必須是數字)計算分區號(由應用傳遞參數,顯式指定分區號)。
例如:id爲05-100000002
在上面的配置中,根據 id ,從 startIndex=0 開始,截取 size=2 位字符串數字即 05,05 就是獲取的分區,如果沒傳默認分配到 defaultPartition;如果截取出的字符超過了partitionCount,則分配到defaultPartition

(8)一致性 hash

一致性 hash 預算有效解決了分佈式數據的擴容問題。

<tableRule name="sharding-by-murmur">
	<rule>
		<columns>user_id</columns>
		<algorithm>murmur</algorithm>
	</rule>
</tableRule>
<function name="murmur" class="io.mycat.route.function.PartitionByMurmurHash">
	<property name="seed">0</property><!-- 默認是 0-->
	<property name="count">2</property><!-- 要分片的數據庫節點數量,必須指定,否則沒法分片-->
	<property name="virtualBucketTimes">160</property><!-- 一個實際的數據庫節點被映射爲這麼多虛擬節點,默認是 160 倍,也就是虛擬節點數是物理節點數的 160 倍-->
	<!--
	<property name="weightMapFile">weightMapFile</property>
	節點的權重,沒有指定權重的節點默認是 1。以 properties 文件的格式填寫,以從 0 開始到 count-1 的整數值也就
	是節點索引爲 key,以節點權重值爲值。所有權重值必須是正整數,否則以 1 代替 -->
	<!--
	<property name="bucketMapPath">/etc/mycat/bucketMapPath</property>
	用於測試時觀察各物理節點與虛擬節點的分佈情況,如果指定了這個屬性,會把虛擬節點的 murmur hash 值與物理節
	點的映射按行輸出到這個文件,沒有默認值,如果不指定,就不會輸出任何東西 -->
</function>

其它分片規則詳見官方文檔10.5節:http://www.mycat.io/document/mycat-definitive-guide.pdf

四、讀寫分離

先只做讀寫分離,不做分庫分表,Mycat只是幫我們轉發一下請求,讀轉發到從庫,寫轉發到主庫。

1. 配置server.xml

  1. 對於讀寫分離而言,是不需要考慮主鍵生成方式的,也就是不需要配置全局序列號的,故sequnceHandlerType參數設置爲1

    <property name="sequnceHandlerType">1</property>
    
  2. 配置連接mycat的用戶名和密碼,以及對應的邏輯庫

    <user name="mycat">
        <property name="password">123456</property>
        <property name="schemas">mycatdb</property>
    </user>
    

2. 配置schema.xml

  1. 由於只做讀寫分離,則schema標籤裏面不用配置table

    <schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"/>
    
  2. 配置dataNode

    <dataNode name="dn1" dataHost="localhost1" database="test" />
    
  3. 配置dataHost,指定writeHost和readHost

  • 一主三從dataHost配置:

    <dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
    		  writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
    	
    	<heartbeat>select user()</heartbeat>
    	
    	<!--配置寫數據庫(主庫) 一主三從的讀寫分離配置 -->
    	<writeHost host="hostM3307" url="localhost:3307" user="root" password="123456">
    		<!--配置寫數據庫下的讀數據庫(從庫)-->
    		<readHost host="hostS3308" url="localhost:3308" user="root" password="123456" />
    		<readHost host="hostS3309" url="localhost:3309" user="root" password="123456" />
    		<readHost host="hostS3310" url="localhost:3310" user="root" password="123456" />
    	</writeHost>
    	
    </dataHost>
    
  • 雙主雙從dataHost配置:

    <dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
    		  writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
    	
    	<!--配置心跳語句 -->
    	<heartbeat>select user()</heartbeat>
    	
    	<!--配置寫數據庫(主庫) 雙主雙從的讀寫分離配置 -->
    	<writeHost host="hostM3307" url="localhost:3307" user="root" password="123456">
    		<!--配置寫數據庫下的讀數據庫(從庫)-->
    		<readHost host="hostS3308" url="localhost:3308" user="root" password="123456" />
    		<readHost host="hostS3309" url="localhost:3309" user="root" password="123456" />
    	</writeHost>
    	
    	<writeHost host="hostM3308" url="localhost:3308" user="root" password="123456">
    		<!--配置寫數據庫下的讀數據庫(從庫)-->
    		<readHost host="hostS3307" url="localhost:3307" user="root" password="123456" />
    		<readHost host="hostS3310" url="localhost:3310" user="root" password="123456" />
    	</writeHost>
    	
    </dataHost>
    

五、分庫分表

不管是何種方式的切分,主鍵生成必須交給MyCat實現

1. 水平

(1)配置server.xml

指定主鍵的生成策略

<!--配置數據庫的主鍵怎麼生成,0爲本地文件方式,1爲數據庫方式,2爲時間戳序列方式-->
<property name="sequnceHandlerType">0</property>

(2)配置schema.xml

指定邏輯庫,分片結點,結點主機等

  • 要實現分庫分表,那麼就需要在<schema>標籤下配置表了,現在是水平切分,表示要對哪張表進行切分
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="100">
    <!-- 要實現分庫分表,那麼就需要在<schema>標籤下配置表了,現在是水平切分,表示要對哪張表進行切分 -->
    <table name="orders" primaryKey="id" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="mod-long" />
</schema>

<!--配置真實的數據庫名稱 test -->
<dataNode name="dn1" dataHost="localhost1" database="test01" />
<dataNode name="dn2" dataHost="localhost1" database="test02" />
<dataNode name="dn3" dataHost="localhost1" database="test03" />

<!--讀寫分離前面已經說過了,這裏就只寫了雙主雙從的情況-->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
		  writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
    <heartbeat>select user()</heartbeat>
    <writeHost host="hostM3307" url="localhost:3307" user="root" password="123456">
        <readHost host="hostS3308" url="localhost:3308" user="root" password="123456" />
        <readHost host="hostS3309" url="localhost:3309" user="root" password="123456" />
    </writeHost>
    <writeHost host="hostM3308" url="localhost:3308" user="root" password="123456">
        <readHost host="hostS3307" url="localhost:3307" user="root" password="123456" />
        <readHost host="hostS3310" url="localhost:3310" user="root" password="123456" />
    </writeHost>
</dataHost>
</mycat:schema>

(2)配置rule.xml

根據實際情況選擇水平拆分的規則,這裏使用的是求模法:

<tableRule name="mod-long">
	<rule>
		<columns>id</columns>
		<algorithm>mod-long</algorithm>
	</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
	<!-- how many data nodes -->
	<property name="count">3</property>
</function>

2. 垂直

垂直切分帶來的價值:可以屏蔽掉多數據源的問題,只需要一個統一入口mycat就可以操作下面的多個數據庫

(1)配置server.xml

指定主鍵生成策略

<!--配置數據庫的主鍵怎麼生成,0爲本地文件方式,1爲數據庫方式,2爲時間戳序列方式-->
<property name="sequnceHandlerType">0</property>

(2)配置schema.xml

也同樣需要配置table標籤,水平切分是要對哪張表進行切分就配置那張表;垂直切分需要將數據庫中的所有表都配置上,指定不同的datanode

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

	<schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="100">
		
		<!-- 要實現分庫分表,那麼就需要在<schema>標籤下配置表了,現在是垂直切分 -->
		
		<!--需求:整個P2P平臺的數據庫(p2p)進行垂直切分,分爲前臺數據庫(p2p-web)、後臺數據庫(p2p-admin)、紅包數據庫(p2p-red)-->
		
		<!--P2P前臺數據庫-->
		<table name="orders" primaryKey="id" autoIncrement="true" dataNode="dn1"/>
		<table name="users" primaryKey="id" autoIncrement="true" dataNode="dn1"/>
		
		<!--P2P後臺數據庫-->
		<table name="products" primaryKey="id" autoIncrement="true" dataNode="dn2"/>
		<table name="news" primaryKey="id" autoIncrement="true" dataNode="dn2"/>
		
		<!--P2P紅包數據庫-->
		<table name="reds" primaryKey="id" autoIncrement="true" dataNode="dn3"/>
		<table name="redType" primaryKey="id" autoIncrement="true" dataNode="dn3"/>
	</schema>

	<!--配置真實的數據庫名稱 testdb01 -->
	<dataNode name="dn1" dataHost="localhost1" database="p2p-web" />
	<dataNode name="dn2" dataHost="localhost1" database="p2p-admin" />
	<dataNode name="dn3" dataHost="localhost1" database="p2p-red" />

	<!--配置具體的數據庫連接信息、讀寫分離、心跳語句 -->
	<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
			  writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
		
		<!--配置心跳語句 -->
		<heartbeat>select user()</heartbeat>
		
		<!--配置寫數據庫(主庫) 雙主雙從的讀寫分離配置 -->
		<writeHost host="hostM3307" url="localhost:3307" user="root" password="123456">
			<!--配置寫數據庫下的讀數據庫(從庫)-->
			<readHost host="hostS3308" url="localhost:3308" user="root" password="123456" />
			<readHost host="hostS3309" url="localhost:3309" user="root" password="123456" />
		</writeHost>
		
		<writeHost host="hostM3308" url="localhost:3308" user="root" password="123456">
			<!--配置寫數據庫下的讀數據庫(從庫)-->
			<readHost host="hostS3307" url="localhost:3307" user="root" password="123456" />
			<readHost host="hostS3310" url="localhost:3310" user="root" password="123456" />
		</writeHost>
		
	</dataHost>
</mycat:schema>

六、Mycat全局序列號

在實現分庫分表的情況下,數據庫自增主鍵已無法保證自增主鍵的全局唯一。爲此,MyCat 提供了全局 sequence,並且提供了包含本地配置和數據庫配置等多種實現方式。

1. 本地文件方式

在servler.xml文件中配置<property name="sequnceHandlerType">0</property>,即爲本地文件方式,在conf/sequence_conf.properties中維護主鍵信息,每當插入數據時,從該文件中取出主鍵信息,並修改該文件的值。

conf/sequence_conf.properties文件默認提供的主鍵信息:

  • HISIDS:表示使用過的歷史分段(一般無特殊需要可不配置)
  • MINID:表示最小 ID 值
  • MAXID:表示最大ID 值
  • CURID(currentId):當前主鍵id,下回插入時,插入的主鍵爲:10001
#default global sequence
GLOBAL.HISIDS=
GLOBAL.MINID=10001
GLOBAL.MAXID=200000
GLOBAL.CURID=10000
  • 如果想要每個表生成的主鍵連續,可以在sequence_conf.properties配置當前表的生成值,一般將Global替換爲自己對應表的前綴即可。

  • 取主鍵值的時候通過next value for MYCATSEQ_XXXX獲取

例如插入語句:

insert into tbl_user (id, name) values (next value for MYCATSEQ_Global, '張三');
  • 缺點:當 MyCAT 重新發布後,配置文件中的 sequence 會恢復到初始值。
  • 優點:本地加載,讀取速度較快。

2. 時間戳方式

在servler.xml文件中配置<property name="sequnceHandlerType">2</property>,即爲時間戳方式

ID= 64 位二進制 (42(毫秒)+5(機器 ID)+5(業務編碼)+12(重複累加)
換算成十進制爲 18 位數的 long 類型,每毫秒可以併發 12 位二進制的累加。

這種方式,需要將主鍵設置爲bigint或者varchar(長度一般20)類型,插入時,不指定主鍵,MyCat會自動生成主鍵

3. 數據庫方式

在servler.xml文件中配置<property name="sequnceHandlerType">1</property>,表示使用數據庫方式生成sequence

(1)原理

在數據庫中建立一張表,存放 sequence 名稱(name),sequence 當前值(current_value),步長(increment int 類型每次讀取多少個 sequence,假設爲 K)等信息;

這裏是數據庫方式生成主鍵ID,不是採用數據庫的主鍵自增,而是mycat利用mysql數據庫生成一個主鍵,使用的是數據庫的函數,函數需要自定義

  1. 當初次使用該 sequence 時,根據傳入的 sequence 名稱,從數據庫這張表中讀取 current_value,和 increment 到 MyCat 中,並將數據庫中的 current_value 設置爲原 current_value 值+increment 值。
  2. MyCat 將讀取到 current_value+increment 作爲本次要使用的 sequence 值,下次使用時,自動加 1,當使用 increment 次後,執行步驟 1 相同的操作。
  3. MyCat 負責維護這張表,用到哪些 sequence,只需要在這張表中插入一條記錄即可。若某次讀取的 sequence 沒有用完,系統就停掉了,則這次讀取的 sequence 剩餘值不會再使用。

(2)創建表與函數

在數據庫中創建一張表,插入一條數據,創建三個函數:

# 創建存放 sequence 的表
DROP TABLE IF EXISTS MYCAT_SEQUENCE;

# name sequence 名稱
# current_value 當前 value
# increment 增長步長。 可理解爲 mycat 在數據庫中一次讀取多少個 sequence放到內存中。 當這些用完後, 下次再從數據庫中讀取。
CREATE TABLE MYCAT_SEQUENCE (name VARCHAR(50) NOT NULL,current_value INT NOT NULL,increment INT NOT NULL DEFAULT 1,
PRIMARY KEY(name)) ENGINE=InnoDB default charset=utf8;

# 插入一條 sequence
INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ("GLOBAL", 0, 100);

# 獲取當前 sequence 的值 (返回當前值,增量)
DROP FUNCTION IF EXISTS mycat_seq_currval;
DELIMITER
CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf8
DETERMINISTIC
BEGIN
DECLARE retval VARCHAR(64);
SET retval="-999999999,null";
SELECT concat(CAST(current_value AS CHAR),",",CAST(increment AS CHAR)) INTO retval FROM MYCAT_SEQUENCE WHERE name = seq_name;
RETURN retval;
END
DELIMITER;

# 設置 sequence 值
DROP FUNCTION IF EXISTS mycat_seq_setval;
DELIMITER
CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),value INTEGER) RETURNS varchar(64) CHARSET utf8
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = value
WHERE name = seq_name;
RETURN mycat_seq_currval(seq_name);
END
DELIMITER;

#  獲取下一個 sequence 值
DROP FUNCTION IF EXISTS mycat_seq_nextval;
DELIMITER
CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf8
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = current_value + increment WHERE name = seq_name;
RETURN mycat_seq_currval(seq_name);
END
DELIMITER;

(3)配置在哪個節點上

MYCAT_SEQUENCE 表和以上的 3 個 function,需要放在同一個節點上。在 conf/sequence_db_conf.propertie 文件中 GLOBAL 參數指定存放的節點:

#sequence stored in datanode
GLOBAL=dn1
COMPANY=dn1
CUSTOMER=dn1
ORDERS=dn1

例如:上面的MYCAT_SEQUENCE 表和以上的 3 個 function 存放的數據庫對應的節點爲dn3,則 conf/sequence_db_conf.propertie 文件中的GLOBAL參數應改爲dn3

如果執行的時候報:

sql you might want to use the less safe log_bin_trust_function_creators variable 

需要對數據庫做如下設置:

  • windows 下 my.ini[mysqld]加上 log_bin_trust_function_creators=1
  • linux 下/etc/my.cnf 下 my.ini[mysqld]加上 log_bin_trust_function_creators=1

修改完後,即可在 mysql 數據庫中執行上面的函數。

使用示例:

insert into table1(id,name) values(next value for MYCATSEQ_GLOBAL,‘test’)

八、java連接MyCat

  1. SpringBoot中,連接MyCat的參數指定(和連接MySQL類似,端口改爲8066,用戶名和密碼改爲MyCat中的):

    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://192.168.29.128:8066/mycat?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=CTT
    spring.datasource.username=mycat
    spring.datasource.password=123
    
  2. 在數據庫的插入語句中,根據指定的主鍵生成規則,獲取相應的主鍵進行插入

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章