Java面試題029:一文深入了解MySQL(1)
Java面試題030:一文深入了解MySQL(2)
Java面試題031:一文深入了解MySQL(3)
Java面試題032:一文深入了解MySQL(4)
Java面試題033:一文深入了解MySQL(5)
1、分庫分表簡介
????????隨著業務規模的擴大和系統復雜度的增長,數據量和訪問量也同時增加,關系型數據庫本身比較容易成為系統瓶頸,單機存儲容量、連接數、處理能力都有限。當單表的數據量達到1000W或100G以后,由于查詢維度較多,即使添加從庫、優化索引,做很多操作時性能仍下降嚴重。單個服務的磁盤空間是有限制的,并發壓力下,所有的請求都訪問同一個節點,肯定會對磁盤IO造成非常大的影響。此時就要考慮對其進行切分了,切分的目的就在于減少數據庫的負擔,縮短查詢時間。
????????分庫:將一個庫的數據按照一定規則拆分到多個庫中
????????分表:把一個表的數據按照一定規則放到多個表中
????????當單個庫太大時,先要看一下是因為表太多還是數據量太大,如果是表太多,則應該將部分表進行遷移(可以按業務區分),這就是垂直切分。如果是數據量太大,則需要將表拆成更多的小表,來減少單表的數據量,這就是水平拆分。
2、四種實現形式
(1)垂直分庫:是指在一個系統中的按照不同業務進行拆分,將不同的業務數據存放到不同的數據庫中。典型的案例是微服務架構中不同的業務微服務可以將自己的業務數據存放到自己的數據源中,以服務的形式向系統提供數據查詢。
(2)垂直分表:一般是表中的字段較多時,按照業務需要將列字段拆分為多個表。
????????垂直分庫(表)是將不同的數據進行分別存放,每個庫或表存放的數據差別很大。
????????當一個應用難以再細粒度的垂直切分,或切分后數據量行數巨大,存在單庫讀寫、存儲性能瓶頸,這時候就需要進行水平切分了,可以理解為負載均衡。
(3)水平分庫:每個庫(表)的結構都一樣,數據都不一樣。它們的并集就是全量數據。
(4)水平分表:每個庫(表)的結構都一樣,數據都不一樣。它們的并集就是全量數據。
3、分庫分表規則
(1)根據數值范圍range
????????按照時間區間或ID區間來切分。例如:按日期將不同月甚至是日的數據分散到不同的庫中;將userId為1~9999的記錄分到第一個庫,10000~20000的分到第二個庫。
優點:
- 單表大小可控
- 便于水平擴展,后期想對集群擴容時,只需添加節點即可,無需對其他分片的數據進行遷移
- 使用分片字段進行范圍查找時,連續分片可快速定位分片,有效避免跨分片查詢的問題。
缺點:
- 熱點數據成為性能瓶頸。連續分片可能存在數據熱點,例如按時間字段分片,有些分片存儲最近時間段內的數據,可能會被頻繁的讀寫,而有些分片存儲的歷史數據,則很少被查詢。
??某些系統中使用的“冷熱數據分離“,將一些使用較少的歷史數據遷移到其他庫中,業務功能上只提供熱點數據的查詢,也是類似的實踐。
(2)根據數值取模Hash
????????對字段值進行哈希運算后取余分區,適用于數據分布均勻的場景。
????????例如:將 Customer 表根據 cusno 字段切分到4個庫中,余數為0的放到第一個庫,余數為1的放到第二個庫,以此類推。這樣同一個用戶的數據會分散到同一個庫中,如果查詢條件帶有cusno字段,則可明確定位到相應庫去查詢。
優點:
- 數據分片相對比較均勻,不容易出現熱點和并發訪問的瓶頸
缺點:
-
后期分片集群擴容時,需要遷移舊的數據(使用一致性hash算法能較好的避免這個問題),否則會導致歷史數據失效。
-
容易面臨跨分片查詢的復雜問題。比如上例中,如果頻繁用到的查詢條件中不帶cusno時,將會導致無法定位數據庫,從而需要同時向4個庫發起查詢,再在內存中合并數據,取最小集返回給應用,分庫反而成為拖累。
4、實現方式
????????分庫分表的開源框架中常用的就是sharding-sphere和Mycat。
????????sharding-sphere和Mycat后續會有專門單獨的章節進行梳理。
? ? ? ? 一般分庫分表步驟分為:根據容量(當前容量和增長量)評估分庫或分表個數 -> 選key(均勻)-> 分表規則(hash或range等)-> 執行(一般雙寫)-> 擴容問題(盡量減少數據的移動)。
(1)Mycat
Mycat是一個強大的數據庫中間件,不僅僅可以用作讀寫分離、以及分表分庫、容災備份,而且可以用于多租戶應用開發、云平臺基礎設施、讓你的架構具備很強的適應性和靈活性,借助于即將發布的Mycat智能優化模塊,系統的數據訪問瓶頸和熱點一目了然,根據這些統計分析數據,你可以自動或手工調整后端存儲,將不同的表映射到不同存儲引擎上,而整個應用的代碼一行也不用改變。
????????Mycat的原理中最重要的一個動詞是“攔截”,它攔截用戶發送過來的SQL語句,首先對SQL語句做了一些特定的分析:如分片分析、路由分析、讀寫分離分析、緩存分析等,然后將此SQL發往后端的真實數據庫,并將返回的結果做適當的處理,最終再返回給用戶。
????????Mycat典型的應用場景:
-
單純的讀寫分離,此時配置最為簡單,支持讀寫分離,主從切換;
-
分表分庫,對于超過1000萬的表進行分片,最大支持1000億的單表分片;
-
多租戶應用,每個應用一個庫,應用程序只連接Mycat,不改造程序本身,實現多租戶化;
-
報表系統,借助Mycat的分表能力,處理大規模報表的統計;
-
替代Hbase,分析大數據;
-
作為海量數據實時查詢的一種簡單有效方案,比如100億條頻繁查詢的記錄需要在3秒內查詢出來結果,除了基于主鍵的查詢,還可能存在范圍查詢或其他屬性查詢,此時Mycat可能是最簡單有效的選擇。
Mycat高可用方案
????????Mycat系統的高可用涉及到Mycat本身的高可用以及后端MySQL的高可用,大多數情況下,建議采用標準的MySQL主從復制高可用性配置。
????????MySQL節點開啟主從復制的配置方案,并將主節點配置為Mycat的dataHost里的writeNode,從節點配置為readNode,同時Mycat內部定期對一個dataHost里的所有writeHost與readHost節點發起心跳檢測,正常情況下,Mycat會將第一個writeHost作為寫節點,所有的DML SQL會發送給此節點,若Mycat開啟了讀寫分離,則查詢節點會根據讀寫分離的策略發往readHost(+writeHost)執行,當一個dataHost里面配置了兩個或多個writeHost的情況下,如果第一個writeHost宕機,則Mycat會在默認的3次心跳檢查失敗后,自動切換到下一個可用的writeHost執行DML SQL語句。
????????Mycat自身的高可用性,官方建議是采用基于硬件的負載均衡器或者軟件方式的HAproxy,HAProxy相比LVS的使用要簡單很多,功能方面也很豐富,免費開源。(HAproxy+Mycat集群+MySQL主從所組成的高可用性方案)
(2)sharding-sphere
????????Apache ShardingSphere 是一款分布式的數據庫生態系統, 可以將任意數據庫轉換為分布式數據庫,并通過數據分片、彈性伸縮、加密等能力對原有數據庫進行增強。它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar這3款相互獨立的產品組成。
????????ShardingSphere-JDBC 定位為輕量級 Java 框架,在 Java 的 JDBC 層提供的額外服務。 它使用客戶端直連數據庫,以 jar 包形式提供服務,無需額外部署和依賴,可理解為增強版的 JDBC 驅動,完全兼容 JDBC 和各種 ORM 框架。
- 適用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC;
- 支持任何第三方的數據庫連接池,如:DBCP, C3P0, BoneCP, HikariCP 等;
- 支持任意實現 JDBC 規范的數據庫,目前支持 MySQL,PostgreSQL,Oracle,SQLServer 以及任何可使用 JDBC 訪問的數據庫。
????????ShardingSphere-Proxy 定位為透明化的數據庫代理端,通過實現數據庫二進制協議,對異構語言提供支持。 目前提供 MySQL 和 PostgreSQL 協議,透明化數據庫操作,對 DBA 更加友好。
????????通過混合使用 ShardingSphere-JDBC 和 ShardingSphere-Proxy,并采用同一注冊中心統一配置分片策略,能夠靈活的搭建適用于各種場景的應用系統。
????????數據分片
????????指按照某個維度將存放在單一數據庫中的數據分散地存放至多個數據庫或表中以達到提升性能瓶頸以及可用性的效果。 數據分片的有效手段是對關系型數據庫進行分庫和分表。ShardingSphere 基于底層數據庫提供分布式數據庫解決方案,可以水平擴展計算和存儲。
????????ShardingSphere通過YAML 配置方式,實現數據分片,在 YAML 文件中配置數據分片規則,包含數據源、分片規則、全局屬性等配置項,然后調用 YamlShardingSphereDataSourceFactory 對象的 createDataSource 方法,根據配置信息自動完成ShardingSphereDataSource 對象的創建。
rules:
- !SHARDINGtables: # 數據分片規則配置<logic_table_name> (+): # 邏輯表名稱actualDataNodes (?): # 由數據源名 + 表名組成(參考 Inline 語法規則)databaseStrategy (?): # 分庫策略,缺省表示使用默認分庫策略,以下的分片策略只能選其一standard: # 用于單分片鍵的標準分片場景shardingColumn: # 分片列名稱shardingAlgorithmName: # 分片算法名稱complex: # 用于多分片鍵的復合分片場景shardingColumns: # 分片列名稱,多個列以逗號分隔shardingAlgorithmName: # 分片算法名稱hint: # Hint 分片策略shardingAlgorithmName: # 分片算法名稱none: # 不分片tableStrategy: # 分表策略,同分庫策略keyGenerateStrategy: # 分布式序列策略column: # 自增列名稱,缺省表示不使用自增主鍵生成器keyGeneratorName: # 分布式序列算法名稱auditStrategy: # 分片審計策略auditorNames: # 分片審計算法名稱- <auditor_name>- <auditor_name>allowHintDisable: true # 是否禁用分片審計hintautoTables: # 自動分片表規則配置t_order_auto: # 邏輯表名稱actualDataSources (?): # 數據源名稱shardingStrategy: # 切分策略standard: # 用于單分片鍵的標準分片場景shardingColumn: # 分片列名稱shardingAlgorithmName: # 自動分片算法名稱bindingTables (+): # 綁定表規則列表- <logic_table_name_1, logic_table_name_2, ...> - <logic_table_name_1, logic_table_name_2, ...> defaultDatabaseStrategy: # 默認數據庫分片策略defaultTableStrategy: # 默認表分片策略defaultKeyGenerateStrategy: # 默認的分布式序列策略defaultShardingColumn: # 默認分片列名稱# 分片算法配置shardingAlgorithms:<sharding_algorithm_name> (+): # 分片算法名稱type: # 分片算法類型props: # 分片算法屬性配置# ...# 分布式序列算法配置keyGenerators:<key_generate_algorithm_name> (+): # 分布式序列算法名稱type: # 分布式序列算法類型props: # 分布式序列算法屬性配置# ...# 分片審計算法配置auditors:<sharding_audit_algorithm_name> (+): # 分片審計算法名稱type: # 分片審計算法類型props: # 分片審計算法屬性配置# ...- !BROADCASTtables: # 廣播表規則列表- <table_name>- <table_name>
配置示例:
dataSources:ds_0:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.jdbc.DriverjdbcUrl: jdbc:mysql://localhost:3306/demo_ds_0?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8username: rootpassword:ds_1:dataSourceClassName: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.jdbc.DriverjdbcUrl: jdbc:mysql://localhost:3306/demo_ds_1?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8username: rootpassword:rules:
- !SHARDINGtables: # 數據分片規則配置t_order: # 邏輯表名稱actualDataNodes: ds_${0..1}.t_order_${0..1} # 由數據源名 + 表名組成(參考 Inline 語法規則)tableStrategy: # 分表策略,缺省表示使用默認分表策略,以下的分片策略只能選其一standard:shardingColumn: order_idshardingAlgorithmName: t_order_inlinekeyGenerateStrategy:column: order_idkeyGeneratorName: snowflakeauditStrategy:auditorNames:- sharding_key_required_auditorallowHintDisable: truet_order_item:actualDataNodes: ds_${0..1}.t_order_item_${0..1}tableStrategy:standard:shardingColumn: order_idshardingAlgorithmName: t_order_item_inlinekeyGenerateStrategy:column: order_item_idkeyGeneratorName: snowflaket_account:actualDataNodes: ds_${0..1}.t_account_${0..1}tableStrategy:standard:shardingAlgorithmName: t_account_inlinekeyGenerateStrategy:column: account_idkeyGeneratorName: snowflakedefaultShardingColumn: account_idbindingTables:- t_order,t_order_itemdefaultDatabaseStrategy:standard:shardingColumn: user_idshardingAlgorithmName: database_inlinedefaultTableStrategy:none:shardingAlgorithms:database_inline:type: INLINEprops:algorithm-expression: ds_${user_id % 2}t_order_inline:type: INLINEprops:algorithm-expression: t_order_${order_id % 2}t_order_item_inline:type: INLINEprops:algorithm-expression: t_order_item_${order_id % 2}t_account_inline:type: INLINEprops:algorithm-expression: t_account_${account_id % 2}keyGenerators:snowflake:type: SNOWFLAKEauditors:sharding_key_required_auditor:type: DML_SHARDING_CONDITIONS- !BROADCASTtables:- t_addressprops:sql-show: false
YamlShardingSphereDataSourceFactory.createDataSource(getFile("/META-INF/sharding-databases-tables.yaml"));
分片鍵
????????用于將數據庫(表)水平拆分的數據庫字段。 例:將訂單表中的訂單主鍵的尾數取模分片,則訂單主鍵為分片字段。 SQL 中如果無分片字段,將執行全路由,性能較差。 除了對單分片字段的支持,Apache ShardingSphere 也支持根據多個字段進行分片。
分片算法
????????ShardingSphere 內置提供了多種分片算法,按照類型可以劃分為自動分片算法、標準分片算法、復合分片算法和 Hint 分片算法,能夠滿足用戶絕大多數業務場景的需要。此外,考慮到業務場景的復雜性,內置算法也提供了自定義分片算法的方式,用戶可以通過編寫 Java 代碼來完成復雜的分片邏輯。
(1)自動分片算法
????????自動分片算法的分片邏輯由 ShardingSphere 自動管理,需要通過配置 autoTables 分片規則進行使用。
- 取模分片算法:MOD
- 哈希取模分片算法:HASH_MOD
- 基于分片容量的范圍分片算法:VOLUME_RANGE
- 基于分片邊界的范圍分片算法:BOUNDARY_RANGE
-
自動時間段分片算法:AUTO_INTERVAL
(2)標準分片算法
用于處理使用單一鍵作為分片鍵的?=
、IN
、BETWEEN AND
、>
、<
、>=
、<=
?進行分片的場景。
Apache ShardingSphere 內置的標準分片算法實現類包括:
- 行表達式分片算法:提供對 SQL 語句中的?
=
?和?IN
?的分片操作支持,只支持單分片鍵。 - 時間范圍分片算法
(3)復合分片算法
????????用于處理使用多鍵作為分片鍵進行分片的場景,包含多個分片鍵的邏輯較復雜,需要應用開發者自行處理其中的復雜度。
????????行表達式:簡化數據節點配置工作量
????????由兩部分組成,分別是字符串開頭的對應 SPI 實現的 Type Name 部分和表達式部分。
${['online', 'offline']}_table${1..3}
最終會解析為:
online_table1, online_table2, online_table3, offline_table1, offline_table2, offline_table3
(4)Hint 分片算法
????????用于處理使用?Hint
?行分片的場景。