據(jù)庫與數(shù)據(jù)存儲(chǔ):分庫分表實(shí)戰(zhàn))
1. 引言在互聯(lián)網(wǎng)業(yè)務(wù)高速發(fā)展的今天單庫單表往往成為系統(tǒng)性能的瓶頸。當(dāng)數(shù)據(jù)量達(dá)到千萬級(jí)甚至億級(jí)時(shí)數(shù)據(jù)庫的讀寫性能會(huì)急劇下降索引膨脹、鎖競爭、連接數(shù)耗盡等問題接踵而至。此時(shí)分庫分表便成為Java后端架構(gòu)中不可或缺的優(yōu)化手段。本文將從實(shí)際業(yè)務(wù)場景出發(fā)系統(tǒng)講解分庫分表的核心概念、主流中間件Apache ShardingSphere的實(shí)戰(zhàn)用法、分布式ID的生成方案以及分庫分表后必然面臨的跨庫查詢難題與應(yīng)對(duì)策略。文章配有大量可運(yùn)行的代碼示例幫助讀者從理論走向落地。2. 為什么需要分庫分表2.1 單庫單表的瓶頸在業(yè)務(wù)初期一個(gè)數(shù)據(jù)庫實(shí)例、一張大表往往能支撐起整個(gè)系統(tǒng)。但隨著用戶量和數(shù)據(jù)量的增長會(huì)出現(xiàn)以下問題存儲(chǔ)瓶頸單表數(shù)據(jù)量過大B樹索引層級(jí)加深查詢IO次數(shù)增多。寫入瓶頸單庫寫入并發(fā)有限主從延遲放大。連接瓶頸數(shù)據(jù)庫連接數(shù)有限高并發(fā)下連接池被占滿。運(yùn)維瓶頸大表DDL如加索引、加字段耗時(shí)極長甚至鎖表。2.2 分庫分表的兩種維度維度說明典型場景垂直拆分按業(yè)務(wù)模塊拆庫/拆表如訂單庫、用戶庫、商品庫微服務(wù)化、模塊解耦水平拆分按某個(gè)字段分片鍵將數(shù)據(jù)分散到多個(gè)庫/表如按用戶ID取模單表數(shù)據(jù)量巨大、寫入并發(fā)高實(shí)際項(xiàng)目中通常是先垂直拆分再對(duì)核心大表做水平拆分。3. 分庫分表核心概念在動(dòng)手實(shí)踐前需要先理解幾個(gè)關(guān)鍵術(shù)語邏輯表對(duì)用戶而言操作的是邏輯表名如t_order實(shí)際數(shù)據(jù)分散在多個(gè)物理表中。物理表真實(shí)存儲(chǔ)數(shù)據(jù)的表如t_order_0、t_order_1。分片鍵Sharding Key用于計(jì)算數(shù)據(jù)歸屬的字段如order_id、user_id。分片算法決定數(shù)據(jù)如何分布常見有取模、哈希、范圍、時(shí)間等。數(shù)據(jù)節(jié)點(diǎn)一個(gè)物理表實(shí)例如ds0.t_order_0。4. ShardingSphere 實(shí)戰(zhàn)4.1 ShardingSphere 簡介Apache ShardingSphere 是一套開源的分布式數(shù)據(jù)庫中間件解決方案由三個(gè)產(chǎn)品組成ShardingSphere-JDBC輕量級(jí)Java框架以jar包形式提供服務(wù)適合單體或微服務(wù)應(yīng)用。ShardingSphere-Proxy透明數(shù)據(jù)庫代理以獨(dú)立服務(wù)形式部署對(duì)應(yīng)用無侵入。ShardingSphere-Sidecar云原生環(huán)境下的代理目前演進(jìn)中。本文重點(diǎn)講解最常用的ShardingSphere-JDBC。4.2 環(huán)境準(zhǔn)備!-- pom.xml 引入依賴 --dependencygroupIdorg.apache.shardingsphere/groupIdartifactIdshardingsphere-jdbc-core-spring-boot-starter/artifactIdversion5.4.1/version/dependency4.3 配置文件application.ymlspring:shardingsphere:datasource:names:ds0,ds1ds0:type:com.zaxxer.hikari.HikariDataSourcedriver-class-name:com.mysql.cj.jdbc.Driverjdbc-url:jdbc:mysql://localhost:3306/order_db_0?useSSLfalseusername:rootpassword:rootds1:type:com.zaxxer.hikari.HikariDataSourcedriver-class-name:com.mysql.cj.jdbc.Driverjdbc-url:jdbc:mysql://localhost:3306/order_db_1?useSSLfalseusername:rootpassword:rootrules:sharding:tables:t_order:actual-data-nodes:ds$-{0..1}.t_order_$-{0..1}table-strategy:standard:sharding-column:order_idsharding-algorithm-name:order_inlinekey-generate-strategy:column:order_idkey-generator-name:snowflakesharding-algorithms:order_inline:type:INLINEprops:algorithm-expression:t_order_$-{order_id % 2}key-generators:snowflake:type:SNOWFLAKEprops:sql-show:true4.4 實(shí)體與MapperDataTableName(t_order)publicclassOrder{privateLongorderId;privateLonguserId;privateBigDecimalamount;privateLocalDateTimecreateTime;}MapperpublicinterfaceOrderMapperextendsBaseMapperOrder{// 使用 MyBatis-Plus無需額外SQL}4.5 寫入與查詢測(cè)試ServicepublicclassOrderService{ResourceprivateOrderMapperorderMapper;publicvoidinsertOrder(){for(inti0;i10;i){OrderordernewOrder();order.setUserId(1000Li);order.setAmount(newBigDecimal(99.90));order.setCreateTime(LocalDateTime.now());orderMapper.insert(order);// order_id 由雪花算法自動(dòng)生成}}publicOrderqueryOrder(LongorderId){// 根據(jù)分片鍵查詢ShardingSphere 自動(dòng)路由到正確的物理表returnorderMapper.selectById(orderId);}}注意查詢條件必須包含分片鍵否則會(huì)觸發(fā)全庫全表路由廣播查詢性能較差。5. 分布式ID 生成方案分庫分表后數(shù)據(jù)庫自增主鍵無法保證全局唯一因此需要分布式ID。常見方案如下5.1 方案對(duì)比方案優(yōu)點(diǎn)缺點(diǎn)UUID實(shí)現(xiàn)簡單、無中心化無序、過長影響索引性能數(shù)據(jù)庫號(hào)段有序、性能較好依賴數(shù)據(jù)庫需維護(hù)號(hào)段表Redis INCR性能高依賴Redis需考慮持久化雪花算法Snowflake趨勢(shì)遞增、高性能、無中心化依賴機(jī)器時(shí)鐘時(shí)鐘回?fù)軙?huì)出問題5.2 雪花算法原理雪花算法生成的ID為64位Long型結(jié)構(gòu)如下| 1bit 符號(hào)位 | 41bit 時(shí)間戳 | 10bit 機(jī)器ID | 12bit 序列號(hào) |41bit時(shí)間戳可表示約69年。10bit機(jī)器ID支持1024臺(tái)機(jī)器。12bit序列號(hào)同一毫秒內(nèi)可生成4096個(gè)ID。5.3 自定義雪花算法實(shí)現(xiàn)publicclassSnowflakeIdGenerator{privatefinallongworkerId;privatefinallongdatacenterId;privatelongsequence0L;privatelonglastTimestamp-1L;privatestaticfinallongTWEPOCH1288834974657L;privatestaticfinallongWORKER_ID_BITS5L;privatestaticfinallongDATACENTER_ID_BITS5L;privatestaticfinallongSEQUENCE_BITS12L;publicSnowflakeIdGenerator(longworkerId,longdatacenterId){this.workerIdworkerId;this.datacenterIddatacenterId;}publicsynchronizedlongnextId(){longtimestampSystem.currentTimeMillis();if(timestamplastTimestamp){thrownewRuntimeException(時(shí)鐘回?fù)墚惓?;}if(timestamplastTimestamp){sequence(sequence1)4095;if(sequence0){timestamptilNextMillis(lastTimestamp);}}else{sequence0L;}lastTimestamptimestamp;return((timestamp-TWEPOCH)22)|(datacenterId17)|(workerId12)|sequence;}privatelongtilNextMillis(longlastTimestamp){longtimestampSystem.currentTimeMillis();while(timestamplastTimestamp){timestampSystem.currentTimeMillis();}returntimestamp;}}生產(chǎn)環(huán)境建議直接使用 ShardingSphere 內(nèi)置的雪花算法或引入成熟的hutool、mybatis-plus內(nèi)置ID生成器。6. 跨庫查詢難題與應(yīng)對(duì)分庫分表后原本簡單的單表查詢變得復(fù)雜主要面臨以下問題6.1 常見問題跨庫JOIN數(shù)據(jù)分散在不同庫無法直接JOIN。分頁排序全局分頁需要先在各分片排序再歸并。聚合函數(shù)COUNT、SUM等需要各分片計(jì)算后匯總。分布式事務(wù)跨庫寫入需要分布式事務(wù)保證一致性。6.2 應(yīng)對(duì)策略問題解決方案跨庫JOIN冗余字段、應(yīng)用層組裝、寬表設(shè)計(jì)全局分頁使用ShardingSphere的歸并功能或禁止深分頁聚合統(tǒng)計(jì)使用ShardingSphere的分布式聚合或離線數(shù)倉分布式事務(wù)Seata AT模式、TCC、本地消息表6.3 ShardingSphere 歸并示例// 分頁查詢ShardingSphere 會(huì)自動(dòng)歸并各分片結(jié)果PageOrderpagenewPage(1,10);LambdaQueryWrapperOrderwrappernewLambdaQueryWrapper();wrapper.orderByDesc(Order::getCreateTime);orderMapper.selectPage(page,wrapper);注意深分頁如第10000頁在分庫分表場景下性能極差建議通過「游標(biāo)分頁」或「禁止跳頁」來規(guī)避。6.4 分布式事務(wù)Seata 簡介GlobalTransactionalpublicvoidcreateOrderWithDeductStock(){// 1. 插入訂單訂單庫orderMapper.insert(order);// 2. 扣減庫存庫存庫stockMapper.deduct(stockId,count);// 3. 任一失敗全局回滾}7. 分庫分表最佳實(shí)踐分片鍵選擇盡量選擇查詢頻率高、分布均勻的字段如user_id、order_id。避免跨分片查詢業(yè)務(wù)設(shè)計(jì)上盡量讓查詢帶上分片鍵。容量規(guī)劃提前評(píng)估數(shù)據(jù)增長合理設(shè)置分片數(shù)量避免后期擴(kuò)容。讀寫分離結(jié)合分庫分表通常與讀寫分離搭配使用。監(jiān)控與治理通過ShardingSphere的SQL日志、監(jiān)控面板觀察路由與性能。8. 總結(jié)分庫分表是解決海量數(shù)據(jù)存儲(chǔ)與高并發(fā)寫入的關(guān)鍵技術(shù)。本文從瓶頸分析出發(fā)介紹了垂直與水平拆分重點(diǎn)演示了ShardingSphere-JDBC的配置與使用并詳細(xì)講解了分布式ID的雪花算法實(shí)現(xiàn)最后分析了跨庫查詢的挑戰(zhàn)與應(yīng)對(duì)方案。在實(shí)際項(xiàng)目中分庫分表并非銀彈需要結(jié)合業(yè)務(wù)特點(diǎn)、數(shù)據(jù)規(guī)模、團(tuán)隊(duì)維護(hù)成本綜合權(quán)衡。建議讀者在理解原理的基礎(chǔ)上通過本地搭建環(huán)境動(dòng)手實(shí)踐才能真正掌握這門核心技能。9. 參考與延伸閱讀Apache ShardingSphere 官方文檔https://shardingsphere.apache.org/Seata 分布式事務(wù)框架https://seata.io/MyBatis-Plus 官方文檔https://baomidou.com/