ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java数据库与数据存储:分库分表实战

Java数据库与数据存储:分库分表实战 1. 引言在互联网业务高速发展的今天单库单表往往成为系统性能的瓶颈。当数据量达到千万级甚至亿级时数据库的读写性能会急剧下降索引膨胀、锁竞争、连接数耗尽等问题接踵而至。此时分库分表便成为Java后端架构中不可或缺的优化手段。本文将从实际业务场景出发系统讲解分库分表的核心概念、主流中间件Apache ShardingSphere的实战用法、分布式ID的生成方案以及分库分表后必然面临的跨库查询难题与应对策略。文章配有大量可运行的代码示例帮助读者从理论走向落地。2. 为什么需要分库分表2.1 单库单表的瓶颈在业务初期一个数据库实例、一张大表往往能支撑起整个系统。但随着用户量和数据量的增长会出现以下问题存储瓶颈单表数据量过大B树索引层级加深查询IO次数增多。写入瓶颈单库写入并发有限主从延迟放大。连接瓶颈数据库连接数有限高并发下连接池被占满。运维瓶颈大表DDL如加索引、加字段耗时极长甚至锁表。2.2 分库分表的两种维度维度说明典型场景垂直拆分按业务模块拆库/拆表如订单库、用户库、商品库微服务化、模块解耦水平拆分按某个字段分片键将数据分散到多个库/表如按用户ID取模单表数据量巨大、写入并发高实际项目中通常是先垂直拆分再对核心大表做水平拆分。3. 分库分表核心概念在动手实践前需要先理解几个关键术语逻辑表对用户而言操作的是逻辑表名如t_order实际数据分散在多个物理表中。物理表真实存储数据的表如t_order_0、t_order_1。分片键Sharding Key用于计算数据归属的字段如order_id、user_id。分片算法决定数据如何分布常见有取模、哈希、范围、时间等。数据节点一个物理表实例如ds0.t_order_0。4. ShardingSphere 实战4.1 ShardingSphere 简介Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案由三个产品组成ShardingSphere-JDBC轻量级Java框架以jar包形式提供服务适合单体或微服务应用。ShardingSphere-Proxy透明数据库代理以独立服务形式部署对应用无侵入。ShardingSphere-Sidecar云原生环境下的代理目前演进中。本文重点讲解最常用的ShardingSphere-JDBC。4.2 环境准备!-- 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 实体与MapperDataTableName(t_order)publicclassOrder{privateLongorderId;privateLonguserId;privateBigDecimalamount;privateLocalDateTimecreateTime;}MapperpublicinterfaceOrderMapperextendsBaseMapperOrder{// 使用 MyBatis-Plus无需额外SQL}4.5 写入与查询测试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 由雪花算法自动生成}}publicOrderqueryOrder(LongorderId){// 根据分片键查询ShardingSphere 自动路由到正确的物理表returnorderMapper.selectById(orderId);}}注意查询条件必须包含分片键否则会触发全库全表路由广播查询性能较差。5. 分布式ID 生成方案分库分表后数据库自增主键无法保证全局唯一因此需要分布式ID。常见方案如下5.1 方案对比方案优点缺点UUID实现简单、无中心化无序、过长影响索引性能数据库号段有序、性能较好依赖数据库需维护号段表Redis INCR性能高依赖Redis需考虑持久化雪花算法Snowflake趋势递增、高性能、无中心化依赖机器时钟时钟回拨会出问题5.2 雪花算法原理雪花算法生成的ID为64位Long型结构如下| 1bit 符号位 | 41bit 时间戳 | 10bit 机器ID | 12bit 序列号 |41bit时间戳可表示约69年。10bit机器ID支持1024台机器。12bit序列号同一毫秒内可生成4096个ID。5.3 自定义雪花算法实现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(时钟回拨异常);}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;}}生产环境建议直接使用 ShardingSphere 内置的雪花算法或引入成熟的hutool、mybatis-plus内置ID生成器。6. 跨库查询难题与应对分库分表后原本简单的单表查询变得复杂主要面临以下问题6.1 常见问题跨库JOIN数据分散在不同库无法直接JOIN。分页排序全局分页需要先在各分片排序再归并。聚合函数COUNT、SUM等需要各分片计算后汇总。分布式事务跨库写入需要分布式事务保证一致性。6.2 应对策略问题解决方案跨库JOIN冗余字段、应用层组装、宽表设计全局分页使用ShardingSphere的归并功能或禁止深分页聚合统计使用ShardingSphere的分布式聚合或离线数仓分布式事务Seata AT模式、TCC、本地消息表6.3 ShardingSphere 归并示例// 分页查询ShardingSphere 会自动归并各分片结果PageOrderpagenewPage(1,10);LambdaQueryWrapperOrderwrappernewLambdaQueryWrapper();wrapper.orderByDesc(Order::getCreateTime);orderMapper.selectPage(page,wrapper);注意深分页如第10000页在分库分表场景下性能极差建议通过「游标分页」或「禁止跳页」来规避。6.4 分布式事务Seata 简介GlobalTransactionalpublicvoidcreateOrderWithDeductStock(){// 1. 插入订单订单库orderMapper.insert(order);// 2. 扣减库存库存库stockMapper.deduct(stockId,count);// 3. 任一失败全局回滚}7. 分库分表最佳实践分片键选择尽量选择查询频率高、分布均匀的字段如user_id、order_id。避免跨分片查询业务设计上尽量让查询带上分片键。容量规划提前评估数据增长合理设置分片数量避免后期扩容。读写分离结合分库分表通常与读写分离搭配使用。监控与治理通过ShardingSphere的SQL日志、监控面板观察路由与性能。8. 总结分库分表是解决海量数据存储与高并发写入的关键技术。本文从瓶颈分析出发介绍了垂直与水平拆分重点演示了ShardingSphere-JDBC的配置与使用并详细讲解了分布式ID的雪花算法实现最后分析了跨库查询的挑战与应对方案。在实际项目中分库分表并非银弹需要结合业务特点、数据规模、团队维护成本综合权衡。建议读者在理解原理的基础上通过本地搭建环境动手实践才能真正掌握这门核心技能。9. 参考与延伸阅读Apache ShardingSphere 官方文档https://shardingsphere.apache.org/Seata 分布式事务框架https://seata.io/MyBatis-Plus 官方文档https://baomidou.com/
返回列表