<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright 1999-2012 Alibaba Group. 。。。。--> <!DOCTYPE cobar:schema SYSTEM "schema.dtd"> <cobar:schema xmlns:cobar="http://cobar.alibaba.com/"> <!-- schema定義 name="lyw" 意思是對外顯示的數據庫名是lyw,dataNode="dnG"表示默認的數據節點是dnG --> <schema name="lyw" dataNode="dnG"> <table name="h1" dataNode="dn0,dn1,dn2,dn3" rule="ruleLong" /> </schema> <!-- 數據節點定義,數據節點由數據源和其他一些參數組織而成。ds[0]這樣的下標是dataSource中數據數組的序號 --> <dataNode name="dn0"> <property name="dataSource"> <dataSourceRef>ds[0]</dataSourceRef> </property> </dataNode> <dataNode name="dn1"> <property name="dataSource"> <dataSourceRef>ds[1]</dataSourceRef> </property> </dataNode> <dataNode name="dn2"> <property name="dataSource"> <dataSourceRef>ds[2]</dataSourceRef> </property> </dataNode> <dataNode name="dn3"> <property name="dataSource"> <dataSourceRef>ds[3]</dataSourceRef> </property> </dataNode> <!-- 不分片的數據源,跟schema第一個dataNode對應 --> <dataNode name="dnG"> <property name="dataSource"> <dataSourceRef>ds[8]</dataSourceRef> </property> </dataNode> <!-- 數據源定義,數據源是一個具體的後端數據連接的表示。 --> <dataSource name="ds" type="mysql"> <property name="location"> <location>192.168.1.8:14011/lyw</location> <location>192.168.1.8:14021/lyw</location> <location>192.168.1.8:14031/lyw</location> <location>192.168.1.8:14041/lyw</location> <location>192.168.1.8:14051/lyw</location> <location>192.168.1.8:14061/lyw</location> <location>192.168.1.8:14071/lyw</location> <location>192.168.1.8:14081/lyw</location> <location>192.168.1.8:14091/lyw</location> </property> <property name="user">lyw</property> <property name="password">123456</property> <property name="sqlMode">STRICT_TRANS_TABLES</property> </dataSource> </cobar:schema>
配置好schema.xml後,我們可以看到裡面有個字段rule="ruleLong",這個ruleLong的具體內容是配置在rule.xml文件中,我們這裡的配置如下
<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright 1999-2012 Alibaba Group. 。。。。--> <!DOCTYPE cobar:rule SYSTEM "rule.dtd"> <cobar:rule xmlns:cobar="http://cobar.alibaba.com/"> <!-- 路由規則定義,定義什麼表,什麼字段,采用什麼路由算法 --> <!-- 所有路由規則tableRule都必須在所有函數function前面 --> <tableRule name="ruleLong"> <rule> <columns>id</columns> <algorithm><![CDATA[ funcLong(${id}) ]]></algorithm> </rule> </tableRule> <!-- 路由函數定義,所有函數定義都需要在規則下面 --> <!--- partitionCount * partitionLength必須等於1024 --> <function name="funcLong" class="com.alibaba.cobar.route.function.PartitionByLong"> <property name="partitionCount">4</property> <property name="partitionLength">256</property> </function> </cobar:rule>
可以看到ruleLong規則中用到funcLong函數,funcLong函數在下面定義,注意所有函數定義都需要在規則下面,partitionCount * partitionLength必須等於1024,否則無法啟動。 然後是server.xml,這個文件修改下用戶名密碼就可以了,其他用默認參數。
<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright 1999-2012 Alibaba Group. --> <!DOCTYPE cobar:server SYSTEM "server.dtd"> <cobar:server xmlns:cobar="http://cobar.alibaba.com/"> <!-- 用戶訪問定義,用戶名、密碼、schema等信息。 --> <user name="lyw"> <property name="password">123456</property> <property name="schemas">lyw</property> </user> </cobar:server>
另外還有個配置文件log4j.xml,無需修改。 我們的配置文件都已准備好,然後就啟動吧
$ bin/startup.sh $ jps 15894 CobarStartup 15946 Jps $ netstat -nlp|grep java tcp6 0 0 :::8066 :::* LISTEN 15894/java tcp6 0 0 :::9066 :::* LISTEN 15894/java
java7下可以直接啟動,如果時java8需要注釋掉startup.sh中的一行。 1 # JAVA_OPTS="$JAVA_OPTS -XX:+UseCMSCompactAtFullCollection" 請檢查下進程是否啟動,如果配置有誤是無法啟動的。啟動後,我們可以看到已經開啟了兩個端口,8066和9066,其中8066是用於數據讀寫等操作的,9066是用於cobar自身管理的。我們迫不及待的要去試下了。 第四步:使用Cobar
lyw@lywd:~/db/mariadb-10.1$ bin/mysql -ulyw -p123456 -h127.0.0.1 -P8066 MySQL [(none)]> show databases; +----------+ | DATABASE | +----------+ | lyw | +----------+ 1 row in set (0.04 sec) MySQL [(none)]> use lyw; Database changed MySQL [lyw]> show tables; Empty set (0.00 sec) MySQL [lyw]> create table h1 (id int primary key, v varchar(32)); Query OK, 0 rows affected (0.01 sec) MySQL [lyw]> insert into h1 (id, v) values(1, 'aa'), (2, '2'), (256, 'cc'), (600,'dd'),(900, 'ee'), (1000, 'ff'); Query OK, 6 rows affected (0.02 sec) Records: 2 Duplicates: 0 Warnings: 0 MySQL [lyw]> select * from h1; +------+------+ | id | v | +------+------+ | 256 | cc | | 900 | ee | | 1000 | ff | | 600 | dd | | 1 | aa | | 2 | 2 | +------+------+ 6 rows in set (0.00 sec) MySQL [lyw]> select * from h1 where id = 256; +-----+------+ | id | v | +-----+------+ | 256 | cc | +-----+------+ 1 row in set (0.00 sec) MySQL [lyw]> select * from h1 where id in (256, 900, 901); +-----+------+ | id | v | +-----+------+ | 256 | cc | | 900 | ee | +-----+------+ 2 rows in set (0.00 sec)
最後返回的結果看起來有點亂,並且每次執行這樣的select語句,順序都會不同,這是因為我們插入的這6條數據已經根據ruleLong的規則分散在了4個數據庫中,返回的時候cobar只是簡單的合並,並沒有排序。我們這時候可以到4個庫中分別查詢,每個庫都只包含其中的一部分數據。 注意:前面的insert語句中指定了列名(id, v),這是必須的,否則會將數據插入到所有數據庫中去,即插入1條等於插入4條。 select * from h1 where id = 256; 這行查詢語句指定了id = 256,因此cobar會計算256這個值是在哪個數據庫(第二個)因此這條語句只會在第二個數據庫中查找 select * from h1 where id in (256, 900, 901); 這行查詢語句指定了兩個id,cobar會計算這些id都屬於哪個庫,然後去對應的庫查詢,實際上會變成兩個語句, 在第二個庫執行select * from h1 where id in (256), 在第四個庫執行select * from h1 where id in (900, 901), 然後合並數據返回。 我們可以用explain命令查看cobar的拆分情況。這個命令只是語法分析,不會到mysql中執行。 MySQL [lyw]> explain select * from h1 where id in (256, 900, 901); +-----------+-----------------------------------------+ | DATA_NODE | SQL | +-----------+-----------------------------------------+ | dn1 | SELECT * FROM h1 WHERE id IN (256) | | dn3 | SELECT * FROM h1 WHERE id IN (900, 901) | +-----------+-----------------------------------------+ 2 rows in set (0.00 sec) Cobar是不支持事務的,begin命令就不可以運行,很多操作盡量使用語句內原子操作。比如 update h1 set a=a+1 where id = 2; 而不是先讀出數據,再修改。拆成兩條就需要事務支持才安全了。 Cobar支持多庫同時操作,但只是在多個庫分別執行後,一起返回數據而已,我們試下下面的幾個語句 MySQL [lyw]> select * from h1 limit 1; +-----+------+ | id | v | +-----+------+ | 600 | dd | | 256 | cc | | 1 | aa | | 900 | ee | +-----+------+ 我們是想要得到一條數據,而實際上是得到4條,並且是每個庫中一條,因此這樣的結果並不符合我們的初衷,所以對於分頁這樣的操作用cobar並不合適。大家還可以去試下sort, group,join等操作,以及他們的組合操作,都是如此。 那Cobar合適的是什麼呢?cobar最合適的就是單行的操作,另外還有in這樣的多行操作。或者其他只需要一個庫就能搞定的操作。這也是需要我們在設計表結構的時候多下工夫才行。 第五步:多種分片方式配置 前面講了一個按照數字進行hash分片的例子。cobar自身提供了4種分片方法,分別是PartitionByLong,PartitionByString,PartitionByFileMap,Dimension2PartitionFunction。每種方法需要配置的參數都不同 PartitionByString 按字符串hash分片 我們需要修改rule.xml文件,如下部分,記得所有的tableRule 在所有的function前面
<tableRule name="ruleString"> <rule> <columns>id</columns> <algorithm><![CDATA[ funcString(${id}) ]]></algorithm> </rule> </tableRule> <function name="funcString" class="com.alibaba.cobar.route.function.PartitionByString"> <property name="partitionCount">4</property> <property name="partitionLength">256</property> <property name="hashSlice">:12</property> </function>
其中hashSlice的含義是字符串的哪幾個字符進行hash運算,例子中:12表示前面的12個字符進行運算,另外還有負數表示法,表示從後面開始數。 在schema.xml增加一個表格 1 <table name="h3" dataNode="dn0,dn1,dn2,dn3" rule="ruleString" /> 配置好後,我們可以用集群管理的reload方法熱更新配置文件。(注意端口是9066) bin/mysql -ulyw -p123456 -h127.0.0.1 -P 9066 MySQL [(none)]> reload @@config; Query OK, 1 row affected (0.02 sec) Reload config success PartitionByFileMap 按文件內容分片 我們需要修改rule.xml文件,如下部分,
<tableRule name="ruleFileMap"> <rule> <columns>district </columns> <algorithm><![CDATA[ funcFileMap(${id}) ]]></algorithm> </rule> </tableRule> <function name="funcFileMap" class="com.alibaba.cobar.route.function.PartitionByFileMap"> <property name="fileMapPath">/home/lyw/file_map.txt</property> <property name="defaultNode">0</property> </function>
rule.xml中fileMapPath字段我們指定了一個配置文件/home/lyw/file_map.txt,這個文件內容的格式是k=v結構,k是分片的字符串,v是節點序號(不是hash值)。內容如下,您可自己多寫一些。 1 2 3 4 5 a=0 b=1 c=2 d=3 。。。。。。 defaultNode 字段表示如果key值不在這個配置文件中,那麼將數據存儲在這個節點中。 然後我們還要在schema.xml增加一個表格 <table name="h5" dataNode="dn0,dn1,dn2,dn3" rule="ruleFileMap" /> 只有字符串完整地屬於配置文件中,才算匹配到,不是前綴,如例子中只有a,b,c,d可以找到對應的節點,其他任何值都將放入默認節點。因此這種方式一般不是用於id等主鍵字段,而是其他種類有限的字段,如國家、省份等。 Dimension2PartitionFunction 二維分片 二維分片有兩個維度,都需要配置,因此配置內容較多 rule.xml:
<tableRule name="rule2D"> <rule> <columns>id, id2</columns> <algorithm><![CDATA[ func2D(${id}, ${id2}) ]]></algorithm> </rule> <rule> <columns>id</columns> <algorithm><![CDATA[ func2D(${id}, null) ]]></algorithm> </rule> <rule> <columns>id2</columns> <algorithm><![CDATA[ func2D(null, ${id2}) ]]></algorithm> </rule> </tableRule> <function name="func2D" class="com.alibaba.cobar.route.function.Dimension2PartitionFunction"> <property name="keyTypeX">string</property> <property name="partitionCountX">2</property> <property name="partitionLengthX">512</property> <property name="hashSliceX">:12</property> <property name="keyTypeY">long</property> <property name="partitionCountY">2</property> <property name="partitionLengthY">512</property> </function>
從rule.xml文件中我們看到tableRule配置了三個rule,其中第一個規則是有兩個參數的,後兩個規則只有一個參數,這3個rule不是必須全配置,但是要實現只匹配一個維度的話,就需要配置,否則當查詢語句中只有一個維度的key時會進行所有庫的執行,效率不高。 函數需要指定兩個維度,字符串和數字都可以,其他類型目前不支持。 同樣schema.xml增加一個表格 <table name="d1" dataNode="dn0,dn1,dn2,dn3" rule="rule2D" /> 如果需要二維分片,集群規模一般要相當大了,比如8*8=64,否則采用二維分片的意義不大。 第六步:Cobar自身集群配置 前面配置的cobar只是在一台機器上運行,而運行時cobar需要的資源是比較多的,一台cobar可以拖3台左右mysql服務器,而實際上只要用到cobar,mysql數量都在8台以上,所以需要多個cobar支撐,集群相關的內容在server.xml中配置,
<cluster> <node name="cobar1"> <property name="host">192.168.1.8</property> <property name="weight">1</property> </node> <node name="cobar2"> <property name="host">192.168.1.9</property> <property name="weight">1</property> </node> <node name="cobar3"> <property name="host">192.168.1.10</property> <property name="weight">1</property> </node> </cluster>
每增加一台機器就多配置一個node,然後將這個配置文件復制到每台cobar電腦上,並啟動。這時我們在任意一台電腦上用下面的命令查看活著的集群(宕機的節點不會顯示)
MySQL [lyw]> show cobar_cluster; +--------------+--------+ | HOST | WEIGHT | +--------------+--------+ | 192.168.1.8 | 1 | | 192.168.1.9 | 1 | | 192.168.1.10 | 1 | +--------------+--------+ 3 rows in set (0.00 sec)
此處雖然有host和weight兩個值,但是cobar並沒有做負載均衡相關的具體事情,只是告訴客戶端,cobar集群的運行情況,讓客戶端自己制定負載均衡策略。 第七步:Cobar自身管理 前面講的是數據操作,用的是8066端口,cobar提供集群管理功能默認用的是9066端口 bin/mysql -ulyw -p123456 -h127.0.0.1 -P9066 MySQL [(none)]> show @@help; reload命令會是一個常用命令,當修改了配置文件,就執行下這個命令讓配置生效。 MySQL [(none)]> reload @@config; Query OK, 1 row affected (0.01 sec) Reload config success 注意server.xml中的system部分不可以熱加載,需要重啟生效。 如果加載後發現有誤,可以用rollback命令回滾配置,注意只能回滾一次。 假如我們要看下所有服務器的活動情況,我們可以用下面的命令
MySQL [(none)]> show @@heartbeat; +--------+-------+-------------+-------+---------+-------+----------+---------+--------------+---------------------+-------+ | NAME | TYPE | HOST | PORT | RS_CODE | RETRY | STATUS | TIMEOUT | EXECUTE_TIME | LAST_ACTIVE_TIME | STOP | +--------+-------+-------------+-------+---------+-------+----------+---------+--------------+---------------------+-------+ | cobar1 | COBAR | 192.168.1.8 | 8066 | 1 | 0 | idle | 10000 | 0,0,0 | 2015-10-22 10:33:28 | false | | cobar2 | COBAR | 192.168.1.9 | 8066 | -1 | 3 | checking | 10000 | 0,0,0 | 2015-10-22 10:33:27 | false | | cobar3 | COBAR | 192.168.1.10| 8066 | -1 | 1 | checking | 10000 | 0,0,0 | 2015-10-22 10:33:26 | false | | dn0 | MYSQL | 192.168.1.8 | 14011 | 0 | 0 | idle | -1 | 0,0,0 | NULL | false | | dn1 | MYSQL | 192.168.1.8 | 14021 | 0 | 0 | idle | -1 | 0,0,0 | NULL | false | | dn2 | MYSQL | 192.168.1.8 | 14031 | 0 | 0 | idle | -1 | 0,0,0 | NULL | false | | dn3 | MYSQL | 192.168.1.8 | 14041 | 0 | 0 | idle | -1 | 0,0,0 | NULL | false | | dnG | MYSQL | 192.168.1.8 | 14091 | 0 | 0 | idle | -1 | 0,0,0 | NULL | false | +--------+-------+-------------+-------+---------+-------+----------+---------+--------------+---------------------+-------+ 8 rows in set (0.00 sec)
前面3行是cobar自身集群,我這裡只啟動了一台,所以只有一台的狀態是正常的,其他兩台都不可用,一直在嘗試檢查是否活動起來。 下面5台並沒有開啟心跳,所以timeout是-1,不會進行檢查,直接認為是正常的。 其他命令您可以自己嘗試,都很好理解。 第八步:dataNode的高可用配置 細心的您一定發現了我們開始的時候部署了9個mysql,但是我們只有用到了5台,另外4台還沒有用起來,現在我們要將另外4台也用起來。 我們計劃將另外4台和前面4台兩兩配置為雙主(也可以是其他方案,如galera等) 然後修改我們的配置文件schema.xml中dataNode的內容,每個dataNode都增加一台對應的服務器,並且配置上心跳,(實際上心跳最好是寫操作的語句)
<!-- 數據節點定義,數據節點由數據源和其他一些參數組織而成。 --> <dataNode name="dn0"> <property name="dataSource"> <dataSourceRef>ds[0]</dataSourceRef> <dataSourceRef>ds[4]</dataSourceRef> </property> <property name="heartbeatSQL">select user()</property> </dataNode> <dataNode name="dn1"> <property name="dataSource"> <dataSourceRef>ds[1]</dataSourceRef> <dataSourceRef>ds[5]</dataSourceRef> </property> <property name="heartbeatSQL">select user()</property> </dataNode> <dataNode name="dn2"> <property name="dataSource"> <dataSourceRef>ds[2]</dataSourceRef> <dataSourceRef>ds[6]</dataSourceRef> </property> <property name="heartbeatSQL">select user()</property> </dataNode> <dataNode name="dn3"> <property name="dataSource"> <dataSourceRef>ds[3]</dataSourceRef> <dataSourceRef>ds[7]</dataSourceRef> </property> <property name="heartbeatSQL">select user()</property> </dataNode>
然後reload配置,在用show @@datanode;命令檢查下運行情況 MySQL [(none)]> show @@datanode; +------+-------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+ | NAME | DATASOURCES | INDEX | TYPE | ACTIVE | IDLE | SIZE | EXECUTE | TOTAL_TIME | MAX_TIME | MAX_SQL | RECOVERY_TIME | +------+-------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+ | dn0 | ds[0],ds[4] | 0 | mysql | 0 | 0 | 128 | 0 | 0 | 0 | 0 | -1 | | dn1 | ds[1],ds[5] | 0 | mysql | 0 | 0 | 128 | 0 | 0 | 0 | 0 | -1 | | dn2 | ds[2],ds[6] | 0 | mysql | 0 | 0 | 128 | 0 | 0 | 0 | 0 | -1 | | dn3 | ds[3],ds[7] | 0 | mysql | 0 | 0 | 128 | 0 | 0 | 0 | 0 | -1 | | dnG | ds[8] | 0 | mysql | 0 | 0 | 128 | 0 | 0 | 0 | 0 | -1 | +------+-------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+ 5 rows in set (0.01 sec) 我們發現DATASOURCES中前面4個都有兩個節點,INDEX目前都是0,表示連接的是前面這個節點。 這時我們模擬宕機,將ds[0]這個節點kill掉,過幾秒鐘再用上面這個命令查看時,就會發現第一個INDEX變成了1,也就是說用到的是ds[4]節點。之後的讀寫都會在ds[4]中操作。 然後我們重新啟動ds[0],再查狀態,發現還是INDEX還是維持1,cobar不會主動切換回去。假如我們想切換回去,可以用命令switch @@datasource name:index,(序號參數可選) MySQL [(none)]> switch @@datasource dn0:0; Query OK, 1 row affected (0.03 sec) 注意該功能只會修改當前cobar的配置,其他節點並不會一同修改,因此有一定風險。配置文件的加載也是一樣,多個節點間不會同步,因此也有一點風險。 至此Cobar的基本特性就是這樣了,更多的高級功能就在逐步的使用中去發現吧。