'海量數據下的分庫分表最佳實戰'

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

以阿里訂單系統為例(參考《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實現》),它選擇了三個column作為三個獨立的sharding column。

即:order_id,user_id,merchant_code

其中,user_id和merchant_code就是買家ID和賣家ID,因為阿里的訂單系統中買家和賣家的查詢流量都比較大,並且查詢對實時性要求都很高。而根據order_id進行分庫分表,應該是根據order_id的查詢也比較多。

這裡還有一點需要提及,多個sharding-column的分庫分表是冗餘全量還是隻冗餘關係索引表,需要我們自己權衡。

冗餘全量的情況如下圖,每個sharding列對應的表的數據都是全量的,這樣做的優點是不需要二次查詢,性能更好,缺點是比較浪費存儲空間(淺綠色字段就是sharding-column):

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

以阿里訂單系統為例(參考《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實現》),它選擇了三個column作為三個獨立的sharding column。

即:order_id,user_id,merchant_code

其中,user_id和merchant_code就是買家ID和賣家ID,因為阿里的訂單系統中買家和賣家的查詢流量都比較大,並且查詢對實時性要求都很高。而根據order_id進行分庫分表,應該是根據order_id的查詢也比較多。

這裡還有一點需要提及,多個sharding-column的分庫分表是冗餘全量還是隻冗餘關係索引表,需要我們自己權衡。

冗餘全量的情況如下圖,每個sharding列對應的表的數據都是全量的,這樣做的優點是不需要二次查詢,性能更好,缺點是比較浪費存儲空間(淺綠色字段就是sharding-column):

海量數據下的分庫分表最佳實戰

冗餘關係索引表的情況如下圖,只有一個sharding column的分庫分表的數據是全量的,其他分庫分表只是與這個sharding column的關係表。

這樣做的優點是節省空間,缺點是除了第一個sharding column的查詢,其他sharding column的查詢都需要二次查詢。

這三張表的關係如下圖所示(淺綠色字段就是sharding column):

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

以阿里訂單系統為例(參考《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實現》),它選擇了三個column作為三個獨立的sharding column。

即:order_id,user_id,merchant_code

其中,user_id和merchant_code就是買家ID和賣家ID,因為阿里的訂單系統中買家和賣家的查詢流量都比較大,並且查詢對實時性要求都很高。而根據order_id進行分庫分表,應該是根據order_id的查詢也比較多。

這裡還有一點需要提及,多個sharding-column的分庫分表是冗餘全量還是隻冗餘關係索引表,需要我們自己權衡。

冗餘全量的情況如下圖,每個sharding列對應的表的數據都是全量的,這樣做的優點是不需要二次查詢,性能更好,缺點是比較浪費存儲空間(淺綠色字段就是sharding-column):

海量數據下的分庫分表最佳實戰

冗餘關係索引表的情況如下圖,只有一個sharding column的分庫分表的數據是全量的,其他分庫分表只是與這個sharding column的關係表。

這樣做的優點是節省空間,缺點是除了第一個sharding column的查詢,其他sharding column的查詢都需要二次查詢。

這三張表的關係如下圖所示(淺綠色字段就是sharding column):

海量數據下的分庫分表最佳實戰

冗餘全量表PK 冗餘關係表

  1. 速度對比:冗餘全量表速度更快,冗餘關係表需要二次查詢,即使有引入緩存,還是多一次網絡開銷;
  2. 存儲成本:冗餘全量表需要幾倍於冗餘關係表的存儲成本;
  3. 維護代價:冗餘全量表維護代價更大,涉及到數據變更時,多張表都要進行修改。

總結:選擇冗餘全量表還是索引關係表,這是一種架構上的trade off,兩者的優缺點明顯,阿里的訂單表是冗餘全量表。

用戶表

用戶表幾個核心字段一般如下:

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

以阿里訂單系統為例(參考《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實現》),它選擇了三個column作為三個獨立的sharding column。

即:order_id,user_id,merchant_code

其中,user_id和merchant_code就是買家ID和賣家ID,因為阿里的訂單系統中買家和賣家的查詢流量都比較大,並且查詢對實時性要求都很高。而根據order_id進行分庫分表,應該是根據order_id的查詢也比較多。

這裡還有一點需要提及,多個sharding-column的分庫分表是冗餘全量還是隻冗餘關係索引表,需要我們自己權衡。

冗餘全量的情況如下圖,每個sharding列對應的表的數據都是全量的,這樣做的優點是不需要二次查詢,性能更好,缺點是比較浪費存儲空間(淺綠色字段就是sharding-column):

海量數據下的分庫分表最佳實戰

冗餘關係索引表的情況如下圖,只有一個sharding column的分庫分表的數據是全量的,其他分庫分表只是與這個sharding column的關係表。

這樣做的優點是節省空間,缺點是除了第一個sharding column的查詢,其他sharding column的查詢都需要二次查詢。

這三張表的關係如下圖所示(淺綠色字段就是sharding column):

海量數據下的分庫分表最佳實戰

冗餘全量表PK 冗餘關係表

  1. 速度對比:冗餘全量表速度更快,冗餘關係表需要二次查詢,即使有引入緩存,還是多一次網絡開銷;
  2. 存儲成本:冗餘全量表需要幾倍於冗餘關係表的存儲成本;
  3. 維護代價:冗餘全量表維護代價更大,涉及到數據變更時,多張表都要進行修改。

總結:選擇冗餘全量表還是索引關係表,這是一種架構上的trade off,兩者的優缺點明顯,阿里的訂單表是冗餘全量表。

用戶表

用戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

一般用戶登錄場景既可以通過mobile_no,又可以通過email,還可以通過username進行登錄。

但是一些用戶相關的API,又都包含user_id,那麼可能需要根據這4個column都進行分庫分表,即4個列都是sharding-column。

賬戶表

賬戶表幾個核心字段一般如下:

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

以阿里訂單系統為例(參考《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實現》),它選擇了三個column作為三個獨立的sharding column。

即:order_id,user_id,merchant_code

其中,user_id和merchant_code就是買家ID和賣家ID,因為阿里的訂單系統中買家和賣家的查詢流量都比較大,並且查詢對實時性要求都很高。而根據order_id進行分庫分表,應該是根據order_id的查詢也比較多。

這裡還有一點需要提及,多個sharding-column的分庫分表是冗餘全量還是隻冗餘關係索引表,需要我們自己權衡。

冗餘全量的情況如下圖,每個sharding列對應的表的數據都是全量的,這樣做的優點是不需要二次查詢,性能更好,缺點是比較浪費存儲空間(淺綠色字段就是sharding-column):

海量數據下的分庫分表最佳實戰

冗餘關係索引表的情況如下圖,只有一個sharding column的分庫分表的數據是全量的,其他分庫分表只是與這個sharding column的關係表。

這樣做的優點是節省空間,缺點是除了第一個sharding column的查詢,其他sharding column的查詢都需要二次查詢。

這三張表的關係如下圖所示(淺綠色字段就是sharding column):

海量數據下的分庫分表最佳實戰

冗餘全量表PK 冗餘關係表

  1. 速度對比:冗餘全量表速度更快,冗餘關係表需要二次查詢,即使有引入緩存,還是多一次網絡開銷;
  2. 存儲成本:冗餘全量表需要幾倍於冗餘關係表的存儲成本;
  3. 維護代價:冗餘全量表維護代價更大,涉及到數據變更時,多張表都要進行修改。

總結:選擇冗餘全量表還是索引關係表,這是一種架構上的trade off,兩者的優缺點明顯,阿里的訂單表是冗餘全量表。

用戶表

用戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

一般用戶登錄場景既可以通過mobile_no,又可以通過email,還可以通過username進行登錄。

但是一些用戶相關的API,又都包含user_id,那麼可能需要根據這4個column都進行分庫分表,即4個列都是sharding-column。

賬戶表

賬戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

與賬戶表相關的API,一般條件都有account_no,所以以account_no作為sharding-column即可。

複雜查詢

上面提到的都是條件中有sharding column的SQL執行。但是,總有一些查詢條件是不包含sharding column的,同時,我們也不可能為了這些請求量並不高的查詢,無限制的冗餘分庫分表。

那麼這些條件中沒有sharding column的SQL怎麼處理?

以sharding-jdbc為例,有多少個分庫分表,就要併發路由到多少個分庫分表中執行,然後對結果進行合併。具體如何合併,可以看筆者sharding-jdbc系列文章,有分析源碼講解合併原理。

這種條件查詢相對於有sharding column的條件查詢性能很明顯會下降很多。如果有幾十個,甚至上百個分庫分表,只要某個表的執行由於某些因素變慢,就會導致整個SQL的執行響應變慢,這非常符合木桶理論。

更有甚者,那些運營系統中的模糊條件查詢,或者上十個條件篩選。這種情況下,即使單表都不好創建索引,更不要說分庫分表的情況下。

那麼怎麼辦呢?這個時候大名鼎鼎的elasticsearch,即es就派上用場了。將分庫分表所有數據全量冗餘到es中,將那些複雜的查詢交給es處理。

淘寶我的所有訂單頁面如下,篩選條件有多個,且商品標題可以模糊匹配,這即使是單表都解決不了的問題(索引滿足不了這種場景),更不要說分庫分表了:

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

以阿里訂單系統為例(參考《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實現》),它選擇了三個column作為三個獨立的sharding column。

即:order_id,user_id,merchant_code

其中,user_id和merchant_code就是買家ID和賣家ID,因為阿里的訂單系統中買家和賣家的查詢流量都比較大,並且查詢對實時性要求都很高。而根據order_id進行分庫分表,應該是根據order_id的查詢也比較多。

這裡還有一點需要提及,多個sharding-column的分庫分表是冗餘全量還是隻冗餘關係索引表,需要我們自己權衡。

冗餘全量的情況如下圖,每個sharding列對應的表的數據都是全量的,這樣做的優點是不需要二次查詢,性能更好,缺點是比較浪費存儲空間(淺綠色字段就是sharding-column):

海量數據下的分庫分表最佳實戰

冗餘關係索引表的情況如下圖,只有一個sharding column的分庫分表的數據是全量的,其他分庫分表只是與這個sharding column的關係表。

這樣做的優點是節省空間,缺點是除了第一個sharding column的查詢,其他sharding column的查詢都需要二次查詢。

這三張表的關係如下圖所示(淺綠色字段就是sharding column):

海量數據下的分庫分表最佳實戰

冗餘全量表PK 冗餘關係表

  1. 速度對比:冗餘全量表速度更快,冗餘關係表需要二次查詢,即使有引入緩存,還是多一次網絡開銷;
  2. 存儲成本:冗餘全量表需要幾倍於冗餘關係表的存儲成本;
  3. 維護代價:冗餘全量表維護代價更大,涉及到數據變更時,多張表都要進行修改。

總結:選擇冗餘全量表還是索引關係表,這是一種架構上的trade off,兩者的優缺點明顯,阿里的訂單表是冗餘全量表。

用戶表

用戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

一般用戶登錄場景既可以通過mobile_no,又可以通過email,還可以通過username進行登錄。

但是一些用戶相關的API,又都包含user_id,那麼可能需要根據這4個column都進行分庫分表,即4個列都是sharding-column。

賬戶表

賬戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

與賬戶表相關的API,一般條件都有account_no,所以以account_no作為sharding-column即可。

複雜查詢

上面提到的都是條件中有sharding column的SQL執行。但是,總有一些查詢條件是不包含sharding column的,同時,我們也不可能為了這些請求量並不高的查詢,無限制的冗餘分庫分表。

那麼這些條件中沒有sharding column的SQL怎麼處理?

以sharding-jdbc為例,有多少個分庫分表,就要併發路由到多少個分庫分表中執行,然後對結果進行合併。具體如何合併,可以看筆者sharding-jdbc系列文章,有分析源碼講解合併原理。

這種條件查詢相對於有sharding column的條件查詢性能很明顯會下降很多。如果有幾十個,甚至上百個分庫分表,只要某個表的執行由於某些因素變慢,就會導致整個SQL的執行響應變慢,這非常符合木桶理論。

更有甚者,那些運營系統中的模糊條件查詢,或者上十個條件篩選。這種情況下,即使單表都不好創建索引,更不要說分庫分表的情況下。

那麼怎麼辦呢?這個時候大名鼎鼎的elasticsearch,即es就派上用場了。將分庫分表所有數據全量冗餘到es中,將那些複雜的查詢交給es處理。

淘寶我的所有訂單頁面如下,篩選條件有多個,且商品標題可以模糊匹配,這即使是單表都解決不了的問題(索引滿足不了這種場景),更不要說分庫分表了:

海量數據下的分庫分表最佳實戰

所以,以訂單表為例,整個架構如下:

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

以阿里訂單系統為例(參考《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實現》),它選擇了三個column作為三個獨立的sharding column。

即:order_id,user_id,merchant_code

其中,user_id和merchant_code就是買家ID和賣家ID,因為阿里的訂單系統中買家和賣家的查詢流量都比較大,並且查詢對實時性要求都很高。而根據order_id進行分庫分表,應該是根據order_id的查詢也比較多。

這裡還有一點需要提及,多個sharding-column的分庫分表是冗餘全量還是隻冗餘關係索引表,需要我們自己權衡。

冗餘全量的情況如下圖,每個sharding列對應的表的數據都是全量的,這樣做的優點是不需要二次查詢,性能更好,缺點是比較浪費存儲空間(淺綠色字段就是sharding-column):

海量數據下的分庫分表最佳實戰

冗餘關係索引表的情況如下圖,只有一個sharding column的分庫分表的數據是全量的,其他分庫分表只是與這個sharding column的關係表。

這樣做的優點是節省空間,缺點是除了第一個sharding column的查詢,其他sharding column的查詢都需要二次查詢。

這三張表的關係如下圖所示(淺綠色字段就是sharding column):

海量數據下的分庫分表最佳實戰

冗餘全量表PK 冗餘關係表

  1. 速度對比:冗餘全量表速度更快,冗餘關係表需要二次查詢,即使有引入緩存,還是多一次網絡開銷;
  2. 存儲成本:冗餘全量表需要幾倍於冗餘關係表的存儲成本;
  3. 維護代價:冗餘全量表維護代價更大,涉及到數據變更時,多張表都要進行修改。

總結:選擇冗餘全量表還是索引關係表,這是一種架構上的trade off,兩者的優缺點明顯,阿里的訂單表是冗餘全量表。

用戶表

用戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

一般用戶登錄場景既可以通過mobile_no,又可以通過email,還可以通過username進行登錄。

但是一些用戶相關的API,又都包含user_id,那麼可能需要根據這4個column都進行分庫分表,即4個列都是sharding-column。

賬戶表

賬戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

與賬戶表相關的API,一般條件都有account_no,所以以account_no作為sharding-column即可。

複雜查詢

上面提到的都是條件中有sharding column的SQL執行。但是,總有一些查詢條件是不包含sharding column的,同時,我們也不可能為了這些請求量並不高的查詢,無限制的冗餘分庫分表。

那麼這些條件中沒有sharding column的SQL怎麼處理?

以sharding-jdbc為例,有多少個分庫分表,就要併發路由到多少個分庫分表中執行,然後對結果進行合併。具體如何合併,可以看筆者sharding-jdbc系列文章,有分析源碼講解合併原理。

這種條件查詢相對於有sharding column的條件查詢性能很明顯會下降很多。如果有幾十個,甚至上百個分庫分表,只要某個表的執行由於某些因素變慢,就會導致整個SQL的執行響應變慢,這非常符合木桶理論。

更有甚者,那些運營系統中的模糊條件查詢,或者上十個條件篩選。這種情況下,即使單表都不好創建索引,更不要說分庫分表的情況下。

那麼怎麼辦呢?這個時候大名鼎鼎的elasticsearch,即es就派上用場了。將分庫分表所有數據全量冗餘到es中,將那些複雜的查詢交給es處理。

淘寶我的所有訂單頁面如下,篩選條件有多個,且商品標題可以模糊匹配,這即使是單表都解決不了的問題(索引滿足不了這種場景),更不要說分庫分表了:

海量數據下的分庫分表最佳實戰

所以,以訂單表為例,整個架構如下:

海量數據下的分庫分表最佳實戰

具體情況具體分析:多sharding column不到萬不得已的情況下最好不要使用,成本較大,上面提到的用戶表筆者就不太建議使用。

因為用戶表有一個很大的特點就是它的上限是肯定的,即使全球70億人全是你的用戶,這點數據量也不大,所以筆者更建議採用單sharding column + es的模式簡化架構。

es+HBase簡要

這裡需要提前說明的是,solr + HBase結合的方案在社區中出現的頻率可能更高,本篇文章為了保持一致性,所有全文索引方案選型都是es。

至於es+HBase和solr+HBase孰優孰劣,或者說es和solr孰優孰劣,不是本文需要討論的範疇,事實上也沒有太多討論的意義。

es和solr本就是兩個非常優秀且旗鼓相當的中間件。最近幾年es更火爆:

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

以阿里訂單系統為例(參考《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實現》),它選擇了三個column作為三個獨立的sharding column。

即:order_id,user_id,merchant_code

其中,user_id和merchant_code就是買家ID和賣家ID,因為阿里的訂單系統中買家和賣家的查詢流量都比較大,並且查詢對實時性要求都很高。而根據order_id進行分庫分表,應該是根據order_id的查詢也比較多。

這裡還有一點需要提及,多個sharding-column的分庫分表是冗餘全量還是隻冗餘關係索引表,需要我們自己權衡。

冗餘全量的情況如下圖,每個sharding列對應的表的數據都是全量的,這樣做的優點是不需要二次查詢,性能更好,缺點是比較浪費存儲空間(淺綠色字段就是sharding-column):

海量數據下的分庫分表最佳實戰

冗餘關係索引表的情況如下圖,只有一個sharding column的分庫分表的數據是全量的,其他分庫分表只是與這個sharding column的關係表。

這樣做的優點是節省空間,缺點是除了第一個sharding column的查詢,其他sharding column的查詢都需要二次查詢。

這三張表的關係如下圖所示(淺綠色字段就是sharding column):

海量數據下的分庫分表最佳實戰

冗餘全量表PK 冗餘關係表

  1. 速度對比:冗餘全量表速度更快,冗餘關係表需要二次查詢,即使有引入緩存,還是多一次網絡開銷;
  2. 存儲成本:冗餘全量表需要幾倍於冗餘關係表的存儲成本;
  3. 維護代價:冗餘全量表維護代價更大,涉及到數據變更時,多張表都要進行修改。

總結:選擇冗餘全量表還是索引關係表,這是一種架構上的trade off,兩者的優缺點明顯,阿里的訂單表是冗餘全量表。

用戶表

用戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

一般用戶登錄場景既可以通過mobile_no,又可以通過email,還可以通過username進行登錄。

但是一些用戶相關的API,又都包含user_id,那麼可能需要根據這4個column都進行分庫分表,即4個列都是sharding-column。

賬戶表

賬戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

與賬戶表相關的API,一般條件都有account_no,所以以account_no作為sharding-column即可。

複雜查詢

上面提到的都是條件中有sharding column的SQL執行。但是,總有一些查詢條件是不包含sharding column的,同時,我們也不可能為了這些請求量並不高的查詢,無限制的冗餘分庫分表。

那麼這些條件中沒有sharding column的SQL怎麼處理?

以sharding-jdbc為例,有多少個分庫分表,就要併發路由到多少個分庫分表中執行,然後對結果進行合併。具體如何合併,可以看筆者sharding-jdbc系列文章,有分析源碼講解合併原理。

這種條件查詢相對於有sharding column的條件查詢性能很明顯會下降很多。如果有幾十個,甚至上百個分庫分表,只要某個表的執行由於某些因素變慢,就會導致整個SQL的執行響應變慢,這非常符合木桶理論。

更有甚者,那些運營系統中的模糊條件查詢,或者上十個條件篩選。這種情況下,即使單表都不好創建索引,更不要說分庫分表的情況下。

那麼怎麼辦呢?這個時候大名鼎鼎的elasticsearch,即es就派上用場了。將分庫分表所有數據全量冗餘到es中,將那些複雜的查詢交給es處理。

淘寶我的所有訂單頁面如下,篩選條件有多個,且商品標題可以模糊匹配,這即使是單表都解決不了的問題(索引滿足不了這種場景),更不要說分庫分表了:

海量數據下的分庫分表最佳實戰

所以,以訂單表為例,整個架構如下:

海量數據下的分庫分表最佳實戰

具體情況具體分析:多sharding column不到萬不得已的情況下最好不要使用,成本較大,上面提到的用戶表筆者就不太建議使用。

因為用戶表有一個很大的特點就是它的上限是肯定的,即使全球70億人全是你的用戶,這點數據量也不大,所以筆者更建議採用單sharding column + es的模式簡化架構。

es+HBase簡要

這裡需要提前說明的是,solr + HBase結合的方案在社區中出現的頻率可能更高,本篇文章為了保持一致性,所有全文索引方案選型都是es。

至於es+HBase和solr+HBase孰優孰劣,或者說es和solr孰優孰劣,不是本文需要討論的範疇,事實上也沒有太多討論的意義。

es和solr本就是兩個非常優秀且旗鼓相當的中間件。最近幾年es更火爆:

海量數據下的分庫分表最佳實戰

如果拋開選型過程中所有歷史包袱,單論es+HBase和solr+HBase的優劣,很明顯後者是更好的選擇。

solr+HBase高度集成,引入索引服務後我們最關心,也是最重要的索引一致性問題,solr+HBase已經有了非常成熟的解決方案一一 Lily HBase Indexer

延伸閱讀

阿里雲上的雲數據庫HBase版也是藉助solr實現全文索引,有興趣的同學可以戳鏈接瞭解更多:

https://help.aliyun.com/product/49055.html?spm=5176.124785.631202.con1.603452c0cz7bj2

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

以阿里訂單系統為例(參考《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實現》),它選擇了三個column作為三個獨立的sharding column。

即:order_id,user_id,merchant_code

其中,user_id和merchant_code就是買家ID和賣家ID,因為阿里的訂單系統中買家和賣家的查詢流量都比較大,並且查詢對實時性要求都很高。而根據order_id進行分庫分表,應該是根據order_id的查詢也比較多。

這裡還有一點需要提及,多個sharding-column的分庫分表是冗餘全量還是隻冗餘關係索引表,需要我們自己權衡。

冗餘全量的情況如下圖,每個sharding列對應的表的數據都是全量的,這樣做的優點是不需要二次查詢,性能更好,缺點是比較浪費存儲空間(淺綠色字段就是sharding-column):

海量數據下的分庫分表最佳實戰

冗餘關係索引表的情況如下圖,只有一個sharding column的分庫分表的數據是全量的,其他分庫分表只是與這個sharding column的關係表。

這樣做的優點是節省空間,缺點是除了第一個sharding column的查詢,其他sharding column的查詢都需要二次查詢。

這三張表的關係如下圖所示(淺綠色字段就是sharding column):

海量數據下的分庫分表最佳實戰

冗餘全量表PK 冗餘關係表

  1. 速度對比:冗餘全量表速度更快,冗餘關係表需要二次查詢,即使有引入緩存,還是多一次網絡開銷;
  2. 存儲成本:冗餘全量表需要幾倍於冗餘關係表的存儲成本;
  3. 維護代價:冗餘全量表維護代價更大,涉及到數據變更時,多張表都要進行修改。

總結:選擇冗餘全量表還是索引關係表,這是一種架構上的trade off,兩者的優缺點明顯,阿里的訂單表是冗餘全量表。

用戶表

用戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

一般用戶登錄場景既可以通過mobile_no,又可以通過email,還可以通過username進行登錄。

但是一些用戶相關的API,又都包含user_id,那麼可能需要根據這4個column都進行分庫分表,即4個列都是sharding-column。

賬戶表

賬戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

與賬戶表相關的API,一般條件都有account_no,所以以account_no作為sharding-column即可。

複雜查詢

上面提到的都是條件中有sharding column的SQL執行。但是,總有一些查詢條件是不包含sharding column的,同時,我們也不可能為了這些請求量並不高的查詢,無限制的冗餘分庫分表。

那麼這些條件中沒有sharding column的SQL怎麼處理?

以sharding-jdbc為例,有多少個分庫分表,就要併發路由到多少個分庫分表中執行,然後對結果進行合併。具體如何合併,可以看筆者sharding-jdbc系列文章,有分析源碼講解合併原理。

這種條件查詢相對於有sharding column的條件查詢性能很明顯會下降很多。如果有幾十個,甚至上百個分庫分表,只要某個表的執行由於某些因素變慢,就會導致整個SQL的執行響應變慢,這非常符合木桶理論。

更有甚者,那些運營系統中的模糊條件查詢,或者上十個條件篩選。這種情況下,即使單表都不好創建索引,更不要說分庫分表的情況下。

那麼怎麼辦呢?這個時候大名鼎鼎的elasticsearch,即es就派上用場了。將分庫分表所有數據全量冗餘到es中,將那些複雜的查詢交給es處理。

淘寶我的所有訂單頁面如下,篩選條件有多個,且商品標題可以模糊匹配,這即使是單表都解決不了的問題(索引滿足不了這種場景),更不要說分庫分表了:

海量數據下的分庫分表最佳實戰

所以,以訂單表為例,整個架構如下:

海量數據下的分庫分表最佳實戰

具體情況具體分析:多sharding column不到萬不得已的情況下最好不要使用,成本較大,上面提到的用戶表筆者就不太建議使用。

因為用戶表有一個很大的特點就是它的上限是肯定的,即使全球70億人全是你的用戶,這點數據量也不大,所以筆者更建議採用單sharding column + es的模式簡化架構。

es+HBase簡要

這裡需要提前說明的是,solr + HBase結合的方案在社區中出現的頻率可能更高,本篇文章為了保持一致性,所有全文索引方案選型都是es。

至於es+HBase和solr+HBase孰優孰劣,或者說es和solr孰優孰劣,不是本文需要討論的範疇,事實上也沒有太多討論的意義。

es和solr本就是兩個非常優秀且旗鼓相當的中間件。最近幾年es更火爆:

海量數據下的分庫分表最佳實戰

如果拋開選型過程中所有歷史包袱,單論es+HBase和solr+HBase的優劣,很明顯後者是更好的選擇。

solr+HBase高度集成,引入索引服務後我們最關心,也是最重要的索引一致性問題,solr+HBase已經有了非常成熟的解決方案一一 Lily HBase Indexer

延伸閱讀

阿里雲上的雲數據庫HBase版也是藉助solr實現全文索引,有興趣的同學可以戳鏈接瞭解更多:

https://help.aliyun.com/product/49055.html?spm=5176.124785.631202.con1.603452c0cz7bj2

海量數據下的分庫分表最佳實戰

es+HBase原理

剛剛討論到上面的以MySQL為核心,分庫分表+es的方案,隨著數據量越來越來,雖然分庫分表可以繼續成倍擴容,但是這時候壓力又落到了es這裡,這個架構也會慢慢暴露出問題!

一般訂單表,積分明細表等需要分庫分表的核心表都會有好幾十列,甚至上百列(假設有50列),但是整個表真正需要參與條件索引的可能就不到10個條件(假設有10列)。

這時候把50個列所有字段的數據全量索引到es中,對es集群有很大的壓力,後面的es分片故障恢復也會需要很長的時間。

這個時候我們可以考慮減少es的壓力,讓es集群有限的資源儘可能保存條件檢索時最需要的最有價值的數據

即只把可能參與條件檢索的字段索引到es中,這樣整個es集群壓力減少到原來的1/5(核心表50個字段,只有10個字段參與條件)

而50個字段的全量數據保存到HBase中,這就是經典的es+HBase組合方案,即 索引與數據存儲隔離的方案

Hadoop體系下的HBase存儲能力我們都知道是海量的,而且根據它的rowkey查詢性能那叫一個快如閃電,而es的多條件檢索能力非常強大。

這個方案把es和HBase的優點發揮的淋漓盡致,同時又規避了它們的缺點,可以說是一個揚長避免的最佳實踐。

它們之間的交互大概是這樣的:先根據用戶輸入的條件去es查詢獲取符合過濾條件的rowkey值,然後用rowkey值去HBase查詢,後面這一查詢步驟的時間幾乎可以忽略,因為這是HBase最擅長的場景。

交互圖如下所示:

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

以阿里訂單系統為例(參考《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實現》),它選擇了三個column作為三個獨立的sharding column。

即:order_id,user_id,merchant_code

其中,user_id和merchant_code就是買家ID和賣家ID,因為阿里的訂單系統中買家和賣家的查詢流量都比較大,並且查詢對實時性要求都很高。而根據order_id進行分庫分表,應該是根據order_id的查詢也比較多。

這裡還有一點需要提及,多個sharding-column的分庫分表是冗餘全量還是隻冗餘關係索引表,需要我們自己權衡。

冗餘全量的情況如下圖,每個sharding列對應的表的數據都是全量的,這樣做的優點是不需要二次查詢,性能更好,缺點是比較浪費存儲空間(淺綠色字段就是sharding-column):

海量數據下的分庫分表最佳實戰

冗餘關係索引表的情況如下圖,只有一個sharding column的分庫分表的數據是全量的,其他分庫分表只是與這個sharding column的關係表。

這樣做的優點是節省空間,缺點是除了第一個sharding column的查詢,其他sharding column的查詢都需要二次查詢。

這三張表的關係如下圖所示(淺綠色字段就是sharding column):

海量數據下的分庫分表最佳實戰

冗餘全量表PK 冗餘關係表

  1. 速度對比:冗餘全量表速度更快,冗餘關係表需要二次查詢,即使有引入緩存,還是多一次網絡開銷;
  2. 存儲成本:冗餘全量表需要幾倍於冗餘關係表的存儲成本;
  3. 維護代價:冗餘全量表維護代價更大,涉及到數據變更時,多張表都要進行修改。

總結:選擇冗餘全量表還是索引關係表,這是一種架構上的trade off,兩者的優缺點明顯,阿里的訂單表是冗餘全量表。

用戶表

用戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

一般用戶登錄場景既可以通過mobile_no,又可以通過email,還可以通過username進行登錄。

但是一些用戶相關的API,又都包含user_id,那麼可能需要根據這4個column都進行分庫分表,即4個列都是sharding-column。

賬戶表

賬戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

與賬戶表相關的API,一般條件都有account_no,所以以account_no作為sharding-column即可。

複雜查詢

上面提到的都是條件中有sharding column的SQL執行。但是,總有一些查詢條件是不包含sharding column的,同時,我們也不可能為了這些請求量並不高的查詢,無限制的冗餘分庫分表。

那麼這些條件中沒有sharding column的SQL怎麼處理?

以sharding-jdbc為例,有多少個分庫分表,就要併發路由到多少個分庫分表中執行,然後對結果進行合併。具體如何合併,可以看筆者sharding-jdbc系列文章,有分析源碼講解合併原理。

這種條件查詢相對於有sharding column的條件查詢性能很明顯會下降很多。如果有幾十個,甚至上百個分庫分表,只要某個表的執行由於某些因素變慢,就會導致整個SQL的執行響應變慢,這非常符合木桶理論。

更有甚者,那些運營系統中的模糊條件查詢,或者上十個條件篩選。這種情況下,即使單表都不好創建索引,更不要說分庫分表的情況下。

那麼怎麼辦呢?這個時候大名鼎鼎的elasticsearch,即es就派上用場了。將分庫分表所有數據全量冗餘到es中,將那些複雜的查詢交給es處理。

淘寶我的所有訂單頁面如下,篩選條件有多個,且商品標題可以模糊匹配,這即使是單表都解決不了的問題(索引滿足不了這種場景),更不要說分庫分表了:

海量數據下的分庫分表最佳實戰

所以,以訂單表為例,整個架構如下:

海量數據下的分庫分表最佳實戰

具體情況具體分析:多sharding column不到萬不得已的情況下最好不要使用,成本較大,上面提到的用戶表筆者就不太建議使用。

因為用戶表有一個很大的特點就是它的上限是肯定的,即使全球70億人全是你的用戶,這點數據量也不大,所以筆者更建議採用單sharding column + es的模式簡化架構。

es+HBase簡要

這裡需要提前說明的是,solr + HBase結合的方案在社區中出現的頻率可能更高,本篇文章為了保持一致性,所有全文索引方案選型都是es。

至於es+HBase和solr+HBase孰優孰劣,或者說es和solr孰優孰劣,不是本文需要討論的範疇,事實上也沒有太多討論的意義。

es和solr本就是兩個非常優秀且旗鼓相當的中間件。最近幾年es更火爆:

海量數據下的分庫分表最佳實戰

如果拋開選型過程中所有歷史包袱,單論es+HBase和solr+HBase的優劣,很明顯後者是更好的選擇。

solr+HBase高度集成,引入索引服務後我們最關心,也是最重要的索引一致性問題,solr+HBase已經有了非常成熟的解決方案一一 Lily HBase Indexer

延伸閱讀

阿里雲上的雲數據庫HBase版也是藉助solr實現全文索引,有興趣的同學可以戳鏈接瞭解更多:

https://help.aliyun.com/product/49055.html?spm=5176.124785.631202.con1.603452c0cz7bj2

海量數據下的分庫分表最佳實戰

es+HBase原理

剛剛討論到上面的以MySQL為核心,分庫分表+es的方案,隨著數據量越來越來,雖然分庫分表可以繼續成倍擴容,但是這時候壓力又落到了es這裡,這個架構也會慢慢暴露出問題!

一般訂單表,積分明細表等需要分庫分表的核心表都會有好幾十列,甚至上百列(假設有50列),但是整個表真正需要參與條件索引的可能就不到10個條件(假設有10列)。

這時候把50個列所有字段的數據全量索引到es中,對es集群有很大的壓力,後面的es分片故障恢復也會需要很長的時間。

這個時候我們可以考慮減少es的壓力,讓es集群有限的資源儘可能保存條件檢索時最需要的最有價值的數據

即只把可能參與條件檢索的字段索引到es中,這樣整個es集群壓力減少到原來的1/5(核心表50個字段,只有10個字段參與條件)

而50個字段的全量數據保存到HBase中,這就是經典的es+HBase組合方案,即 索引與數據存儲隔離的方案

Hadoop體系下的HBase存儲能力我們都知道是海量的,而且根據它的rowkey查詢性能那叫一個快如閃電,而es的多條件檢索能力非常強大。

這個方案把es和HBase的優點發揮的淋漓盡致,同時又規避了它們的缺點,可以說是一個揚長避免的最佳實踐。

它們之間的交互大概是這樣的:先根據用戶輸入的條件去es查詢獲取符合過濾條件的rowkey值,然後用rowkey值去HBase查詢,後面這一查詢步驟的時間幾乎可以忽略,因為這是HBase最擅長的場景。

交互圖如下所示:

海量數據下的分庫分表最佳實戰

HBase檢索能力擴展

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

以阿里訂單系統為例(參考《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實現》),它選擇了三個column作為三個獨立的sharding column。

即:order_id,user_id,merchant_code

其中,user_id和merchant_code就是買家ID和賣家ID,因為阿里的訂單系統中買家和賣家的查詢流量都比較大,並且查詢對實時性要求都很高。而根據order_id進行分庫分表,應該是根據order_id的查詢也比較多。

這裡還有一點需要提及,多個sharding-column的分庫分表是冗餘全量還是隻冗餘關係索引表,需要我們自己權衡。

冗餘全量的情況如下圖,每個sharding列對應的表的數據都是全量的,這樣做的優點是不需要二次查詢,性能更好,缺點是比較浪費存儲空間(淺綠色字段就是sharding-column):

海量數據下的分庫分表最佳實戰

冗餘關係索引表的情況如下圖,只有一個sharding column的分庫分表的數據是全量的,其他分庫分表只是與這個sharding column的關係表。

這樣做的優點是節省空間,缺點是除了第一個sharding column的查詢,其他sharding column的查詢都需要二次查詢。

這三張表的關係如下圖所示(淺綠色字段就是sharding column):

海量數據下的分庫分表最佳實戰

冗餘全量表PK 冗餘關係表

  1. 速度對比:冗餘全量表速度更快,冗餘關係表需要二次查詢,即使有引入緩存,還是多一次網絡開銷;
  2. 存儲成本:冗餘全量表需要幾倍於冗餘關係表的存儲成本;
  3. 維護代價:冗餘全量表維護代價更大,涉及到數據變更時,多張表都要進行修改。

總結:選擇冗餘全量表還是索引關係表,這是一種架構上的trade off,兩者的優缺點明顯,阿里的訂單表是冗餘全量表。

用戶表

用戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

一般用戶登錄場景既可以通過mobile_no,又可以通過email,還可以通過username進行登錄。

但是一些用戶相關的API,又都包含user_id,那麼可能需要根據這4個column都進行分庫分表,即4個列都是sharding-column。

賬戶表

賬戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

與賬戶表相關的API,一般條件都有account_no,所以以account_no作為sharding-column即可。

複雜查詢

上面提到的都是條件中有sharding column的SQL執行。但是,總有一些查詢條件是不包含sharding column的,同時,我們也不可能為了這些請求量並不高的查詢,無限制的冗餘分庫分表。

那麼這些條件中沒有sharding column的SQL怎麼處理?

以sharding-jdbc為例,有多少個分庫分表,就要併發路由到多少個分庫分表中執行,然後對結果進行合併。具體如何合併,可以看筆者sharding-jdbc系列文章,有分析源碼講解合併原理。

這種條件查詢相對於有sharding column的條件查詢性能很明顯會下降很多。如果有幾十個,甚至上百個分庫分表,只要某個表的執行由於某些因素變慢,就會導致整個SQL的執行響應變慢,這非常符合木桶理論。

更有甚者,那些運營系統中的模糊條件查詢,或者上十個條件篩選。這種情況下,即使單表都不好創建索引,更不要說分庫分表的情況下。

那麼怎麼辦呢?這個時候大名鼎鼎的elasticsearch,即es就派上用場了。將分庫分表所有數據全量冗餘到es中,將那些複雜的查詢交給es處理。

淘寶我的所有訂單頁面如下,篩選條件有多個,且商品標題可以模糊匹配,這即使是單表都解決不了的問題(索引滿足不了這種場景),更不要說分庫分表了:

海量數據下的分庫分表最佳實戰

所以,以訂單表為例,整個架構如下:

海量數據下的分庫分表最佳實戰

具體情況具體分析:多sharding column不到萬不得已的情況下最好不要使用,成本較大,上面提到的用戶表筆者就不太建議使用。

因為用戶表有一個很大的特點就是它的上限是肯定的,即使全球70億人全是你的用戶,這點數據量也不大,所以筆者更建議採用單sharding column + es的模式簡化架構。

es+HBase簡要

這裡需要提前說明的是,solr + HBase結合的方案在社區中出現的頻率可能更高,本篇文章為了保持一致性,所有全文索引方案選型都是es。

至於es+HBase和solr+HBase孰優孰劣,或者說es和solr孰優孰劣,不是本文需要討論的範疇,事實上也沒有太多討論的意義。

es和solr本就是兩個非常優秀且旗鼓相當的中間件。最近幾年es更火爆:

海量數據下的分庫分表最佳實戰

如果拋開選型過程中所有歷史包袱,單論es+HBase和solr+HBase的優劣,很明顯後者是更好的選擇。

solr+HBase高度集成,引入索引服務後我們最關心,也是最重要的索引一致性問題,solr+HBase已經有了非常成熟的解決方案一一 Lily HBase Indexer

延伸閱讀

阿里雲上的雲數據庫HBase版也是藉助solr實現全文索引,有興趣的同學可以戳鏈接瞭解更多:

https://help.aliyun.com/product/49055.html?spm=5176.124785.631202.con1.603452c0cz7bj2

海量數據下的分庫分表最佳實戰

es+HBase原理

剛剛討論到上面的以MySQL為核心,分庫分表+es的方案,隨著數據量越來越來,雖然分庫分表可以繼續成倍擴容,但是這時候壓力又落到了es這裡,這個架構也會慢慢暴露出問題!

一般訂單表,積分明細表等需要分庫分表的核心表都會有好幾十列,甚至上百列(假設有50列),但是整個表真正需要參與條件索引的可能就不到10個條件(假設有10列)。

這時候把50個列所有字段的數據全量索引到es中,對es集群有很大的壓力,後面的es分片故障恢復也會需要很長的時間。

這個時候我們可以考慮減少es的壓力,讓es集群有限的資源儘可能保存條件檢索時最需要的最有價值的數據

即只把可能參與條件檢索的字段索引到es中,這樣整個es集群壓力減少到原來的1/5(核心表50個字段,只有10個字段參與條件)

而50個字段的全量數據保存到HBase中,這就是經典的es+HBase組合方案,即 索引與數據存儲隔離的方案

Hadoop體系下的HBase存儲能力我們都知道是海量的,而且根據它的rowkey查詢性能那叫一個快如閃電,而es的多條件檢索能力非常強大。

這個方案把es和HBase的優點發揮的淋漓盡致,同時又規避了它們的缺點,可以說是一個揚長避免的最佳實踐。

它們之間的交互大概是這樣的:先根據用戶輸入的條件去es查詢獲取符合過濾條件的rowkey值,然後用rowkey值去HBase查詢,後面這一查詢步驟的時間幾乎可以忽略,因為這是HBase最擅長的場景。

交互圖如下所示:

海量數據下的分庫分表最佳實戰

HBase檢索能力擴展

海量數據下的分庫分表最佳實戰

圖片來源於HBase技術社區-HBase應用實踐專場-HBase for Solr

總結

最後,對幾種方案總結如下( sharding column簡稱為sc ):

-

單個sc

多個sc

sc+es

sc+es+HBase

"

每個優秀的程序員和架構師都應該掌握分庫分表,移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

我們以支付寶為例,支付寶用戶是8億;微信用戶更是10億。而訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。

事實上,MySQL單表可以存儲10億級數據,只是這時候性能比較差。業界公認MySQL單表容量在1千萬以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 分區;
  2. 分庫分表;
  3. NoSQL / NewSQL;

說明一下:只分庫,或者只分表,或者分庫分表融合方案都統一認為是分庫分表方案。因為分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

Why Not NoSQL / NewSQL?

首先,為什麼不選擇第三種方案NoSQL/NewSQL,我認為主要是RDBMS有以下幾個優點:

  • RDBMS生態完善;
  • RDBMS絕對穩定;
  • RDBMS的事務特性;

NoSQL/NewSQL作為新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以 RDBMS 存儲為主, NoSQL / NewSQL 存儲為輔!

互聯網公司又以MySQL為主,國企 & 銀行等不差錢的企業以Oracle / DB2為主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

Why Not 分區?

我們再看分區表方案。瞭解這個方案之前,先了解它的原理:

分區表是由多個相關的底層表實現,這些底層表也是由句柄對象表示,所以我們也可以直接訪問各個分區。

存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引只是在各個底層表上各自加上一個相同的索引。

從存儲引擎的角度來看,底層表和一個普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

事實上,這個方案也不錯,它對用戶屏蔽了sharding的細節,即使查詢條件沒有sharding column,它也能正常工作(只是這時候性能一般)。

不過它的缺點很明顯:很多的資源都受到單機的限制,例如連接數,網絡吞吐等!

雖然每個分區可以獨立存儲,但是分區表的總入口還是一個MySQL示例。從而導致它的併發能力非常一般,遠遠達不到互聯網高併發的要求!

至於網上提到的一些其他缺點比如:無法使用外鍵,不支持全文索引。我認為這都不算缺點,21世紀的項目如果還是使用外鍵和數據庫的全文索引,我都懶得吐槽了!

所以,如果使用分區表,你的業務應該具備如下兩個特點:

  1. 數據不是海量(分區數有限,存儲能力就有限);
  2. 併發能力要求不高;

Why 分庫分表?

最後要介紹的就是目前互聯網行業處理海量數據的通用方法: 分庫分表

雖然大家都是採用分庫分表方案來處理海量核心數據,但是還沒有一個一統江湖的中間件,筆者這裡列舉一些有一定知名度的分庫分表中間件:

  • 阿里的TDDL,DRDS和cobar
  • 開源社區的sharding-jdbc(3.x已經更名為sharding-sphere)
  • 民間組織的MyCAT
  • 360的Atlas;
  • 美團的zebra

備註 :sharding-jdbc 的作者張亮大神原來在噹噹,現在在京東金融。但是sharding-jdbc的版權屬於開源社區,不是公司的,也不是張亮個人的!

其他比如網易,58,京東等公司都有自研的中間件。總之各自為戰,也可以說是百花齊放。

但是這麼多的分庫分表中間件全部可以歸結為兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)

架構如下:

海量數據下的分庫分表最佳實戰

PROXY模式代表有阿里的cobar,民間組織的MyCAT,架構如下:

海量數據下的分庫分表最佳實戰

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的: SQL解析,重寫,路由,執行,結果歸併

筆者比較傾向於CLIENT模式,架構簡單,性能損耗較小,運維成本低。

接下來,以幾個常見的大表為案例,說明分庫分表如何落地!

實戰案例

分庫分表第一步也是最重要的一步,即sharding column的選取,sharding column 選擇的好壞將直接決定整個分庫分表方案最終是否成功。

而sharding column的選取跟業務強相關,筆者認為選擇sharding column的方法最主要分析你的API流量,優先考慮流量大的API,將流量比較大的API對應的SQL提取出來,將這些SQL共同的條件作為sharding column。

例如一般的OLTP系統都是對用戶提供服務,這些API對應的SQL都有條件用戶ID,那麼,用戶ID就是非常好的sharding column。

這裡列舉分庫分表的幾種主要處理思路:

  1. 只選取一個sharding column進行分庫分表 ;
  2. 多個sharding column多個分庫分表;
  3. sharding column分庫分表 + es;

再以幾張實際表為例,說明如何分庫分表。

訂單表

訂單表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

以阿里訂單系統為例(參考《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實現》),它選擇了三個column作為三個獨立的sharding column。

即:order_id,user_id,merchant_code

其中,user_id和merchant_code就是買家ID和賣家ID,因為阿里的訂單系統中買家和賣家的查詢流量都比較大,並且查詢對實時性要求都很高。而根據order_id進行分庫分表,應該是根據order_id的查詢也比較多。

這裡還有一點需要提及,多個sharding-column的分庫分表是冗餘全量還是隻冗餘關係索引表,需要我們自己權衡。

冗餘全量的情況如下圖,每個sharding列對應的表的數據都是全量的,這樣做的優點是不需要二次查詢,性能更好,缺點是比較浪費存儲空間(淺綠色字段就是sharding-column):

海量數據下的分庫分表最佳實戰

冗餘關係索引表的情況如下圖,只有一個sharding column的分庫分表的數據是全量的,其他分庫分表只是與這個sharding column的關係表。

這樣做的優點是節省空間,缺點是除了第一個sharding column的查詢,其他sharding column的查詢都需要二次查詢。

這三張表的關係如下圖所示(淺綠色字段就是sharding column):

海量數據下的分庫分表最佳實戰

冗餘全量表PK 冗餘關係表

  1. 速度對比:冗餘全量表速度更快,冗餘關係表需要二次查詢,即使有引入緩存,還是多一次網絡開銷;
  2. 存儲成本:冗餘全量表需要幾倍於冗餘關係表的存儲成本;
  3. 維護代價:冗餘全量表維護代價更大,涉及到數據變更時,多張表都要進行修改。

總結:選擇冗餘全量表還是索引關係表,這是一種架構上的trade off,兩者的優缺點明顯,阿里的訂單表是冗餘全量表。

用戶表

用戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

一般用戶登錄場景既可以通過mobile_no,又可以通過email,還可以通過username進行登錄。

但是一些用戶相關的API,又都包含user_id,那麼可能需要根據這4個column都進行分庫分表,即4個列都是sharding-column。

賬戶表

賬戶表幾個核心字段一般如下:

海量數據下的分庫分表最佳實戰

與賬戶表相關的API,一般條件都有account_no,所以以account_no作為sharding-column即可。

複雜查詢

上面提到的都是條件中有sharding column的SQL執行。但是,總有一些查詢條件是不包含sharding column的,同時,我們也不可能為了這些請求量並不高的查詢,無限制的冗餘分庫分表。

那麼這些條件中沒有sharding column的SQL怎麼處理?

以sharding-jdbc為例,有多少個分庫分表,就要併發路由到多少個分庫分表中執行,然後對結果進行合併。具體如何合併,可以看筆者sharding-jdbc系列文章,有分析源碼講解合併原理。

這種條件查詢相對於有sharding column的條件查詢性能很明顯會下降很多。如果有幾十個,甚至上百個分庫分表,只要某個表的執行由於某些因素變慢,就會導致整個SQL的執行響應變慢,這非常符合木桶理論。

更有甚者,那些運營系統中的模糊條件查詢,或者上十個條件篩選。這種情況下,即使單表都不好創建索引,更不要說分庫分表的情況下。

那麼怎麼辦呢?這個時候大名鼎鼎的elasticsearch,即es就派上用場了。將分庫分表所有數據全量冗餘到es中,將那些複雜的查詢交給es處理。

淘寶我的所有訂單頁面如下,篩選條件有多個,且商品標題可以模糊匹配,這即使是單表都解決不了的問題(索引滿足不了這種場景),更不要說分庫分表了:

海量數據下的分庫分表最佳實戰

所以,以訂單表為例,整個架構如下:

海量數據下的分庫分表最佳實戰

具體情況具體分析:多sharding column不到萬不得已的情況下最好不要使用,成本較大,上面提到的用戶表筆者就不太建議使用。

因為用戶表有一個很大的特點就是它的上限是肯定的,即使全球70億人全是你的用戶,這點數據量也不大,所以筆者更建議採用單sharding column + es的模式簡化架構。

es+HBase簡要

這裡需要提前說明的是,solr + HBase結合的方案在社區中出現的頻率可能更高,本篇文章為了保持一致性,所有全文索引方案選型都是es。

至於es+HBase和solr+HBase孰優孰劣,或者說es和solr孰優孰劣,不是本文需要討論的範疇,事實上也沒有太多討論的意義。

es和solr本就是兩個非常優秀且旗鼓相當的中間件。最近幾年es更火爆:

海量數據下的分庫分表最佳實戰

如果拋開選型過程中所有歷史包袱,單論es+HBase和solr+HBase的優劣,很明顯後者是更好的選擇。

solr+HBase高度集成,引入索引服務後我們最關心,也是最重要的索引一致性問題,solr+HBase已經有了非常成熟的解決方案一一 Lily HBase Indexer

延伸閱讀

阿里雲上的雲數據庫HBase版也是藉助solr實現全文索引,有興趣的同學可以戳鏈接瞭解更多:

https://help.aliyun.com/product/49055.html?spm=5176.124785.631202.con1.603452c0cz7bj2

海量數據下的分庫分表最佳實戰

es+HBase原理

剛剛討論到上面的以MySQL為核心,分庫分表+es的方案,隨著數據量越來越來,雖然分庫分表可以繼續成倍擴容,但是這時候壓力又落到了es這裡,這個架構也會慢慢暴露出問題!

一般訂單表,積分明細表等需要分庫分表的核心表都會有好幾十列,甚至上百列(假設有50列),但是整個表真正需要參與條件索引的可能就不到10個條件(假設有10列)。

這時候把50個列所有字段的數據全量索引到es中,對es集群有很大的壓力,後面的es分片故障恢復也會需要很長的時間。

這個時候我們可以考慮減少es的壓力,讓es集群有限的資源儘可能保存條件檢索時最需要的最有價值的數據

即只把可能參與條件檢索的字段索引到es中,這樣整個es集群壓力減少到原來的1/5(核心表50個字段,只有10個字段參與條件)

而50個字段的全量數據保存到HBase中,這就是經典的es+HBase組合方案,即 索引與數據存儲隔離的方案

Hadoop體系下的HBase存儲能力我們都知道是海量的,而且根據它的rowkey查詢性能那叫一個快如閃電,而es的多條件檢索能力非常強大。

這個方案把es和HBase的優點發揮的淋漓盡致,同時又規避了它們的缺點,可以說是一個揚長避免的最佳實踐。

它們之間的交互大概是這樣的:先根據用戶輸入的條件去es查詢獲取符合過濾條件的rowkey值,然後用rowkey值去HBase查詢,後面這一查詢步驟的時間幾乎可以忽略,因為這是HBase最擅長的場景。

交互圖如下所示:

海量數據下的分庫分表最佳實戰

HBase檢索能力擴展

海量數據下的分庫分表最佳實戰

圖片來源於HBase技術社區-HBase應用實踐專場-HBase for Solr

總結

最後,對幾種方案總結如下( sharding column簡稱為sc ):

-

單個sc

多個sc

sc+es

sc+es+HBase

海量數據下的分庫分表最佳實戰

總之,對於海量數據,且有一定的併發量的分庫分表,絕不是引入某一個分庫分表中間件就能解決問題,而是一項系統的工程。需要分析整個表相關的業務,讓合適的中間件做它最擅長的事情。

例如有sharding column的查詢走分庫分表,一些模糊查詢,或者多個不固定條件篩選則走es,海量存儲則交給HBase。

做了這麼多事情後,後面還會有很多的工作要做,比如數據同步的一致性問題,還有運行一段時間後,某些表的數據量慢慢達到單表瓶頸,這時候還需要做冷數據遷移。

總之,分庫分表是一項非常複雜的系統工程。任何海量數據的處理,都不是簡單的事情,做好戰鬥的準備吧!

來源:

https://www.jianshu.com/p/f29e73b97794

"

相關推薦

推薦中...