原理
????????一個數據庫由很多表的構成,每個表對應的不同的業務,垂直切分是指按照業務將表進行分類,分不到不同的數據庫上,這樣壓力就分擔到了不同的庫上面。
數據分片
????????數據分片包括里:垂直分片和水平分片,垂直分片包括:垂直分庫和垂直分表,水平分片包括: 水平分庫和水平分表。
垂直分片
垂直分庫
????????數據庫中不同的表對應著不同的業務,垂直切分是指按照業務的不同將表進行分類,分布到不同的數據庫上面;
????????將數據庫部署在不同服務器上,從而達到多個服務器共同分攤壓力的效果
垂直分表
????????表中字段太多且包含大字段的時候,在查詢時對數據庫的IO、內存會受到影響,同時更新數據時,產生的binlog文件會很大,MySQL在主從同步時也會有延遲的風險。
????????將?個表按照字段分成多表,每個表存儲其中?部分字段。
????????對職位表進?垂直拆分, 將職位基本信息放在?張表, 將職位描述信息存放在另?張表
好處
- 解決業務層面的耦合,業務清晰
- 能對不同業務的數據進行分級管理、維護、監控、擴展等
- 高并發場景下,垂直分庫?定程度的提高訪問性能
- 垂直拆分沒有徹底解決單表數據量過大的問題
水平分片
水平分庫
????????將單張表的數據切分到多個服務器上去,每個服務器具有相應的庫表,只是表中數據集合不同。 水平分庫分表能夠有效的緩解單機和單庫的性能瓶頸和壓力,突破IO、連接數、硬件資源等的瓶頸。
水平分表
針對數據量巨大的單張表(比如訂單表),按照規則把?張表的數據切分到多張表里面去。 但是這些表還是在同?個庫中,所以庫級別的數據庫操作還是有IO瓶頸。
總結
垂直分表: 將?個表按照字段分成多表,每個表存儲其中?部分字段。
垂直分庫: 根據表的業務不同,分別存放在不同的庫中,這些庫分別部署在不同的服務器.
水平分庫: 把?張表的數據按照?定規則,分配到不同的數據庫,每?個庫只有這張表的部分數據.
水平分表: 把?張表的數據按照?定規則,分配到同?個數據庫的多張表中,每個表只有這個表的部分數據。
分庫分表
????????按照?定規則把數據庫中的表拆分為多個帶有數據庫實例,物理庫,物理表訪問路徑的分表。
實現
1.添加數據庫、存儲數據源
/*+ mycat:createDataSource{
"name":"dw0",
"url":"jdbc:mysql://192.168.140.100:3306", "user":"root",
"password":"123123"
} */;
/*+ mycat:createDataSource{
"name":"dr0", "url":"jdbc:mysql://192.168.140.100:3306", "user":"root",
"password":"123123"
} */;
/*+ mycat:createDataSource{ "name":"dw1", "url":"jdbc:mysql://192.168.140.99:3306", "user":"root",
"password":"123123"
} */;
/*+ mycat:createDataSource{ "name":"dr1", "url":"jdbc:mysql://192.168.140.99:3306", "user":"root",
"password":"123123"
} */;
#通過注釋命名添加數據源后,在對應目錄會生成相關配置文件 cd /usr/local/mycat/conf/datasources
如下圖:
2.添加集群配置
把新添加的數據源配置成集群
#//在 mycat 終端輸入
/*! mycat:createCluster{"name":"c0","masters":["dw0"],"replicas":["dr0"]} */;
/*! mycat:createCluster{"name":"c1","masters":["dw1"],"replicas":["dr1"]} */;
#可以查看集群配置信息
cd /usr/local/mycat/conf/clusters
如下圖:
3.創建全局表
#添加數據庫db1 CREATE DATABASE db1;
#在建表語句中加上關鍵字 BROADCAST(廣播,即為全局表) CREATE TABLE db1.`travelrecord` (
`id` bigint NOT NULL AUTO_INCREMENT, `user_id` varchar(100) DEFAULT NULL, `traveldate` date DEFAULT NULL, `fee` decimal(10,0) DEFAULT NULL, `days` int DEFAULT NULL,
`blob` longblob,
PRIMARY KEY (`id`),
KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 BROADCAST; #進入相關目錄查看 schema 配置
vim /usr/local/mycat/conf/schemas/db1.schema.json #可以看到自動生成的全局表配置信息
4.創建分片表(分庫分表)
#在 Mycat 終端直接運行建表語句進行數據分片 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;
#數據庫分片規則,表分片規則,以及各分多少片
INSERT INTO orders(id,order_type,customer_id,amount)
VALUES(1,101,100,100100);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(2,101,100,100300);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(3,101,101,120000);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(4,101,101,103000);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(5,102,101,100400);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(6,102,100,100020);
SELECT * FROM orders;
#同樣可以查看生成的配置信息
#進入相關目錄查看 schema 配置
vim /usr/local/mycat/conf/schemas/db1.schema.json
5.創建ER表
?
上述兩表具有相同的分片算法,但是分片字段不相同 Mycat2 在涉及這兩個表的 join 分片字段等價關系的時候可以完成 join 的下推
常用分片規則
MOD_HASH
如果分片值是字符串則先對字符串進行hash轉換為數值類型
分庫鍵和分表鍵是同鍵
分表下標=分片值%(分庫數量*分表數量)
分庫下標=分表下標/分表數量
分庫鍵和分表鍵是不同鍵
分表下標= 分表分片值%分表數量
分庫下標= 分庫分片值%分庫數量
create table travelrecord (
?....
) ENGINE=InnoDB DEFAULT CHARSET=utf8
dbpartition by MOD_HASH (id) dbpartitions 6
tbpartition by MOD_HASH (id) tbpartitions 6;create table travelrecord (
?....
) ENGINE=InnoDB DEFAULT CHARSET=utf8
dbpartition by MOD_HASH (id) dbpartitions 6
tbpartition by MOD_HASH (id) tbpartitions 6;
RANGE_HASH
RANGE_HASH(字段1, 字段2, 截取開始下標)
僅支持數值類型,字符串類型
當時字符串類型時候,第三個參數生效
計算時候優先選擇第一個字段,找不到選擇第二個字段
如果是字符串則根據下標截取其后部分字符串,然后該字符串hash成數值
根據數值按分片數取余
要求截取下標不能少于實際值的長度
兩個字段的數值類型要求一致
create table travelrecord(
...
)ENGINE=InnoDB DEFAULT CHARSET=utf8
dbpartition by RANGE_HASH(id,user_id,3) dbpartitions 3
tbpartition by RANGE_HASH(id,user_id,3) tbpartitions 3;
RIGHT_SHIFT
RIGHT_SHIFT(字段名,位移數)
僅支持數值類型
分片值右移二進制位數,然后按分片數量取余
create table travelrecord(
?...
)ENGINE=InnoDB DEFAULT CHARSET=utf8
dbpartition by RIGHT_SHIFT(id,4) dbpartitions 3
tbpartition by RIGHT_SHIFT(user_id,4) tbpartitions 3;
UNI_HASH
如果分片值是字符串則先對字符串進行hash轉換為數值類型
分庫鍵和分表鍵是同鍵
分庫下標=分片值%分庫數量
分表下標=(分片值%分庫數量)*分表數量+(分片值/分庫數量)%分表數量
分庫鍵和分表鍵是不同鍵
分表下標= 分片值%分表數量
分庫下標=分片值%分庫數量
create table travelrecord (
?....
) ENGINE=InnoDB DEFAULT CHARSET=utf8
dbpartition by UNI_HASH (id) dbpartitions 6
tbpartition by UNI_HASH (id) tbpartitions 6;
WEEK
僅用于分表
僅DATE/DATETIME
一周之中的星期(1-7)進行取余運算
tbpartitions不超過7
create table travelrecord (
?....
) ENGINE=InnoDB DEFAULT CHARSET=utf8
dbpartition by xxx(xx) dbpartitions 8
tbpartition by WEEK(xx) tbpartitions 7;
YYYYDD
僅用于分庫
DD是一年之中的天數
(YYYY*366+DD)%分庫數
create table travelrecord (
?....
) ENGINE=InnoDB DEFAULT CHARSET=utf8
dbpartition by YYYYDD(xxx) dbpartitions 8
tbpartition by xxx(xxx) tbpartitions 12;
YYYYMM
僅用于分庫:(YYYY*12+MM)%分庫數.MM是1-12
create table travelrecord (
?....
) ENGINE=InnoDB DEFAULT CHARSET=utf8
dbpartition by YYYYMM(xxx) dbpartitions 8
tbpartition by xxx(xx) tbpartitions 12;
??? "travelrecord":{
?? ??? ??? ?"createTableSQL":"CREATE TABLE db1.travelrecord (\n\t`id` bigint(22) NOT NULL\n) ENGINE = InnoDB CHARSET = utf8\nDBPARTITION BY YYYYMM(id) DBPARTITIONS 12",
?? ??? ??? ?"function":{
?? ??? ??? ??? ?"properties":{
?? ??? ??? ??? ??? ?"dbNum":"36",
?? ??? ??? ??? ??? ?"mappingFormat":"prototype/db1/travelrecord_${ 2022+(index.toInteger()-1).intdiv(12) }_${? if(index.toInteger()==0)return 'any';? var i=? (index.toInteger()).mod(12);? if(i==0)return '12'; return i; }",?? ??? ??? ??? ??? ?"storeNum":1,
?? ??? ??? ??? ??? ?"dbMethod":"YYYYMM(id)"
?? ??? ??? ??? ?}
?? ??? ??? ?},
?? ??? ??? ?"shardingIndexTables":{}
?? ??? ?}
DD
僅用于分表
僅DATE/DATETIME
一月中的第幾天(1-31)%分表數
tbpartitions不能超過31
create table travelrecord (
?....
) ENGINE=InnoDB DEFAULT CHARSET=utf8
dbpartition by xxx(xx) dbpartitions 8
tbpartition by DD(xx) tbpartitions 31;
YYYYWEEK
支持分庫分表
(YYYY*54+WEEK)%分片數
WEEK的范圍是1-53
java.time.temporal.WeekFields#weekOfWeekBasedYear
create table travelrecord (
?....
) ENGINE=InnoDB DEFAULT CHARSET=utf8
dbpartition by YYYYWEEK(xx) dbpartitions 8
tbpartition by xxx(xx) tbpartitions 12;
MM
僅用于分表
僅支持DATE/DATETIME
月份(1-12)%分表數
tbpartitions不超過12
create table travelrecord (
?....
) ENGINE=InnoDB DEFAULT CHARSET=utf8
dbpartition by xxx(xxx) dbpartitions 12
tbpartition by MM(xxx) tbpartitions 12;
MMDD
僅用于分表
僅DATE/DATETIME
一年之中第幾天%分表數
tbpartitions不超過366
create table travelrecord (
?....
) ENGINE=InnoDB DEFAULT CHARSET=utf8
dbpartition by xxx(xx) dbpartitions 8
tbpartition by MMDD(xx) tbpartitions 366;