分表分庫(kù)
垂直拆分
垂直拆分就是要把表按模塊劃分到不同數(shù)據(jù)庫(kù)表中(當(dāng)然原則還是不破壞第三范式),這種拆分在大型網(wǎng)站的演變過(guò)程中是很常見(jiàn)的。當(dāng)一個(gè)網(wǎng)站還在很小的時(shí)候,只有小量的人來(lái)開(kāi)發(fā)和維護(hù),各模塊和表都在一起,當(dāng)網(wǎng)站不斷豐富和壯大的時(shí)候,也會(huì)變成多個(gè)子系統(tǒng)來(lái)支撐,這時(shí)就有按模塊和功能把表劃分出來(lái)的需求。其實(shí),相對(duì)于垂直切分更進(jìn)一步的是服務(wù)化改造,說(shuō)得簡(jiǎn)單就是要把原來(lái)強(qiáng)耦合的系統(tǒng)拆分成多個(gè)弱耦合的服務(wù),通過(guò)服務(wù)間的調(diào)用來(lái)滿足業(yè)務(wù)需求看,因此表拆出來(lái)后要通過(guò)服務(wù)的形式暴露出去,而不是直接調(diào)用不同模塊的表,淘寶在架構(gòu)不斷演變過(guò)程,最重要的一環(huán)就是服務(wù)化改造,把用戶、交易、店鋪、寶貝這些核心的概念抽取成獨(dú)立的服務(wù),也非常有利于進(jìn)行局部的優(yōu)化和治理,保障核心模塊的穩(wěn)定性
垂直拆分用于分布式場(chǎng)景。
水平拆分
上面談到垂直切分只是把表按模塊劃分到不同數(shù)據(jù)庫(kù),但沒(méi)有解決單表大數(shù)據(jù)量的問(wèn)題,而水平切分就是要把一個(gè)表按照某種規(guī)則把數(shù)據(jù)劃分到不同表或數(shù)據(jù)庫(kù)里。例如像計(jì)費(fèi)系統(tǒng),通過(guò)按時(shí)間來(lái)劃分表就比較合適,因?yàn)橄到y(tǒng)都是處理某一時(shí)間段的數(shù)據(jù)。而像SaaS應(yīng)用,通過(guò)按用戶維度來(lái)劃分?jǐn)?shù)據(jù)比較合適,因?yàn)橛脩襞c用戶之間的隔離的,一般不存在處理多個(gè)用戶數(shù)據(jù)的情況,簡(jiǎn)單的按user_id范圍來(lái)水平切分
通俗理解:水平拆分行,行數(shù)據(jù)拆分到不同表中,垂直拆分列,表數(shù)據(jù)拆分到不同表中
水平分割案例
思路:在大型電商系統(tǒng)中,每天的會(huì)員人數(shù)不斷的增加。達(dá)到一定瓶頸后如何優(yōu)化查詢(xún)。
可能大家會(huì)想到索引,萬(wàn)一用戶量達(dá)到上億級(jí)別,如何進(jìn)行優(yōu)化呢?
使用水平分割拆分?jǐn)?shù)據(jù)庫(kù)表。
如何使用水平拆分?jǐn)?shù)據(jù)庫(kù)
使用水平分割拆分表,具體根據(jù)業(yè)務(wù)需求,有的按照注冊(cè)時(shí)間、取摸、賬號(hào)規(guī)則、年份等。
使用取摸方式分表(使用一致性hash算法,對(duì)userid取模,來(lái)分表)
首先我創(chuàng)建三張表?user0 / user1 /user2 , 然后我再創(chuàng)建?uuid表,該表的作用就是提供自增的id。
create table user0(
id int unsigned primary key ,
name varchar(32) not null default '',
pwd ?varchar(32) not null default '')
engine=myisam charset utf8;
create table user1(
id int unsigned primary key ,
name varchar(32) not null default '',
pwd ?varchar(32) not null default '')
engine=myisam charset utf8;
create table user2(
id int unsigned primary key ,
name varchar(32) not null default '',
pwd ?varchar(32) not null default '')
engine=myisam charset utf8;
create table uuid(
id int unsigned primary key auto_increment)engine=myisam charset utf8;
創(chuàng)建一個(gè)demo項(xiàng)目
POM文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Service代碼
@Service
public?class?UserService {
?
@Autowired
private?JdbcTemplate jdbcTemplate;
?
public?String regit(String name, String pwd) {
// 1.先獲取到 自定增長(zhǎng)ID
String idInsertSQL?= "INSERT INTO uuid VALUES (NULL);";
jdbcTemplate.update(idInsertSQL);
Long insertId?= jdbcTemplate.queryForObject("select last_insert_id()", Long.class);
// 2.判斷存儲(chǔ)表名稱(chēng)
String tableName?= "user"?+ insertId?% 3;
// 3.注冊(cè)數(shù)據(jù)
String insertUserSql?= "INSERT INTO "?+ tableName?+ " VALUES ('"?+ insertId?+ "','"?+ name?+ "','"?+ pwd
+ "');";
System.out.println("insertUserSql:"?+ insertUserSql);
jdbcTemplate.update(insertUserSql);
return?"success";
}
?
public?String get(Long id) {
String tableName?= "user"?+ id?% 3;
String sql?= "select name from "?+ tableName?+ " ?where id="+id;
System.out.println("SQL:"?+ sql);
String name?= jdbcTemplate.queryForObject(sql, String.class);
return?name;
}
?
}
Controller
@RestController
public?class?UserController {
@Autowired
private?UserService userService;
?
@RequestMapping("/regit")
public?String regit(String name, String pwd) {
return?userService.regit(name, pwd);
}
?
@RequestMapping("/get")
public?String get(Long id) {
String name?= userService.get(id);
return?name;
}
?
}
讀寫(xiě)分離:
讀寫(xiě)分離是在優(yōu)化數(shù)據(jù)庫(kù)性能的時(shí)候,常用的技術(shù),因?yàn)樵趯?xiě)數(shù)據(jù)的時(shí)候是會(huì)造成數(shù)據(jù)不安全性能問(wèn)題的,多以比較慢,但是在讀數(shù)據(jù)庫(kù)的時(shí)候是很快的,所以分開(kāi)數(shù)據(jù)能提高性能。
使用數(shù)據(jù)庫(kù)中間件來(lái)實(shí)現(xiàn)分庫(kù)分表:
1、什么是MyCat:?
MyCat是一個(gè)開(kāi)源的分布式數(shù)據(jù)庫(kù)系統(tǒng),是一個(gè)實(shí)現(xiàn)了MySQL協(xié)議的服務(wù)器,前端用戶可以把它看作是一個(gè)數(shù)據(jù)庫(kù)代理,用MySQL客戶端工具和命令行訪問(wèn),而其后端可以用MySQL原生協(xié)議與多個(gè)MySQL服務(wù)器通信,也可以用JDBC協(xié)議與大多數(shù)主流數(shù)據(jù)庫(kù)服務(wù)器通信,其核心功能是分表分庫(kù),即將一個(gè)大表水平分割為N個(gè)小表,存儲(chǔ)在后端MySQL服務(wù)器里或者其他數(shù)據(jù)庫(kù)里。

MyCat發(fā)展到目前的版本,已經(jīng)不是一個(gè)單純的MySQL代理了,它的后端可以支持MySQL、SQL Server、Oracle、DB2、PostgreSQL等主流數(shù)據(jù)庫(kù),也支持MongoDB這種新型NoSQL方式的存儲(chǔ),未來(lái)還會(huì)支持更多類(lèi)型的存儲(chǔ)。而在最終用戶看來(lái),無(wú)論是那種存儲(chǔ)方式,在MyCat里,都是一個(gè)傳統(tǒng)的數(shù)據(jù)庫(kù)表,支持標(biāo)準(zhǔn)的SQL語(yǔ)句進(jìn)行數(shù)據(jù)的操作,這樣一來(lái),對(duì)前端業(yè)務(wù)系統(tǒng)來(lái)說(shuō),可以大幅降低開(kāi)發(fā)難度,提升開(kāi)發(fā)速度
2、MyCat有哪些作用?
目前雖然傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)存在一些列的先天弊端,但NoSQL數(shù)據(jù)庫(kù)又將其替代,但是如果傳統(tǒng)數(shù)據(jù)庫(kù)易于擴(kuò)展和分拆就可以極大的避免單機(jī)單庫(kù)在數(shù)據(jù)增刪改查方面的缺陷。MyCat就是為了解決數(shù)據(jù)庫(kù)的分拆和擴(kuò)展而生的開(kāi)源分布式數(shù)據(jù)庫(kù)系統(tǒng)。其最終的目標(biāo)就是低成本地將現(xiàn)有的單機(jī)數(shù)據(jù)庫(kù)和應(yīng)用平滑遷移到“云”端,解決數(shù)據(jù)存儲(chǔ)和業(yè)務(wù)規(guī)模迅速增長(zhǎng)情況下的數(shù)據(jù)瓶頸問(wèn)題。
舉個(gè)簡(jiǎn)單的例子
如果你只是開(kāi)一個(gè)小賣(mài)鋪(小項(xiàng)目) ,那么你一個(gè)人(數(shù)據(jù)庫(kù))就可以了,但是如果你開(kāi)一個(gè)大型購(gòu)物中心(大項(xiàng)目),如果不分部門(mén)的話,人員(數(shù)據(jù))就很難管理,所以按人員不同分了很多部門(mén)(數(shù)據(jù)庫(kù)),但是光有部門(mén),沒(méi)有一個(gè)統(tǒng)一的管理者(mycat),那么各個(gè)部門(mén)的配合和協(xié)調(diào)能力就大大降低了,超市的健康運(yùn)營(yíng)就會(huì)受到影響。而mycat就是這個(gè)管理者,它是對(duì)數(shù)據(jù)庫(kù)層做一個(gè)抽象,來(lái)管理這些數(shù)據(jù)庫(kù),而最上面的應(yīng)用只需要面對(duì)一個(gè)數(shù)據(jù)庫(kù)層的抽象或者說(shuō)數(shù)據(jù)庫(kù)中間件就好了,這就是Mycat的核心作用。?
所以可以這樣理解:數(shù)據(jù)庫(kù)是對(duì)底層存儲(chǔ)文件的抽象,而Mycat是對(duì)數(shù)據(jù)庫(kù)的抽象。
2.1.一般數(shù)據(jù)庫(kù)的拆表機(jī)制

2.2.MyCat中間件和應(yīng)用層解決方案對(duì)比

3、MyCa的原理
MyCat技術(shù)原理中最重要的一個(gè)動(dòng)詞是“攔截”,它攔截了用戶發(fā)送過(guò)來(lái)的SQL語(yǔ)句,首先對(duì)SQL語(yǔ)句做了一些特定的分析:如分片分析、路由分析、讀寫(xiě)分離分析、緩存分析等,然后將此SQL發(fā)往后端的真實(shí)數(shù)據(jù)庫(kù),并將返回的結(jié)果做適當(dāng)?shù)奶幚?,最終再返回給用戶。
