亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

前言

隨著系統(tǒng)數(shù)據(jù)量的日益增長,在說起數(shù)據(jù)庫架構(gòu)和數(shù)據(jù)庫優(yōu)化的時(shí)候,我們難免會(huì)常常聽到分庫分表這樣的名詞。

當(dāng)然,分庫分表有很多的方法論,比如垂直拆分、水平拆分;也有很多的中間件產(chǎn)品,比如MyCat、ShardingJDBC。

根據(jù)業(yè)務(wù)場(chǎng)景選擇合適的拆分方法,再選擇一個(gè)熟悉的開源框架,就能幫助我們完成項(xiàng)目中所涉及到的數(shù)據(jù)拆分工作。

本文并不打算就這些方法論和開源框架展開深入的探討,筆者想討論另外一個(gè)場(chǎng)景:

如果系統(tǒng)中需要拆分的表并不多,只是1個(gè)或者少量的幾個(gè),我們是否值得引入一些相對(duì)復(fù)雜的中間件產(chǎn)品;特別是,如果我們對(duì)它們的原理不甚了解,是否有信心駕馭它們 ?

基于此,如果你的系統(tǒng)中有少量的表需要拆分,也沒有專門的資源去研究開源組件,那么我們可以自己來實(shí)現(xiàn)一個(gè)簡(jiǎn)單的分庫分表插件;當(dāng)然,如果你的系統(tǒng)比較復(fù)雜,業(yè)務(wù)量較大,還是采用開源組件或者團(tuán)隊(duì)自研組件來解決這事較為穩(wěn)妥。

分庫分表簡(jiǎn)單?那我想問如何實(shí)現(xiàn)“分庫分表插件”?

 

一、原理

分庫分表這事說簡(jiǎn)單也簡(jiǎn)單,說復(fù)雜那也挺復(fù)雜...

簡(jiǎn)單是因?yàn)樗暮诵牧鞒瘫容^明確。就是解析SQL語句,然后根據(jù)預(yù)先配置的規(guī)則,重寫或路由到真實(shí)的數(shù)據(jù)庫表中去;

復(fù)雜在于,SQL語句復(fù)雜且靈活,比如分頁、去重、排序、分組、聚合、關(guān)聯(lián)查詢等操作,如何正確的解析它們。

所以就算是ShardingJDBC,在官網(wǎng)中也明確了支持項(xiàng)和不支持項(xiàng)。

二、注解式配置

相對(duì)于復(fù)雜的配置文件,我們采用較為輕便的注解式配置,它的定義如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public%20@interface%20Sharding%20{
%20%20%20%20String%20tableName();%20%20%20%20%20//邏輯表名
%20%20%20%20String%20field();%20%20%20%20%20%20%20%20%20//分片鍵
%20%20%20%20String%20mode();%20%20%20%20%20%20%20%20%20%20//算法模式
%20%20%20%20int%20length()%20default%200;%20//分表數(shù)量
}

那么,在哪里使用它呢%20?%20比如我們的用戶表需要分表,那就在User這個(gè)實(shí)體對(duì)象上標(biāo)注。

@Data
@Sharding(tableName%20=%20"user",field%20=%20"id",mode%20=%20"hash",length%20=%2016)
public%20class%20User%20{
%20%20%20%20private%20Long%20id;
%20%20%20%20private%20String%20name;
%20%20%20%20private%20String%20address;
%20%20%20%20private%20String%20tel;
%20%20%20%20private%20String%20email;
}

這就說明了,我一共有%2016%20張用戶表,根據(jù)用戶ID,使用Hash算法來計(jì)算它的位置。

當(dāng)然,我們不止有Hash算法,還可以根據(jù)日期范圍來定義。

@Data
@Sharding(tableName%20=%20"car",field%20=%20"creatTime",mode%20=%20"range")
public%20class%20Car%20{
%20%20%20%20private%20long%20id;
%20%20%20%20private%20String%20number;
%20%20%20%20private%20String%20brand;
%20%20%20%20private%20String%20creatTime;
%20%20%20%20private%20long%20userId;
}

三、分片算法

在這里,筆者實(shí)現(xiàn)了兩種分片方式,就是HashAlgorithm和RangeAlgorithm%20。

1、范圍分片

如果你的系統(tǒng)中有使用冷熱數(shù)據(jù)分離,我們可以按照日期將不同月的數(shù)據(jù)分散到不同的表中。

比如車輛的創(chuàng)建時(shí)間是2019-12-10%2015:30:00,這條數(shù)據(jù)將會(huì)被分配到car_201912這張表中去。

我們通過截取時(shí)間的年月部分,然后再加上邏輯表名即可。

public%20class%20RangeAlgorithm%20implements%20Algorithm%20{
%20%20%20%20@Override
%20%20%20%20public%20String%20doSharding(String%20tableName,%20Object%20value,int%20length)%20{
%20%20%20%20%20%20%20%20if%20(value!=null){
%20%20%20%20%20%20%20%20%20%20%20%20try{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20DateUtil.parseDateTime(value.toString());
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20String%20replace%20=%20value.toString().substring(0,%207).replace("-",%20"");
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20String%20newName%20=%20tableName+"_"+replace;
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20newName;
%20%20%20%20%20%20%20%20%20%20%20%20}catch%20(DateException%20ex){
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20logger.error("時(shí)間格式不符合要求!傳入?yún)?shù):{},正確格式:{}",value.toString(),"yyyy-MM-dd%20HH:mm:ss");
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20tableName;
%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20return%20tableName;
%20%20%20%20}
}

2、Hash分片

在Hash分片算法中,我們可以先判斷表的數(shù)量,是不是2的冪次方。如果不是,就通過算數(shù)方式獲取下標(biāo),如果是呢,就通過位運(yùn)算的方式獲取下標(biāo)。當(dāng)然了,這是在HashMap源碼中學(xué)到的哦。

public%20class%20HashAlgorithm%20implements%20Algorithm%20{
%20%20%20%20@Override
%20%20%20%20public%20String%20doSharding(String%20tableName,%20Object%20value,int%20length)%20{
%20%20%20%20%20%20%20%20if%20(this.isEmpty(value)){
%20%20%20%20%20%20%20%20%20%20%20%20return%20tableName;
%20%20%20%20%20%20%20%20}else{
%20%20%20%20%20%20%20%20%20%20%20%20int%20h;
%20%20%20%20%20%20%20%20%20%20%20%20int%20hash%20=%20(h%20=%20value.hashCode())%20^%20(h%20>>>%2016);
%20%20%20%20%20%20%20%20%20%20%20%20int%20index;
%20%20%20%20%20%20%20%20%20%20%20%20if%20(is2Power(length)){
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20index%20=%20(length%20-%201)%20&%20hash;
%20%20%20%20%20%20%20%20%20%20%20%20}else%20{
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20index%20=%20Math.floorMod(hash,%20length);
%20%20%20%20%20%20%20%20%20%20%20%20}
%20%20%20%20%20%20%20%20%20%20%20%20return%20tableName+"_"+index;
%20%20%20%20%20%20%20%20}
%20%20%20%20}
}

四、攔截器

配置和分片算法都有了,接下來就是重頭戲了。在這里,我們使用Mybatis攔截器將它們派上用場(chǎng)。

常年CRUD的我們,都知道一條業(yè)務(wù)SQL肯定逃不出它們的范圍。其中,在業(yè)務(wù)上我們的刪除功能一般都是邏輯刪除,所以,基本上不會(huì)有DELETE操作。

相較而言,新增和修改SQL都比較簡(jiǎn)單且格式固定,查詢SQL往往比較靈活且復(fù)雜。所以,在這里筆者定義了兩個(gè)攔截器。

不過,在介紹攔截器之前,我們有理由要了解另外兩個(gè)東西:SQL語法解析器和分片算法處理器。

1、JSqlParser

JSqlParser負(fù)責(zé)解析SQL語句,并轉(zhuǎn)化為JAVA類的層次結(jié)構(gòu)。我們可以先看個(gè)簡(jiǎn)單的例子來認(rèn)識(shí)它。

public%20static%20void%20main(String[]%20args)%20throws%20JSQLParserException%20{

	String%20insertSql%20=%20"insert%20into%20user%20(id,name,age)%20value(1001,'范閑',20)";
	Statement%20parse%20=%20CCJSqlParserUtil.parse(insertSql);
	Insert%20insert%20=%20(Insert)%20parse;

	String%20tableName%20=%20insert.getTable().getName();
	List<Column>%20columns%20=%20insert.getColumns();
	ItemsList%20itemsList%20=%20insert.getItemsList();
	System.out.println("表名:"+tableName+"%20列名:"+columns+"%20屬性:"+itemsList);
}
輸出:%20表名:user%20列名:[id,%20name,%20age]%20屬性:(1001,%20'范閑',%2020)

我們可以看到,JSqlParser可以解析出SQL的語法信息。相應(yīng)的,我們也可以更改對(duì)象內(nèi)容,從而達(dá)到修改SQL語句的目的。

2、算法處理器

我們的分片算法有多個(gè),具體應(yīng)該調(diào)用哪一個(gè)是在程序運(yùn)行期來決定的。所以,我們使用一個(gè)Map先將算法注冊(cè)起來,然后根據(jù)分片模式來調(diào)用它。這也是策略模式的體現(xiàn)。

@Component
public%20class%20AlgorithmHandler%20{
%20%20%20%20private%20Map<String,%20Algorithm>%20algorithm%20=%20new%20HashMap<>();
%20%20%20%20@PostConstruct
%20%20%20%20public%20void%20init(){
%20%20%20%20%20%20%20%20algorithm.put("range",new%20RangeAlgorithm());
%20%20%20%20%20%20%20%20algorithm.put("hash",new%20HashAlgorithm());
%20%20%20%20}
%20%20%20%20public%20String%20handler(String%20mode,String%20name,Object%20value,int%20length){
%20%20%20%20%20%20%20%20return%20algorithm.get(mode).doSharding(name,%20value,length);
%20%20%20%20}
}

3、攔截器

我們知道,MyBatis允許你在已映射語句執(zhí)行過程中的某一點(diǎn)進(jìn)行攔截調(diào)用。

如果你對(duì)它的原理還不熟悉,那么可以先看看筆者的文章:Mybatis攔截器的原理。

整體來看,它的流程如下:

  • 通過Mybatis攔截待執(zhí)行的SQL;
  • 通過JSqlParser解析SQL,獲取邏輯表名等;
  • 調(diào)用分片算法獲取真實(shí)表名;
  • 修改SQL,并修改BoundSql;
  • Mybatis執(zhí)行修改后的SQL,達(dá)成目的。

比如,對(duì)于insert語句和update語句,它的核心代碼如下:

 

五、查詢及分頁

事實(shí)上,新增和修改都比較簡(jiǎn)單,較為復(fù)雜的是查詢語句。

但是,我們的插件并不在于要滿足所有的查詢語句,而是可以根據(jù)真實(shí)的業(yè)務(wù)場(chǎng)景來擴(kuò)展修改。

不過分頁功能基本上是逃不開的。拿PageHelper為例,它的原理也是通過Mybatis攔截器來實(shí)現(xiàn)的。如果它和我們的分表插件在一起,可能會(huì)產(chǎn)生沖突。

所以在分表插件中,筆者也集成了分頁功能,基本上和PageHelper一樣,但并未直接使用它。另外,對(duì)于查詢來說,在查詢條件中是否帶有分片鍵,也是很關(guān)鍵的地方。

1、查詢

在范圍算法中,在業(yè)務(wù)上我們要求只查詢特定某一個(gè)月或者近幾個(gè)月的數(shù)據(jù)即可;在Hash算法中,我們則要求每次都帶有主鍵。

但第二個(gè)條件往往不能成立,業(yè)務(wù)方也滿足不了每次都必須帶有主鍵。

針對(duì)這種情況,我們只能遍歷所有的表,查詢符合條件的數(shù)據(jù),然后再匯總返回;

 

分庫分表簡(jiǎn)單?那我想問如何實(shí)現(xiàn)“分庫分表插件”?

 

這種方式的缺點(diǎn)顯而易見,性能較差。還有一種方式就是可以將常用的查詢條件與分片鍵建立映射關(guān)系,在查詢時(shí)先根據(jù)查詢條件找到分片鍵的字段值,然后再根據(jù)分片鍵查詢。

2、分頁

如上所言,插件中集成了分頁功能,實(shí)現(xiàn)流程與PageHelper一樣,但考慮到?jīng)_突,并未直接使用。

 

分庫分表簡(jiǎn)單?那我想問如何實(shí)現(xiàn)“分庫分表插件”?

 


作者:清幽之地
原文鏈接:https://juejin.im/post/5dfc6cc0518825126f3735d7

分享到:
標(biāo)簽:分庫分表
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定