天天看點

資料庫架構選型與落地,看這篇就夠了

大家好,我是Jensen。

随着時間和業務的發展,資料庫中的資料量增長是不可控的,庫和表中的資料會越來越大,随之帶來的是更高的磁盤、IO、系統開銷,甚至性能上的瓶頸,而單台伺服器的資源終究是有限的。

是以在面對業務擴張過程中,應用程式對資料庫系統的健壯性,安全性,擴充性提出了更高的要求。

以下,我從資料庫架構、選型與落地來讓大家入門。

0x1.資料庫架構有哪些

資料庫會面臨什麼樣的挑戰呢?

業務剛開始我們隻用單機資料庫就夠了,但随着業務增長,資料規模和使用者規模上升,這個時候資料庫會面臨IO瓶頸、存儲瓶頸、可用性、安全性問題。

  • IO瓶頸:大規模的使用者查詢和寫入,由于單機資料庫的IO有限無法支撐;
  • 存儲瓶頸:大量資料的存儲讓資料庫原有的邏輯結構已經無法适用,性能急劇下降;
  • 可用性:重點業務系統對資料庫的要求是不間斷地提供服務,一旦遇到突發事件時系統可以快速恢複;
  • 安全性:資料的容災能力,保證資料在任何時候不會丢失(避免運維删庫跑路);

為了解決上述的各種問題,資料庫衍生了出不同的架構來解決不同的場景需求。

一、主從架構

将資料庫的寫操作和讀操作分離,主庫接收寫請求,使用多個從庫副本負責讀請求,從庫和主庫同步更新資料保持資料一緻性,從庫可以水準擴充,用于面對讀請求的增加。

這個模式也就是常說的讀寫分離,針對的是小規模資料,而且存在大量讀操作的場景。

因為主從的資料是相同的,一旦主庫當機的時候,從庫可以切換為主庫提供寫入,是以這個架構也可以提高資料庫系統的安全性和可用性;

資料庫架構選型與落地,看這篇就夠了

優點:

  • 結構簡單,技術成熟,市面上大多數資料庫都支援這個方案;
  • 業務入侵最低;
  • 資料擁有多個容災副本,提高資料安全性;
  • 當主伺服器故障時,可立即切換到其他伺服器,提高系統可用性;

缺點:

  • 無法支援大量寫的場景,因為主庫無法水準擴充;
  • 從庫的資料都是全量并且重複的,浪費存儲空間;
  • 資料一緻性問題;
  • 資料同步延遲問題;

二、垂直分庫

在資料庫遇到IO瓶頸過程中,如果IO集中在某一塊的業務中,這個時候可以考慮的就是垂直分庫,将熱點業務拆分出去,避免由熱點業務的密集IO請求影響了其他正常業務,是以垂直分庫也叫業務分庫。

資料庫架構選型與落地,看這篇就夠了

優點:

  • 業務清晰,專庫專用,契合服務化的拆分;
  • 冷熱資料分離,業務資料互相獨立各不幹擾;
  • 獨立的資料庫便于日常疊代和維護;

缺點:

  • 資料之間無法聯表,隻能在程式中多次查詢組合或者備援資料;
  • 需要解決分布式事務問題;

三、水準分片

在資料庫遇到存儲瓶頸的時候,由于資料量過大造成索引性能下降。

這個時候可以考慮将資料做水準拆分,針對資料量巨大的單張表,按照某種規則,切分到多張表裡面去。

但是這些表還是在同一個庫中,是以庫級别的資料庫操作還是有IO瓶頸(單個伺服器的IO有上限)。

是以水準分表主要還是針對資料量較大,整體業務請求量較低的場景。

資料庫架構選型與落地,看這篇就夠了

優點:

  • 單表資料量減少,性能得到提升;
  • 資料都在同個資料庫中,沒有跨庫事務問題;

缺點:

  • 需要解決跨片查詢問題;
  • 資料分片在擴容時需要遷移;
  • 存在IO瓶頸,無法處理大量業務請求;

四、分庫分表

在資料庫遇到存儲瓶頸和IO瓶頸的時候,資料量過大造成索引性能下降,加上同一時間需要處理大規模的業務請求,這個時候單庫的IO上限會限制處理效率。

是以需要将單張表的資料切分到多個伺服器上去,每個伺服器具有相應的庫與表,隻是表中資料集合不同。

分庫分表能夠有效地緩解單機和單庫的性能瓶頸和壓力,突破IO、連接配接數、硬體資源等的瓶頸。

資料庫架構選型與落地,看這篇就夠了

優點:

  • 單表資料量減少,性能得到提升;
  • 資料分散在多個伺服器,提高了整體系統的負載能力;

缺點:

  • 需要解決跨片查詢問題;
  • 資料分片在擴容時需要遷移;
  • 需要處理廣播表(公共表)問題;
  • 需要解決分布式事務問題;

注:分庫還是分表核心關鍵是有沒有IO瓶頸。

分片方式都有什麼呢?

RANGE(範圍分片)

将業務表中的某個關鍵字段排序後,按照順序從0到10000一個表,10001到20000一個表。最常見的就是按照時間切分(月表、年表)。

比如将6個月前,甚至一年前的資料切出去放到另外的一張表,因為随着時間流逝,這些表的資料被查詢的機率變小,銀行的交易記錄多數是采用這種方式。

優點:

  • 天然便于水準擴充,後期想對整個分片叢集擴容時,隻需要添加節點即可,無需對其他分片的資料進行遷移;
  • 使用分片字段進行範圍查找時,連續分片可快速定位分片進行快速查詢,有效避免跨分片查詢的問題。

缺點:

  • 熱點資料成為性能瓶頸。連續分片可能存在資料熱點,例如按時間字段分片,有些分片存儲最近時間段内的資料,可能會被頻繁地讀寫,而有些分片存儲的曆史資料,則很少被查詢。

HASH(哈希分片)

将訂單作為主表,然後将其相關的業務表作為附表,取使用者id然後hash取模,配置設定到不同的資料表或者資料庫上。

優點:

  • 資料分片相對比較均勻,不容易出現熱點和并發通路的瓶頸。

缺點:

  • 後期分片叢集擴容時,需要遷移舊的資料,容易面臨跨分片查詢的複雜問題。比如上例中,如果頻繁用到的查詢條件中不帶使用者id時,将會導緻無法定位資料庫,進而需要同時向所有庫發起查詢,再在記憶體中合并資料,取最小集傳回給應用,分庫反而成為拖累。

講到這裡,我們已經知道資料庫有哪些架構,解決的是哪些問題,是以,我們在日常設計中需要根據資料的特點,資料的傾向性,資料的安全性等來選擇不同的架構。

那麼,我們應該如何選擇資料庫架構呢?

雖然把上面的架構全部組合在一起可以形成一個強大的高可用,高負載的資料庫系統,但是架構選擇合适才是最重要的。

混合架構雖然能夠解決所有的場景的問題,但是也會面臨更多的挑戰,你以為的完美架構,背後其實有着更多的坑。

是以,在選擇資料庫架構之前,我們先思考一下,實際上會面臨什麼樣的難題?

1、對事務支援

分庫分表後(無論是垂直還是水準拆分),就成了分布式事務了,如果依賴資料庫本身的分布式事務管理功能去執行事務,将付出高昂的性能代價(XA事務);如果由應用程式去協助控制,形成程式邏輯上的事務,又會造成程式設計方面的負擔(TCC、SAGA)。

2、多庫結果集合并(group by,order by)

由于資料分布于不同的資料庫中,無法直接對其做分頁、分組、排序等操作,一般應對這種多庫結果集合并的查詢業務都需要采用資料清洗、同步等其他手段處理(TIDB、KUDU等)。

3、資料延遲

主從架構下的多副本機制和水準分庫後的聚合庫都會存在主資料和副本資料之間的延遲問題。

4、跨庫join

分庫分表後表之間的關聯操作将受到限制,我們無法join位于不同分庫的表(垂直),也無法join分表粒度不同的表(水準), 結果原本一次查詢就能夠完成的業務,可能需要多次查詢才能完成。

5、分片擴容

水準分片之後,一旦需要做擴容時。需要将對應的資料做一次遷移,成本代價都極高的。

6、ID生成

分庫分表後由于資料庫獨立,原有的基于資料庫自增ID将無法再使用,這個時候需要采用其他外部的ID生成方案。

0x2.資料庫的選型與落地

那麼,從技術種類上看,都有哪些呢?大廠的選擇又會是怎樣的呢?

一、應用層依賴類(JDBC)

這類分庫分表中間件的特點就是和應用強耦合,需要應用顯示依賴相應的jar包(以Java為例),比如知名的TDDL、當當開源的sharding-jdbc、蘑菇街的TSharding等。

此類中間件的基本思路就是重新實作JDBC的API,通過重新實作DataSource、PrepareStatement等操作資料庫的接口,讓應用層在基本不改變業務代碼的情況下透明地實作分庫分表的能力。

中間件給上層應用提供熟悉的JDBC API,内部通過sql解析、sql重寫、sql路由等一系列的準備工作擷取真正可執行的sql,然後底層再按照傳統的方法(比如資料庫連接配接池)擷取實體連接配接來執行sql,最後把資料結果合并處理成ResultSet傳回給應用層。

優點

  • 無需額外部署,隻要和應用綁定一起釋出即可;
  • 任意資料庫都可以使用;

缺點

  • 不能跨語言,比如Java寫的sharding-jdbc顯然不能用在C#項目中;
  • 資料庫連接配接數消耗高(各個應用節點的連接配接池不共享);

二、中間層代理類(Proxy)

這類分庫分表中間件的核心原理是在應用和資料庫的連接配接之間搭起一個代理層,上層應用以标準的MySQL協定來連接配接代理層,然後代理層負責轉發請求到底層的MySQL實體執行個體,這種方式對應用隻有一個要求,就是隻要用MySQL協定來通信即可。

是以用MySQL Navicat這種純的用戶端都可以直接連接配接你的分布式資料庫,自然也天然支援所有的程式設計語言。

在技術實作上除了和應用層依賴類中間件基本相似外,代理類的分庫分表産品必須實作标準的MySQL協定,某種意義上講資料庫代理層轉發的就是MySQL協定請求,就像Nginx轉發的是Http協定請求。

比較有代表性的産品有開創性質的Amoeba、阿裡開源的Cobar、社群發展比較好的Mycat(基于Cobar開發)等。

優點

  • 跨語言,任何程式設計語言都可以使用;
  • 資料庫連接配接數消耗低(連接配接池由代理層持有,是以可以複用);

缺點

  • 隻支援少量資料庫(MYSQL/PostgreSQL);
  • 需要單獨部署,形成中心化架構(一旦當機系統将不可用);
  • 多了一層代理層,性能下降;

接下來談談落地方案。

JDBC方案:無中心化架構,相容市面上大多數關系型資料庫,适用于開發高性能的輕量級 OLTP 應用(面向前台)。

Proxy方案:提供靜态入口以及異構語言的支援,适用于 OLAP 應用(面向背景)以及對分片資料庫進行管理和運維的場景。

混合方案:在大型複雜系統中存在面向C端使用者的前台應用,也有面向企業分析的背景應用,這個時候就可以采用混合模式。

資料庫架構選型與落地,看這篇就夠了

JDBC 采用無中心化架構,适用于 Java 開發的高性能的輕量級 OLTP 應用;Proxy 提供靜态入口以及異構語言的支援,适用于 OLAP 應用以及對分片資料庫進行管理和運維的場景。

0x3.ShardingSphere

ShardingSphere是一套開源的分布式資料庫中間件解決方案組成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(計劃中)這3款互相獨立的産品組成,他們均提供标準化的資料分片、分布式事務和資料庫治理功能,可适用于如Java同構、異構語言、容器、雲原生等各種多樣化的應用場景。

ShardingSphere提供的核心功能:

  1. 資料分片
  2. 分庫 & 分表
  3. 讀寫分離
  4. 分片政策定制化
  5. 無中心化分布式主鍵
  6. 分布式事務
  7. 标準化事務接口
  8. XA強一緻事務
  9. 柔性事務
資料庫架構選型與落地,看這篇就夠了

Sharding-Proxy

定位為透明化的資料庫代理端,提供封裝了資料庫二進制協定的服務端版本,用于完成對異構語言的支援。

目前已提供MySQL版本,它可以使用任何相容MySQL協定的通路用戶端(如:MySQL Command Client, MySQL Workbench, Navicat等)操作資料,對DBA更加友好。

向應用程式完全透明,可直接當做MySQL使用。

适用于任何相容MySQL協定的用戶端。

資料庫架構選型與落地,看這篇就夠了

Sharding-JDBC

定位為輕量級Java架構,在Java的JDBC層提供的額外服務。 它使用用戶端直連資料庫,以jar包形式提供服務,無需額外部署和依賴,可了解為增強版的JDBC驅動,完全相容JDBC和各種ORM架構。

  • 适用于任何基于JDBC的ORM架構,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
  • 支援任何第三方的資料庫連接配接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
  • 支援任意實作JDBC規範的資料庫。目前支援MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标準的資料庫。
資料庫架構選型與落地,看這篇就夠了

0x4.應用案例

以電商SaaS系統為例,前台應用采用Sharding-JDBC,根據業務場景的差異主要分為三種方案。

分庫(使用者)

問題解析:頭部企業日活高并發高,單獨分庫避免幹擾其他企業使用者,使用者資料的增長緩慢可以不分表。

拆分次元:企業ID分庫

拆分政策:頭部企業單獨庫、非頭部企業一個庫

分庫分表(訂單)

問題解析:訂單資料增長速度較快,在分庫之餘需要分表。

拆分次元:企業ID分庫、使用者ID分表

拆分政策:頭部企業單獨庫、非頭部企業一個庫,分庫之後使用者ID取模拆分表

單庫分表(附件)

問題解析:附件資料特點是并發量不大,隻需要解決資料增長問題,是以單庫IO足以支撐的情況下分表即可。

拆分次元:使用者ID分表

拆分政策:使用者ID取模分表

随着而來的問題

問題一:分布式事務

分布式事務過于複雜也是分布式系統最難處理的問題,由于篇幅有限,後續會開篇專講這一塊内容。

問題二:分布式ID

  1. 簡單粗暴的UUID:生成足夠簡單,本地生成無網絡消耗,具有唯一性。缺點:無序、無業務意義、過長
  2. 基于Redis模式:利用redis的 incr指令實作ID的原子性自增。缺點:需要開啟持久化,有可能丢失
  3. 雪花算法:Snowflake ID組成結構:正數位(占1比特)+ 時間戳(占41比特)+ 機器ID(占5比特)+ 資料中心(占5比特)+ 自增值(占12比特),總共64比特組成的一個Long類型。
  4. 美團leaf:提供雪花和程式自增方案,其中雪花依賴ZK,程式自增依賴資料庫。

問題三:跨片查詢

舉個例子,以使用者id分片之後,需要根據企業id查詢企業所有使用者資訊。

  1. 索引表法:建立索引表,存儲使用者id和企業id的關系。查詢時先通過索引表找到對應使用者id之後,再查詢資料庫。缺點:多一次資料庫查詢,性能下降。使用者和企業關系變動時,需要維護資料。
  2. 基因法:截取使用者id的部分資料加入到企業id,例如使用者id後3位直接加到企業id中,然後分片時根據使用者id後3位計算分片,這個時候使用企業id也可以提取這三位數來計算分片位置,缺點是,隻能針對單一字段,并且對資料有一定的入侵幹擾。
  3. 寬表法:使用NOSQL建立寬表(全量表),提供對應的查詢能力。異構下的讀寫分離,缺點是,引入中間件成本、資料一緻性問題,資料延遲問題;

sharding針對跨片查詢也是能夠支援的,本質上sharding的跨片查詢是采用同時查詢多個分片的資料,然後聚合結果傳回,這個方式對資源耗費比較大,特别是對資料庫連接配接資源的消耗。

假設分4個資料庫,8個表,則sharding會同時發出32個SQL去查詢。一下子消耗掉了32個連接配接;

特别是針對單庫分表的情況要注意,假設單庫分64個表,則要消耗64個連接配接。如果我們部署了2個節點,這個時候兩個節點同時查詢的話,就會遇到資料庫連接配接數上限問題(mysql預設100連接配接數)

問題四:分片擴容

随着資料增長,每個片區的資料也會達到瓶頸,這個時候需要将原有的分片數量進行增加。由于增加了片區,原先的hash規則也跟着變化,造成了需要将舊資料做遷移。

假設原先1個億的資料,hash分64個表,現在增長到50億的資料,需要擴容到128個表,一旦擴容就需要将這50億的資料做一次遷移,遷移成本是無法想象的。

問題五:一緻性哈希

首先,求出每個伺服器的hash值,将其配置到一個 0~2^n 的圓環上(n通常取32)

其次,用同樣的方法求出待存儲對象的主鍵 hash值,也将其配置到這個圓環上。

然後,從資料映射到的位置開始順時針查找,将資料分布到找到的第一個伺服器節點上。

一緻性hash的優點在于加入和删除節點時隻會影響到在哈希環中相鄰的節點,而對其他節點沒有影響。

是以使用一緻性哈希在叢集擴容過程中可以減少資料的遷移。

資料庫架構選型與落地,看這篇就夠了

0x5.總結一下

  1. 資料庫架構可以分為主從架構、垂直架構、水準架構,分别解決高可用、密集IO、大規模資料存儲問題。
  2. 每個架構會面臨不同的難題,資料延遲、分布式事務、跨片查詢、擴容等等,越複雜的架構面對的難題越多,而我們應該把這些難題考慮進去。
  3. 對于架構的選擇上,架構不是越複雜越好,應用在不同的階段需要針對問題點選擇合适的架構。
  4. 從技術種類上看,實作分庫分表技術的中間件分為JDBC方案和PROXY方案。JDBC性能高,跨資料庫;PROXY省連接配接,跨語言。
  5. ShardingSphere是一個功能強大的中間件,支援多種方案(JDBC、PROXY、混合),可以快速實作資料庫讀寫分離、資料庫分片等技術方案。
  6. 根據業務的特點可以選擇不同的分庫方案,在複雜的資料庫架構中,需要解決分布式事務、分布式id、跨片查詢、分片擴容等等問題。

好了,這次分享到這裡,我們日常的實踐可能隻會用到其中一種方案,但它不是資料庫架構的全貌,打開技術視野,才能更好地把存儲工具利用起來。

資料庫架構選型與落地,看這篇就夠了

老規矩,一鍵三連,日入兩千,點贊在看,年薪百萬!

本文作者:Jensen

7年Java老兵,小米主題設計師,手機輸入法設計師,ProcessOn特邀講師。

曾涉獵航空、電信、IoT、垂直電商産品研發,現就職于某知名電商企業。

技術公衆号【架構師修行錄】号主,專注于分享日常架構、技術、職場幹貨,關注回複“DDD”領學習DDD領域模組化。