分表分庫
當項目比較大的時候,基本上都會進行分表分庫的
后面就講講什么時候需要分庫,什么時候需要分表
什么時候需要分庫
垂直分割
垂直拆分就是要把表按模塊划分到不同數據庫表中(當然原則還是不破壞第三范式),這種拆分在大型網站的演變過程中是很常見的。當一個網站還在很小的時候,只有小量的人來開發和維護,各模塊和表都在一起,當網站不斷豐富和壯大的時候,也會變成多個子系統來支撐,這時就有按模塊和功能把表划分出來的需求。其實,相對於垂直切分更進一步的是服務化改造,說得簡單就是要把原來強耦合的系統拆分成多個弱耦合的服務,通過服務間的調用來滿足業務需求看,因此表拆出來后要通過服務的形式暴露出去,而不是直接調用不同模塊的表,淘寶在架構不斷演變過程,最重要的一環就是服務化改造,把用戶、交易、店鋪、寶貝這些核心的概念抽取成獨立的服務,也非常有利於進行局部的優化和治理,保障核心模塊的穩定性
垂直拆分用於分布式場景。
當大團隊在做電商項目的時候,基本上都會將一個項目進行拆分,拆分成n個小項目
這樣做的好處就是,基於逆向服務架構,會拆分多個小項目,每個小項目都有自己單獨的數據庫,這樣的話小項目之間互不影響。
這樣叫做垂直分割。
比如:
會員數據庫、訂單數據庫、支付數據庫等等這樣來分
可以減低開發團隊之間的耦合度。就比如,某個團隊把一個數據庫弄掛了,對另外的團隊基本沒有影響。假如全部用的一個數據庫,是不是全部都掛了,所有用到那個數據庫的團隊項目進度都要延期
什么時候需要分表
水平分割
上面談到垂直切分只是把表按模塊划分到不同數據庫,但沒有解決單表大數據量的問題,而水平切分就是要把一個表按照某種規則把數據划分到不同表或數據庫里。例如像計費系統,通過按時間來划分表就比較合適,因為系統都是處理某一時間段的數據。而像SaaS應用,通過按用戶維度來划分數據比較合適,因為用戶與用戶之間的隔離的,一般不存在處理多個用戶數據的情況,簡單的按user_id范圍來水平切分
為什么需要分表,就比如,一個表,幾十年的數據全部在那個表中,查找,無論你加索引還是怎么,查詢都需要很長的時間。
這個時候就需要做一個分表、分表的規則,一般按照業務需求來定。沒有統一的分法。
比如:
我們的業務場景,主要是存放日志的,日志是需要按照每年存放的
這個時候分表的話,就根據年份來分
再如騰訊的QQ號,根據什么來分表
如果是根據位數,最大的缺點是分部不均勻!
另外如會員系統,通過手機號登錄。會員表中
可以通過手機號前三位分表(有一些項目是這樣做的,沒多大問題),比如136 138 155等,但是都不是怎么均勻
最好通過水平分割(取模算法)來分割
假如我們需要把一個表分成3個表,我們可以把一個是數字的字段,比如int主鍵(userid)。
這個時候,我們可以對表中數據的userid字段對3進行取模,然后對於不同的余數進行分表
這里的取模字段不能用自動增長的
實現取模算法,我們需要有專門的一張表存放對應的userid字段(用來取模的字段)。
在插入行時,先生成id,然后在該表中取出對應的userid,然后賦值過去
是否需要分表,這個依據項目經驗和實際業務情況來的。一般MySQL單表1000W左右的數據是沒有問題的(前提是應用系統和數據庫等層面設計和優化的比較好)
當然,如果需要分表,肯定是需要提前計划半年或者一年計划的。
通俗理解垂直分割和水平分割:水平拆分行,行數據拆分到不同表中, 垂直拆分列,表數據拆分到不同表中
水平分割取模算法案例
使用取模算法分表的最大好處就是,可以非常均勻的分配
首先創建三張表 user0 / user1 /user2 , 然后我再創建 uuid表,該表的作用就是提供自增的id。
創建數據庫: split_horizon
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;
功能就是 注冊分表,以及進行分表查詢
項目很簡單,看下了解下分表是怎么回事就好,
Service代碼
package com.uifuture.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; /** * Created with IntelliJ IDEA. * User: 陳浩翔. * Date: 2018/2/5. * Time: 下午 10:28. * Explain: */ @Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; /** * 注冊的代碼 * @param name * @param pwd * @return */ public String regit(String name,String pwd){ //1.生成userid ,- 先獲取到 自定增長ID String insertUUidSql = "insert into uuid values(null)";//插入空數據,這里的id是自動增長的 jdbcTemplate.update(insertUUidSql);//執行 Long userid = jdbcTemplate.queryForObject("select last_insert_id()", Long.class);//查詢到最近的添加的主鍵id //2.存放具體的那張表中 - 也就是判斷存儲表名稱 String tableName = "user" + userid % 3; //3.插入到具體的表中去- 注冊數據 String insertUserSql = "INSERT INTO " + tableName + " VALUES ('" + userid + "','" + name + "','" + pwd + "');"; System.out.println("insertUserSql:" + insertUserSql); jdbcTemplate.update(insertUserSql); return "success"; } /** * 通過userid查詢name * @param userid * @return */ public String get(Long userid) { //具體哪張表 String tableName = "user" + userid % 3; String sql = "select name from " + tableName + " where id="+userid; System.out.println("SQL:" + sql); return jdbcTemplate.queryForObject(sql, String.class);//執行查詢出name } }
Controller代碼
package com.uifuture.controller; import com.uifuture.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Created with IntelliJ IDEA. * User: 陳浩翔. * Date: 2018/2/5. * Time: 下午 10:44. * Explain: */ @RestController public class UserController { @Autowired private UserService userService; /** * 演示注冊的入口 * @param name * @param pwd * @return */ @RequestMapping("/regit") public String regit(String name, String pwd) { return userService.regit(name, pwd); } /** * 演示獲取name * @param id * @return */ @RequestMapping("/get") public String get(Long id) { return userService.get(id); } }
分表之后也有些缺點。
1.分頁查詢
2.查詢非常受限制
比如我不查詢那個分表的關鍵字段
所以一般會有主表和次表
主表存放所有的數據。次表根據具體業務需求進行分表
還有mycar中間件具有分表功能,可以學學
取模算法的缺點,如果我們的表發生改變,需要我們重新分,很麻煩
(哈哈,可以使用阿里雲的rds雲數據庫,這樣就不用我們關心讀寫分離,分表分庫等等。rds是二次開發的數據庫,所以在性能上來說,比其他的關系型數據庫是快很多的。可以自己去了解下)