在對諸如訂單、交易、支付等實時在線業務系統的研發、維護過程中,隨著業務量的快速增長,我們經常會遇到由於關係型資料庫(如:MySql)單表數據量增長過大而引發的線上事故;雖然這些事故多數時候是由於不合理的慢SQL而引起的系統雪崩,但有時也會出現由於資料庫熱點塊IO爭用而引發的系統性性能下降。總之,單表數據量的無限增長總是會在這樣或那樣的情況下增加系統的不穩定性因素。
所以在大規模實時系統的設計中,除了重點考慮應用結構的分布式化外,往往也不應該忽略資料庫實時存儲、計算能力擴展性方面的考慮。目前解決實時數據增長一般有兩種思路:一種是直接採用分布式資料庫(例如:Tidb、OceanBase之類);另一種是對關係型資料庫進行分庫分表來最大化利用現有資料庫的實時計算能力。絕大部分情況下,後一種方案往往會更現實一些。
本文的主要內容就是通過模擬一個交易系統的訂單庫,來具體演示如何通過ShardingJdbc實現交易訂單數據的分庫分表存儲。在這個過程中會到涉及分庫分表實踐的三種主要場景:1、新系統在設計之初直接使用分庫分表方案;2、歷史系統運行一段時間後如何平滑地實施分庫分表;3、對現有分庫分表邏輯的Scaling操作(包括減少分表、增加分表)涉及的數據遷移問題。
交易系統的訂單數據是分庫分表的一個非常典型場景,由於交易系統對單條數據的實時處理性能要求很高,所以一旦單個訂單表數據量規模達到10億+,就很容易出現由於資料庫熱點塊IO爭用而導致的性能下降,也很容易出現個別不謹慎的SQL操作而引起的系統性雪崩。
但一旦決定實施分庫分表就要提前做好存儲規劃,並對未來數據增長的規模進行一定的評估,同時做好未來增加分庫、增加分表的系統Scaling方案。此外,分庫分表的實施還要考慮應用的接入難度,分庫分表的細節邏輯應該對應用透明;所以一般來說我們需要一個中間代理層來屏蔽分庫分表對應用程式本身帶來的侵入。
目前在Java社區中比較知名的分庫分表代理組件就是ShardingJdbc(目前已被集成在Apache開源項目 ShardingSphere之中),ShardingJdbc本質上是一個輕量級的JDBC驅動代理,在使用的過程中只需要依賴相關Jar包即可,並不需要額外部署任何服務。通過系統配置文件就可以實現分庫分表邏輯的定義,並實現應用透明代理訪問。
接下來,我們以Spring Boot為例演示如何集成ShardingJdbc實現對交易訂單的分庫分表操作,具體步驟如下:
1)、訂單數據的分庫分表規劃
在系統設計之初,如果能夠預見到未來數據量的增長規模,那麼提前做好分庫分表規劃是非常有遠見的。從分庫分表的形式上來說,一般可以有兩類規劃方式:1)、單庫水平分表,如果單一資料庫計算能力比較強,可以在同一個庫中進行數據表的水平拆分;2)、分庫+分表,如果數據規模爆炸式增長,單庫的計算資源有限,為了提升資料庫的整體計算處理性能,也可以同時實現多個庫的分庫分表存儲。
在本文的實例中,我們將訂單數據分庫分表規劃為:1)、資料庫節點2個(ds0、ds1);2)、每個庫的分表數為32張表(0~31)。訂單表的整體數據分庫分表邏輯是根據訂單表中的「user_id欄位%2」實現分庫;然後在分庫邏輯的基礎上根據訂單表中的「order_id欄位%32」實現水平分表。例如,有條user_id為1001、訂單編號為20200713001的訂單數據,根據上述分庫分表規則1001%2=1,20200713001%32=9,那麼該數據將存儲在ds1庫中的第9個分表。
具體的訂單邏輯表結構如下:
create table t_order ( id bigint not null primary key auto_increment, order_id bigint comment &39;, trade_type varchar (30) comment &39;, amount bigint comment &39;, currency varchar (10) comment &39;, status varchar (2) comment &39;, channel varchar (10) comment &39;, trade_no varchar (32) comment &39;, user_id bigint (60) comment &39;, update_time timestamp null default current_timestamp on update current_timestamp comment &39;, create_time timestamp null default current_timestamp comment &39;, remark varchar(128) comment &39;, key unique_idx_pay_id ( order_id ), key idx_user_id ( user_id ), key idx_create_time ( create_time ));alter table t_order comment &39;;
以上邏輯表的具體分表形式為t_order_{0~31},分別分布在ds0、ds1兩個資料庫節點中。
2)、創建實驗工程代碼結構
首先創建一個基於Maven構建的Spring Boot項目,併集成MyBatis資料庫訪問框架,代碼結構如下:
如上圖所示,我們創建了一個基於Spring Boot的基本工程,並在集成了基於Mybatis的資料庫訪問功能。此外該工程還實現了單元/集成測試代碼的分離管理。
3)、SpringBoot+ShardingJdbc實現訂單分庫分表規則配置
接下來我們來看下在Spring Boot項目中如何集成ShardingJdbc,並按照規劃的分庫分表規則進行具體的配置。
首先引入ShardingJdbc針對Spring Boot項目的starter依賴包,具體如下:
<!-- 引入Sharding-JDBC Spring Boot依賴組件 --><!-- Sharding-JDBC For Spring Boot Start --><dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>${sharding-sphere.version}</version></dependency><!-- for spring namespace --><dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-namespace</artifactId> <version>${sharding-sphere.version}</version></dependency><!-- Sharding-JDBC For Spring Boot End -->
引入Spring Boot Starter依賴後,ShardingJdbc會使用自己的數據源配置邏輯,為避免衝突需要在主類中排除掉默認的數據源自動配置類,具體如下:
//排除掉默認的數據源自動配置類@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})public class OrderServerApplication { public static void main(String[] args) { SpringApplication.run(OrderServerApplication.class, args); }}
完成上述操作後,從工程邏輯上看就已經完成了ShardingJdbc與Spring Boot應用的集成。接下來我們要做的就是根據規劃的分庫分表規則,通過配置文件進行分庫分表規則的配置,具體如下:
配置真實數據源spring.shardingsphere.datasource.names=ds0,ds1 配置第2個數據源spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSourcespring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driverspring.shardingsphere.datasource.ds1.url=jdbc:mysql://127.0.0.1:3306/order_1?characterEncoding=utf-8spring.shardingsphere.datasource.ds1.username=rootspring.shardingsphere.datasource.ds1.password=123456 配置t_order表分庫策略(inline-基於行表達式的分片算法)spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column=user_idspring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression=ds${user_id % 2}如其他表有分庫分表需求,配置同上述t_order表FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: 34;orderId&34;tradeType&34;topup&34;amount&34;currency&34;cny&34;userId&FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: FCFAF2; --tt-darkmode-bgcolor: #C0BEB8;">覺得寫的還不錯的就點個讚,加個關注唄!點關注,不迷路,持續更新!!!