1. 程式人生 > >MyCat自增主鍵

MyCat自增主鍵

mycat

全局序列號是MyCAT提供的一個新功能,為了實現分庫分表情況下,表的主鍵是全局唯一,而默認的MySQL的自增長主鍵無法滿足這個要求。全局序列號的語法符合標準SQL規範,其格式為:

next value for MYCATSEQ_XXX

MYCATSEQ_XXX 是序列號的名字,MyCAT自動創建新的序列號,免去了開發的復雜度。

另外,MyCAT也提供了一個全局的序列號,名稱為:MYCATSEQ_GLOBAL

註意,MYCATSEQ_必須大寫才能正確識別。

MyCAT溫馨提示:實踐中,建議每個表用自己的序列號,序列號的命名建議為MYCATSEQ _tableName_ID_SEQ。

實現方式主要有三種:本地文件方式、數據庫方式、本地時間戳算法。

、本地文件方式:

1、原理:此方式MyCAT將sequence配置到文件中,當使用到sequence中的配置後,MyCAT會更新conf中的sequence_conf.properties文件中sequence當前的值。

2、使用方式:

(1)、配置MyCat的Server.xml

# 其中0,表示使用本地文件方式。

<system>

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

</system>

(2)、配置sequence_conf.properties  

$ vim mycat/conf/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

#其中HISIDS表示使用過的歷史分段(一般無特殊需要可不配置),MINID表示最小ID值,MAXID表示最大ID值,CURID表示當前ID值。

# GLOBAL在這裏也可以使用其他的名字,但必須是大寫的;定義以後可以在全局使用。

#可以使用 mysql>select next value for MYCATSEQ_xxx(自定義的名字,這裏就是MYCATSEQ_ GLOBAL); 來查看下一個自增ID。

設置完成以後重啟MyCat。

3、測試

mysql>create table test(id int,name varchar(20));

mysql>insert into test(id,name) values(next value for MYCATSEQ_GLOBAL,@@hostname);

mysql> select * from test;

4、優缺點

優點:本地加載,讀取速度較快。

缺點:當 MyCAT 重新發布後,配置文件中的 sequence 會恢復到初始值。

、數據庫方式:

1、原理:

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

2、Sequence獲取步驟:

 (1)、第一次使用該sequence時,根據傳入的sequence名稱,從數據庫這張表中讀取current_value,和increment到MyCat中,並將數據庫中的current_value設置為原current_value值+increment值(實現方式是基於後續的存儲函數)。

(2)、MyCat將讀取到current_value+increment作為本次要使用的sequence值,下次使用時,自動加1,當使用increment次後,執行步驟1)相同的操作. MyCat負責維護這張表,用到哪些sequence,只需要在這張表中插入一條記錄即可。若某次讀取的sequence沒有用完,系統就停掉了,則這次讀取的sequence剩余值不會再使用。

3、使用方式:

(1)、配置Server.xml , 其中1,表示使用數據庫方式。

<system>

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

</system>

(2)、設置 sequence_db_conf.properties

 在mycat conf目錄下的sequence_db_conf.properties文件中添加如下內容:

#sequence stored in datanode

GLOBAL=dn2

DICT=dn2

dn2:表示把表和函數都建在了dn2節點上。

註意:GLOBAL 和DICT必須為大寫。

重啟MyCat

(3)、在其中一個分片點對應的數據庫中創建表和存儲函數

因我在schema.xml 中配置的是: <dataNode name="dn$1-4" dataHost="localhost1" database="db$1-4" />

譬如我在dn2中創建,對應的數據庫名為db2(為什麽這裏會涉及到datanode,因為後續的sequence_db_conf.properties文件會使用到)。

註意,登錄到數據庫中創建表和存儲函數,而不是在mycat中創建。   

(3.1)、創建表-- 創建MYCAT_SEQUENCE表

DROP TABLE IF EXISTS MYCAT_SEQUENCE;

CREATE TABLE MYCAT_SEQUENCE (

name VARCHAR(50) NOT NULL,

current_value INT NOT NULL,

increment INT NOT NULL DEFAULT 1,

remark varchar(100), -- remark 並不是必須的,在這裏是為了讓每一個表都對應一個全局的自增,在Remark中配置自增項對應的表名。方便後期維護

PRIMARY KEY(name)) ENGINE=InnoDB;

(3.2)、創建存儲函數1--– 獲取當前sequence的值(返回當前值,增量)

DROP FUNCTION IF EXISTS `mycat_seq_currval`;

DELIMITER ;;

CREATE DEFINER=`root`@`%` FUNCTION `mycat_seq_currval`(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET latin1

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 ;

(3.3)、創建存儲函數2-- 獲取下一個sequence值

DROP FUNCTION IF EXISTS `mycat_seq_nextval`;

DELIMITER ;;

CREATE DEFINER=`root`@`%` FUNCTION `mycat_seq_nextval`(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET latin1

DETERMINISTIC

BEGIN

UPDATE MYCAT_SEQUENCE

SET current_value = current_value + increment WHERE name = seq_name;

RETURN mycat_seq_currval(seq_name);

END

;;

DELIMITER ;

(3.4)、創建存儲函數3--設置sequence值

DROP FUNCTION IF EXISTS `mycat_seq_setval`;

DELIMITER ;;

CREATE DEFINER=`root`@`%` FUNCTION `mycat_seq_setval`(seq_name VARCHAR(50), value INTEGER) RETURNS varchar(64) CHARSET latin1

DETERMINISTIC

BEGIN

UPDATE MYCAT_SEQUENCE

SET current_value = value

WHERE name = seq_name;

RETURN mycat_seq_currval(seq_name);

END

;;

DELIMITER ; 

在表MYCAT_SEQUENCE中,其中:

–name sequence名稱

–current_value 當前value

–increment增長步長! 可理解為mycat在數據庫中一次讀取多少個sequence. 當這些用完後, 下次再從數據庫中讀取.

註意:MYCAT_SEQUENCE必須大寫。

創建存儲函數:

註意:必須在同一個數據庫中創建,在本例中,是db2。一共要創建三個存儲函數。

–獲取當前sequence的值(返回當前值,增量)。

4、插入sequence記錄:

-- 插入sequence記錄

INSERT INTO MYCAT_SEQUENCE(name,current_value,increment,remark) VALUES ('DICT', 1, 100,'match:tb_dic');

INSERT INTO MYCAT_SEQUENCE(name,current_value,increment,remark) VALUES ('GLOBAL', 1, 100,'GLOBAL');

-- 代表插入了一個名為mycat的sequence,當前值為1,步長為100。

mysql> select * from mycat_sequence;

+----------------+---------------+-----------+--------------------------------+

| name | current_value | increment | remark |

+----------------+---------------+-----------+--------------------------------+

| DICT | 1 | 100 | match:tb_dic |

| GLOBAL | 200 | 100 | GLOBAL |

+----------------+---------------+-----------+--------------------------------+

至此,數據庫方面的準備工作已結束完畢。

5、開始測試

$ mysql -h127.0.0.1 -utest -ptest -P8066 -DTESTDB

mysql>

create table tb_dic

(

id int not null auto_increment,

dic_name varchar(100) not null comment '字典名稱',

dic_value varchar(20) not null comment '字典值',

dic_type int not null comment '字典類型:如支付方式等',

primary key (id)

);

# 然後插入值

mysql> INSERT into tb_dic(id,dic_name,dic_value,dic_type) VALUES(next value for MYCATSEQ_DICT,'支付方式','1',0);

+-----+--------------+-----------+----------+

| id | dic_name | dic_value | dic_type |

+-----+--------------+-----------+----------+

| 101 | 活動形式 | 2 | 0 |

| 102 | 表單類型 | 2 | 0 |

+-----+--------------+-----------+----------+

錯誤處理: 

ERROR 1003 (HY000): mycat sequnce err.org.opencloudb.config.util.ConfigException: can't find definition for sequence :DICT

因為對於sequence_db_conf.properties的修改當前的mycat並不知曉,這時候,可重啟mycat或者登錄9066管理端口進行 reload @@config ;

至此,測試完畢,關鍵還是兩點:MYCAT_SEQUENCE必須大寫,sequence_db_conf.properties文件中DICT=dn2必須大寫。

、本地時間戳算法

ID= 64位二進制 (42(毫秒)+5(機器ID)+5(業務編碼)+12(重復累加) 。

換算成十進制為18位數的long類型,每毫秒可以並發12位二進制的累加。

1、配置server.xml

<system>

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

</system>

2、在mycat下配置:sequence_time_conf.properties

WORKID=0-31 任意整數

DATAACENTERID=0-31 任意整數

每個mycat配置的 WORKID,DATAACENTERID不同,組成唯一標識,總共支持32*32=1024種組合。

ID示例:56763083475511

、總結:

1、從MyCAT 1.3開始,支持自增長主鍵,依賴於全局序列號機制,建議采用數據庫方式的全局序列號,並正確設置步長,以免影響實際性能。

首先要開啟數據庫方式的全局序列號,對於需要定義自增長主鍵的表,建立對應的全局序列號,與table名稱同名大寫,

如customer序列名為CUSTOMER,然後再 schema.xml 中對customer表的table元素增加屬性autoIncrement值為true.

<table name=”CUSTOMER” autoIncrement=”true”>

2、應用如何獲得自增主鍵:

MyCAT自增字段和返回生成的主鍵ID的經驗分享

(1)、mysql本身對非自增長主鍵,使用last_insert_id()不會返回結果,只會返回0.

(2)、mysql只會對定義自增長主鍵,可以用last_insert_id()返回主鍵值。

(3)、mycat目前提供了自增長主鍵功能,但是如果對應的mysql節點上數據表,沒有定義auto_increment,那麽在mycat層調用last_insert_id()也是不會返回結果的。

(4)、數據庫方式正確的使用方式如下:

(4.1)、mysql定義自增主鍵
CREATE TABLE `tt2` (
`id` bigINT(10) UNSIGNED NOT NULL AUTO_INCREMENT, //必須是自增的

`nm` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

(4.2)mycat定義自增

[root@test conf]# vim schema.xml

<?xml version="1.0"?>

<!DOCTYPE mycat:schema SYSTEM "schema.dtd">

<mycat:schema xmlns:mycat="http://org.opencloudb/">

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">

<!-- random sharding using mod sharind rule -->

<!-- autoIncrement="true" 屬性-->

<table name="tt2" primaryKey="id" autoIncrement="true" dataNode="dn1,dn2,dn3,dn4" rule="mod-long" />

<table name="mycat_sequence" primaryKey="name" dataNode="dn1"/>

</schema>

<dataNode name="dn1" dataHost="localhost1" database="db1" />

<dataNode name="dn2" dataHost="localhost1" database="db2" />

<dataNode name="dn3" dataHost="localhost1" database="db3" />

<dataNode name="dn4" dataHost="localhost1" database="db4" />

<dataHost name="localhost1" maxCon="1000" minCon="20" balance="1" writeType="1" dbType="mysql" dbDriver="native">

<heartbeat>select user()</heartbeat>

<writeHost host="hostM1" url="127.0.0.1:3306" user="root" password="123456">

</writeHost>

</dataHost>

</mycat:schema>

(4.3)mycat對應sequence_db_conf.properties增加相應設置;並在mycat的對應mycat_sequence增加對應記錄。

(4.4)連接mycat,測試結果如下:

127.0.0.1/root:[TESTDB> insert into tt2(nm) values (99);

Query OK, 1 row affected (0.14 sec)

127.0.0.1/root:[TESTDB> select last_insert_id();

+------------------+

| LAST_INSERT_ID() |

+------------------+

| 101 |

+------------------+

1 row in set (0.01 sec)

(4.5)、關於批量插入使用:

A、使用普通的序列號批量插入 :

insert(a,b,c) values(x,x,x),(x,x,x);

b、使用全局序列號批量插入,必須加註解:

/*!mycat:catlet=demo.catlets.BatchInsertSequence */

insert(a,b,c) values(x,x,x),(x,x,x);

c、是sharding key 必須包含在列枚舉中,特別是主鍵是自增的時候必須顯示調用:

/*!mycat:catlet=demo.catlets.BatchInsertSequence */

insert(id,a,b,c) values(next value for MYCATSEQ_ID,x,x,x),(next value for MYCATSEQ_ID,x,x,x);


MyCat自增主鍵