一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分布到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面,如下图:
系统被拆分为用户、订单、支付多个模块,部署在不同机器上。
分库的原则:由于跨库不能关联查询,所以有紧密关联的表应当放在一个数据库中,相互没有关联的表可以分不到不同的数据库。
eg:
客户表 customer rows 20W
订单表 orders rows 600W
订单详情表 orders_detail rows 600W
订单状态字典表 dic_order_type rows 600W
分库方案:将客户表单独分到一个数据库,订单相关联的表放到一个数据库。
分表字段 | 效果 |
---|---|
ID(主键、创建时间) | 查询订单注重实效,历史订单查询次数少。带来的问题是会造成一个时间段分片数据多则该节点访问多,另一个访问少。分片不均匀。 |
customer_id(客户ID) | 根据客户ID分片,俩个节点平均访问,一个客户的订单都在一个库中,查询历史订单较快 |
执行注释脚本,创建集群配置
/*! mycat:createCluster{
"name":"prototype",
"masters":["rwSepw1"],
"replicas":["rwSepr1"]
} */
也可以修改配置文件
{
"clusterType":"MASTER_SLAVE",
"heartbeat":{
"heartbeatTimeout":1000,
"maxRetryCount":3,
"minSwitchTimeInterval":300,
"showLog":false,
"slaveThreshold":0.0
},
"masters":[
"rwSepw1"
],
"maxCon":2000,
"name":"prototype",
"readBalanceType":"BALANCE_ALL",
"replicas":[
"rwSepr1"
],
"switchType":"SWITCH"
}
注意:如果MySQL服务没有启动的话,需要注释掉mycat\conf\datasource下的数据源文件再启动,否则报错会导致mycat启动失败。
mycat2分库分表的优势:可以在终端直接使用注释脚本
创建数据源、集群、库存,并在创建时指定分库、分表。
-- 添加数据源dw1-13306、dw2-23306、dr1-33306、dr2-43306
/*+ mycat:createDataSource{
"name":"dw1",
"url":"jdbc:mysql://127.0.0.1:13306/db1?useSSL=false&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true",
"user":"root",
"password":"admin"
}*/;
-- 查看
/*+ mycat:showDataSources{} */
/*! mycat:createCluster{
"name":"c1",
"masters":["dw1"],
"replicas":["dr1"]
} */
-- 查看
/*! mycat:showClusters{} */
关键字:BROADCAST
create table db1. `dic_order_type` (
id int not null auto_increment,
dcode varchar(100) not null,
dname varchar(100) not null,
primary key(id)
)engine=InnoDB default charset=utf8 BROADCAST;
-- 这里我们加上关键字 BROADCAST,即可将创建的表标识为广播表。
mycat\conf\schemas\db1.schema.json
已经发生了变化。globalTables增加了dic_order_type表配置,其中的broadcast配置了两个targetName。{
"customTables":{},
"globalTables":{
"dic_order_type":{
"broadcast":[
{
"targetName":"c1"
},
{
"targetName":"c2"
}
],
"createTableSQL":"/* ApplicationName=DBeaver 22.3.1 - SQLEditor */\nCREATE TABLE db1.`dic_order_type` (\n\tid int NOT NULL AUTO_INCREMENT,\n\tdcode varchar(100) NOT NULL,\n\tdname varchar(100) NOT NULL,\n\tPRIMARY KEY (id)\n) BROADCAST ENGINE = InnoDB CHARSET = utf8" ,
"sequenceType":"NO_SEQUENCE"
}
},
"normalProcedures":{},
"normalTables":{},
"schemaName":"db1",
"shardingTables":{},
"views":{}
}
关键语法:
dbpartition BY mod_hash(customer_id) tbpartition BY mod_hash(customer_id) tbpartitions 1 dbpartitions 2;
CREATE TABLE db1.orders(
id BIGINT NOT NULL AUTO_INCREMENT,
order_type INT,
customer_id INT,
amount DECIMAL(10,2),
PRIMARY KEY(id),
KEY id(id)
)ENGINE=INNODB DEFAULT CHARSET=utf8
dbpartition BY mod_hash(customer_id) tbpartition BY mod_hash(customer_id)
tbpartitions 1 dbpartitions 2;
-- dbpartition 设置数据库分片规则;tbpartition 设置表分片规则
-- tbpartitions 1 dbpartitions 2 配置分片数。表分成一片,数据库分成两片(即两个数据库各分1片)
注意:这里我在执行的时候遇到了一个问题,就是报错说c0集群can not found。是由于我在之前准备环境的时候准备的c1,和c2。这里我们把c2改成c0即可。
-- 新增数据
INSERT INTO db1.orders values(1,101,100,2010);
INSERT INTO db1.orders values(2,101,100,2020);
INSERT INTO db1.orders values(3,101,101,2030);
INSERT INTO db1.orders values(4,101,101,2040);
INSERT INTO db1.orders values(5,102,101,2050);
INSERT INTO db1.orders values(6,102,100,2060);
`--> mysql db1_0.orders_0
mycat db1.orders
`--> mysql db1_1.orders_1
mycat\conf\schemas\db1.schema.json
,可见shardingTables中新增了配置信息{
"customTables":{},
"globalTables":{},
"normalProcedures":{},
"normalTables":{},
"schemaName":"db1",
"shardingTables":{
"orders":{
"createTableSQL":"/* ApplicationName=DBeaver 22.3.1 - SQLEditor */\nCREATE TABLE db1.orders (\n\tid BIGINT NOT NULL AUTO_INCREMENT,\n\torder_type INT,\n\tcustomer_id INT,\n\tamount DECIMAL(10, 2),\n\tPRIMARY KEY (id),\n\tKEY id (id)\n) ENGINE = INNODB CHARSET = utf8\nDBPARTITION BY mod_hash(customer_id) DBPARTITIONS 2\nTBPARTITION BY mod_hash(customer_id) TBPARTITIONS 1" ,
"function":{
"properties":{
"dbNum":"2",
"mappingFormat":"c${targetIndex}/db1_${dbIndex}/orders_${index}",
"tableNum":"1",
"tableMethod":"mod_hash(customer_id)",
"storeNum":2,
"dbMethod":"mod_hash(customer_id)"
}
},
"shardingIndexTables":{}
}
},
"views":{}
}
-- 这里我们并没有写orders_detail与orders的关系,但是mycat会自动的将这张表与orders分到一个库中。
CREATE TABLE orders_detail(
id BIGINT NOT NULL AUTO_INCREMENT,
detail VARCHAR(2000),
order_id INT,
PRIMARY KEY(id)
)ENGINE=INNODB DEFAULT CHARSET=utf8
dbpartition BY mod_hash(order_id) tbpartition BY mod_hash(order_id)
tbpartitions 1 dbpartitions 2;
INSERT INTO orders_detail VALUES(1,'1111',1);
INSERT INTO orders_detail VALUES(2,'2222',2);
INSERT INTO orders_detail VALUES(3,'3333',3);
INSERT INTO orders_detail VALUES(4,'4444',4);
INSERT INTO orders_detail VALUES(5,'5555',5);
INSERT INTO orders_detail VALUES(6,'6666',6);
select * from orders o;
id|order_type|customer_id|amount |id0|detail|order_id|
--+----------+-----------+-------+---+------+--------+
1| 101| 100|2010.00| 1|1111 | 1|
2| 101| 100|2020.00| 2|2222 | 2|
3| 101| 101|2030.00| 3|3333 | 3|
4| 101| 101|2040.00| 4|4444 | 4|
5| 102| 101|2050.00| 5|5555 | 5|
6| 102| 100|2060.00| 6|6666 | 6|
SELECT * FROM orders o ;
SELECT * FROM orders_detail od;
SELECT * FROM orders o
LEFT JOIN orders_detail od ON o.id = od.order_id ;
但俩个表具有相同的分片算法,但是分片字段不同。
mycat2在涉及这种两个表join分片字段等价关系的时候可以完成join的下推。
其无需指定ER表,是自动识别的,具体看分片算法的接口。我们可以用/*+ mycat:showErGroup{} */
脚本来查看配置的表是否具有ER关系。
其中groupId表示相同的分组,在该分组的表具有相同的存储分布。
/*+ mycat:showErGroup{} */
groupId|schemaName|tableName |
-------+----------+-------------+
0 |db1 |orders |
0 |db1 |orders_detail|
c0就是分片表第一个节点,c1就是第二个节点
,该命名规则允许用户手动改变。分片算法 | 描述 | 分库 | 分表 | 数值类型 |
---|---|---|---|---|
MOD_HASH | 取模哈希 | 是 | 是 | 数值,字符串 |
UNI_HASH | 取模哈希 | 是 | 是 | 数值,字符串 |
RIGHT_SHIFT | 右移哈希 | 是 | 是 | 数值 |
RANGE_HASH | 两字段其一取模 | 是 | 是 | 数值,字符串 |
YYYYMM | 按年月哈希 | 是 | 是 | DATE,DATETIME |
YYYYDD | 按年月哈希 | 是 | 是 | DATE,DATETIME |
YYYYWEEK | 按年周哈希 | 是 | 是 | DATE,DATETIME |
HASH | 取模哈希 | 是 | 是 | 数值,字符串,如果不是,则转换成字符串 |
MM | 按月哈希 | 否 | 是 | DATE,DATETIME |
DD | 按日期哈希 | 否 | 是 | DATE,DATETIME |
MMDD | 按月日哈希 | 是 | 是 | DATE,DATETIME |
WEEK | 按周哈希 | 否 | 是 | DATE,DATETIME |
STR_HASH | 字符串哈希 | 是 | 是 | 字符串 |
[数据分片]hash形式的分片算法。如果分片键是字符串,会将字符串hash转换为数值类型。
[数据分片]hash形式的分片算法。仅支持数值类型。
分片值右移两位,按分片数量取余。
[数值分片]hash形式的分片算法。仅用于分库。
(YYYY*12+MM)%分库数量,MM为1–12。
仅用于分表。仅DATE、DATETIME类型。
一年之中第几天%分表数。tbpartitions不能超过366。
Mycat2默认使用雪花算法作为全局序列。如果不需要mycat默认的全局序列,可以通过配置关闭自动全局序列。
如果不需要自增序列,则修改mycat配置文件中的建表SQL,取消AUTO_INCREMENT
关键字即可。需要则配置上即可。
这样mycat则不认为该字段为自增字段,而会交给mysql的自增主键功能补全自增键。
雪花算法:引入了时间戳和ID保持自增的分布式ID生成算法。
conf\dbseq.sql
文件。mycat\conf\sequences
目录,添加配置文件{数据库名}_{表名}.sequence.json
{
"clazz": "io.mycat.plug.sequence.SequenceMySQLGenerator",
"name": "db1_tableName",
"targetName": "prototype",
"schemaName": "db1"
}
可选参数targetName更改序列号服务器,targetName是执行自增序列的节点,也是dbseq.sql导入的节点。其导入的当前库的苦命与逻辑表的逻辑库名一致。
导入后可以检查库下是否有mycat_sequence表。在有多个mycat做集群的时候需要修改increment值,不能严格的让其自增。Name列的值是对应的库名_表名
,需要自己设置,即插入一条数据,用于记录序列号。
/*+ mycat:setSequence {
"name": "db1_tableName",
"clazz": "io.mycat.plug.sequence.SequenceMySQLGenerator",
"targetName": "prototype",
"schemeName": "db1"
}*/
/*+ mycat:setSequence {
"name": "db1_tableName",
"time": true
}*/