1. 程式人生 > 其它 >MySQL之分庫分表

MySQL之分庫分表

一、前言

分庫分表需要應對的技術難題有如下幾個:

1. 分散式全域性唯一id

2. 分片規則和策略

3. 跨分片技術問題

4. 跨分片事物問題

下面我們來看一下Mycat是如何解決分散式全域性唯一id的問題的

二、Mycat全域性序列號

Mycat保證id唯一的方式有如下幾個:

1)本地檔案方式

2)資料庫方式

3)時間戳方式

4)ZKID生成器

5)ZK遞增ID

推薦使用第4,5種

以上5中方式都要統一在server.xml檔案中開啟全域性序列號的配置和在schema.xml檔案中配置邏輯表的autoIncrement屬性為true(2個必須步驟)

1)sequnceHandlerType進行相應全域性序列號策略選項設定(server.xml),在mycat中對應的原始碼是MyCATSequnceProcessor.java

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

  sequnceHandlerType可取的值有以下幾個:

    0:本地檔案方式
    1:資料庫方式
    2:時間戳方式
    3:ZKID生成器
    4:ZK遞增ID

2)autoIncrement屬性為true(schema.xml)

<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3"  rule="mod-long" />

1. 本地檔案方式

使用到的mycat原始碼:io.mycat.route.sequence.handler.IncrSequenceHandler

在server.xml檔案中開啟全域性序列號的配置:

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

使用到的配置檔案:sequence_conf.properties

#default global sequence
GLOBAL.HISIDS=
GLOBAL.MINID=10001
GLOBAL.MAXID=20000
GLOBAL.CURID=10000

# self define sequence
COMPANY.HISIDS=
COMPANY.MINID=1001
COMPANY.MAXID=2000
COMPANY.CURID=1000

CUSTOMER.HISIDS=
CUSTOMER.MINID=1001
CUSTOMER.MAXID=2000
CUSTOMER.CURID=1000

ORDER.HISIDS=
ORDER.MINID=1001
ORDER.MAXID=2000
ORDER.CURID=1000

HOTNEWS.HISIDS=
HOTNEWS.MINID=1001
HOTNEWS.MAXID=2000
HOTNEWS.CURID=1000

拿HOTNEWS這個表的配置來說明:

HOTNEWS.HISIDS=       #HOTNEWS這張表歷史使用的自增id,一般不配置
HOTNEWS.MINID=1001    #HOTNEWS這張表使用的最小自增id
HOTNEWS.MAXID=2000    #HOTNEWS這張表使用的最大自增id
HOTNEWS.CURID=1000    #HOTNEWS這張表當前使用的自增id

缺點:當Mycat重新發布後,自增ID恢復到初始值。原因是因為sequnceHandlerType定義的是靜態變數,不推薦使用

示例:

1.1 在mycat的schema.xml檔案裡面分別配置邏輯表hotnews和mysql的主從機,注意autoIncrement="true"才能使用全域性唯一id

<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="mod-long" />
<dataNode name="dn1" dataHost="centos1" database="db1" />
        <dataNode name="dn2" dataHost="centos1" database="db2" />
        <dataNode name="dn3" dataHost="centos1" database="db3" />
        <dataHost name="centos1" 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="192.168.152.130:3306" user="root" password="123456">
                        <!-- can have multi read hosts -->
                        <readHost host="hostS2" url="192.168.152.131:3306" user="root" password="123456" />
                </writeHost>
                <!-- <writeHost host="hostS1" url="localhost:3316" user="root"
                                   password="123456" />-->
                <!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>

1.2 在主資料庫(192.168.152.130)分別建立3個數據庫db1,db2,db3,然後建立hotnews表

create database db1;
use db1;
create table hotnews(
   id bigint(20) not null primary key auto_increment,
    title varchar(50) default null

);
create database db2;
use db2;
create table hotnews(
   id bigint(20) not null primary key auto_increment,
    title varchar(50) default null

);
create database db3;
use db3;
create table hotnews(
   id bigint(20) not null primary key auto_increment,
    title varchar(50) default null

);

1.3 插入資料

插入資料到hotnews前我們先來看一下使用本地檔案方式的配置檔案sequence_conf.properties的內容

cat sequence_conf.properties |grep HOTNEWS

插入資料到hotnews

insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, 'test1');
insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, 'test2');
insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, 'test3');

1.4 檢視hotnews表的結果

select * from hotnews;

再次檢視配置檔案sequence_conf.properties的內容,發現內容隨著插入資料的自增id做了改變

2. 資料庫方式

這種方式和本地檔案的方式是一樣的,只是把sequence_conf.properties的內容用資料庫來管理

使用到的mycat原始碼:io.mycat.route.sequence.handler.IncrSequenceMySQLHandler

這裡還是以hotnews表為例

2.1 在server.xml檔案中開啟全域性序列號的配置:

<property name="sequnceHandlerType">1</property>

使用到的配置檔案:sequence_db_conf.properties

vim sequence_db_conf.properties
#sequence stored in datanode
GLOBAL=dn1
COMPANY=dn1
CUSTOMER=dn1
ORDERS=dn1

2.2 選擇其中的一個分片,執行如下步驟,譬如我在dn1中建立,對應的資料庫名為db1(為什麼這裡會涉及到datanode,因為後續的sequence_db_conf.properties檔案會使用到),注意:是登入到資料庫中建立,而不是在mycat中建立

第一步:建立SEQUENCE表,用來儲存序列號

use db1;
DROP TABLE IF EXISTS MYCAT_SEQUENCE;
CREATE TABLE MYCAT_SEQUENCE (
NAME VARCHAR (50) NOT NULL, /*全域性SEQ名稱*/
current_value INT NOT NULL, /*當前序列ID*/
increment INT NOT NULL DEFAULT 100, /*初始序列ID*/
PRIMARY KEY (NAME)
) ENGINE = INNODB ;
INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ('GLOBAL', 100000, 100);

第二步:建立SEQ function

-- 獲取當前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_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 ;

-- 設定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 ;

插入需要自增長的表的策略,這條資料是我們hotnews這個表所需要的。 name必須是大寫的字元,不然就會報錯

insert into MYCAT_SEQUENCE values('HOTNEWS','101','100');

說明:插入了一個名為HOTNEWS的sequence,當前值為101,步長為100。當插入第一條資料時id為201,後面每插入一條資料id加1

第三步:在sequence_db_conf.properties這個檔案中定義hotnews這張表的序列名稱,同時可以定義到哪個分片上。這裡是定義在dn1上的

      名字=分片1[,分片2][,.....][,分片N]

vim sequence_db_conf.properties

儲存:

:wq

2.3 重啟mycat

[root@centos1 mycat]# ./bin/mycat restart;

2.4 連線mycat進行資料測試

mysql -uroot -p123456 -P8066 -h192.168.152.128

插入資料

insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, 'test11111');
insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, 'test11112');
insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, 'test11113');

檢視結果:

select * from hotnews;

缺點:當mycat掛掉時可能出現主鍵衝突。不推薦使用

3.本地時間戳方式

使用到的mycat原始碼:io.mycat.route.sequence.handler.IncrSequenceTimeHandler

3.1 在server.xml檔案中開啟全域性序列號的配置:

<property name="sequnceHandlerType">2</property>

使用到的配置檔案:

sequence_time_conf.properties

          WORKID=01(範圍01-31)

          DATAACENTERID=01(範圍01-31)

示例:

首先清空hotnews這張表的資料,方便檢視測試結果

delete from hotnews;

插入測試資料

insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test11111'); 
insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test11112'); 
insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test11113'); 

檢視結果:

select * from hotnews;

優點:不存在id重複的現象。

缺點:主鍵太長。時間可能被重置。沒有特殊要求的場景可以使用

4.自增長主鍵

方式1:不同自增長初始值+相同步長

方式2:參考“資料庫方式”

5.分散式ZK ID生成(推薦使用)

使用到的mycat原始碼:

io.mycat.route.sequence.handler.IncrSequenceZKHandler

io.mycat.route.sequence.handler.DistributedSequenceHandler

環境準備:先在虛擬機器192.168.152.130裡面裝好zookeeper,具體參考我的文章搭建dubbo+zookeeper+dubboadmin分散式服務框架(windows平臺下)

5.1 在server.xml檔案中開啟全域性序列號的配置:

vim server.xml

<property name="sequnceHandlerType">3</property>

5.2 修改如下配置檔案:

1)myid.properties

vim myid.properties

配置檔案說明:

loadZK=true|false //是否使用zk序列生成器

zkURL=xxx.xxx.xxx.xxx:2182,xxx.xxx.xxx.xxx:2182,xxx.xxx.xxx.xxx:2182

clusterId=叢集名稱

2)sequence_distributed_conf.properties

vim sequence_distributed_conf.properties

配置檔案說明:

INSTANCEID=ZK //改成“ZK”(預設是01)

CLUSTERID=01 //叢集編號

5.3 重啟mycat

[root@centos1 mycat]# ./bin/mycat restart;

5.4 連線mycat進行資料測試

mysql -uroot -p123456 -P8066 -h192.168.152.128

插入資料

insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test00000001');
insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test00000002');
insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test00000003');

檢視結果:

select * from hotnews;

6. ZK遞增方式(推薦使用)

使用到的mycat原始碼:io.mycat.route.sequence.handler.IncrSequenceZKHandler

6.1 在server.xml檔案中開啟全域性序列號的配置:

vim server.xml

<property name="sequnceHandlerType">4</property>

使用到的配置檔案:

1) myid.properties 參考“5. 分散式ZK ID生成器”中的介紹。

2)sequence_conf.properties

HOTNEWS.HISIDS=       #HOTNEWS這張表歷史使用的自增id,一般不配置
HOTNEWS.MINID=1001    #zk使用的區間內最小值
HOTNEWS.MAXID=2000    #zk使用的區間內最大值
HOTNEWS.CURID=1000    #zk使用的區間內當前值

說明:

以hotnews為例,然後根據sequence_conf.properties裡面的hotnews的配置取MAXID和MINID的偏移量(這裡是1000)作為id的增量值,當插入資料時把這1000個值用完了在繼續取MAXID和MINID的偏移量

6.2 重啟mycat

[root@centos1 mycat]# ./bin/mycat restart;

6.3 連線mycat進行資料測試

mysql -uroot -p123456 -P8066 -h192.168.152.128

插入資料

insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test00000001');
insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test00000002');
insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test00000003');

檢視結果:

select *  from hotnews;