java学习基地

微信扫一扫 分享朋友圈

已有 1816 人浏览分享

分库分表Sharding-JDBC入门与项目实战,数据量大了一定要分表 !

[复制链接]
1816 2


     近来项目中很多表的数据量愈来愈年夜,而且招致了一些数据库的机能成绩。因而念借助一些分库非的中心件,完成主动化分库非完成。调研下去,发明Sharding-JDBC今朝成生度最下而且使用最广的Java分库非的客户堆砰件。本文次要引见一些Sharding-JDBC中心观点和消费情况下的真战指北,旨正在协助组内成员快速理解Sharding-JDBC而且可以快速将其利用起去。

中心观点

正在利用Sharding-JDBC之前,必然是先了解分明上面寂中心观点。

逻辑表

程度拆分的数据库(表)当编同逻辑战数据构造表的总称。例:定单数据按照主键尾数拆分为10张表,别离是t_order_0到t_order_9,他们的逻辑表名为t_order。

实在表

正在吩飕的数据库中实在存正在的物理表。即上个示例中的t_order_0到t_order_9。

数据节面

数据吩飕的最小单位。由数据源称号战数据表构成,例:ds_0.t_order_0。

绑定表

指吩飕划定规矩分歧的主表战子表。比方:t_order表战t_order_item表,均根据order_id吩飕,则此两张表互为绑定表干系。绑定表之间的多表联系关系查询没有会呈现笛卡我积联系关系,联系关系查询服从将年夜年夜提拔。举例阐明,假如SQL为:
  1. SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
赶钙代码



假定t_order战t_order_item洞喀的┞峰表各有2个,那末实在表便有t_order_0、t_order_1、t_order_item_0、t_order_item_1。正在没有设置绑定表干系时,假定吩飕键order_id将数值10路由至第0片,将数值11路由至第1片,那末路由后的SQL该当为4条,它们显现为笛卡我积:


  1. SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
  2. SELECT i.* FROM t_order_0 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
  3. SELECT i.* FROM t_order_1 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
  4. SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
赶钙代码


正在设置绑定表干系后,路佑弈SQL该当为2条:
  1. SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
  2. SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
赶钙代码




播送表

指一切的吩飕数据韵感皆存正在的表,表构造战表中的数据正在每一个数据库中均完整分歧。合用于数据量没有年夜且需求取海量数据的表停止联系关系查询的场景,比方:字典表。

数据吩飕吩飕键

用于吩飕的数据库字段,是将数据库(表)程度拆分的枢纽字段。例:将定单表中的定单主键的尾数与模吩飕,则定单主键为吩飕字段。SQL 中假如无吩飕字段,将施行齐路由,机能较好。除对单吩飕字段的撑持,Sharding-JDBC 也撑持按照多个字段停止吩飕。

吩飕算法

经由过程吩飕算法将数据吩飕,撑持经由过程=、>=、<=、>、<、BETWEEN战IN吩飕。吩飕算法需求使用圆开辟者自止完成,可完成的灵敏度十分下。

今朝供给4种吩飕算法。因为吩飕算法战营业完成严密相干,因而并已供给内置吩飕算法,而是经由过程吩飕战略将各类场景提炼出去,供给更下层级的笼统,并供给接心让使用开辟者自止完成吩飕算法。

准确吩飕算法

洞喀 PreciseShardingAlgorithm,用于处置利用单一键做为吩飕键的 = 取 IN 停止吩飕的场景。需求共同 StandardShardingStrategy 利用。

范畴吩飕算法

洞喀 RangeShardingAlgorithm,用于处置利用单一键做为吩飕键的 BETWEEN AND、>、<、>=、<=停止吩飕的场景。需求共同 StandardShardingStrategy 利用。

盖锵吩飕算法

洞喀 ComplexKeysShardingAlgorithm,用于处置利用多键做为吩飕键停止吩飕的场景,包罗多个吩飕键的逻辑较庞大,需求使用开辟者自止处置此中的庞大队耄需求共同 ComplexShardingStrategy 利用。

Hint吩飕算法

洞喀 HintShardingAlgorithm,用于处置经由过程Hint指定吩飕值而非从SQL中提与吩飕值的场景。需求共同 HintShardingStrategy 利用。

吩飕战略

包罗吩飕键战吩飕算法,因为吩飕算法的自力性,将其自力抽离。实正可用于吩飕操纵的识淘飕键 + 吩飕算法,也便识淘飕战略。今朝供给 5 种吩飕战略。

尺度吩飕战略

洞喀 StandardShardingStrategy。供给对 SQ L语句中的 =, >, <, >=, <=, IN 战 BETWEEN AND 的吩飕操纵撑持。StandardShardingStrategy 只撑持单吩飕键,供给 PreciseShardingAlgorithm 战 RangeShardingAlgorithm 两个吩飕算法。PreciseShardingAlgorithm 是必选的,用于处置 = 战 IN 的吩飕。RangeShardingAlgorithm 是可选的,用于处置 BETWEEN AND, >, <, >=, <=吩飕,假如没有设置 RangeShardingAlgorithm,SQL 中的 BETWEEN AND 将根据齐库路由处置。

盖锵吩飕战略

洞喀 ComplexShardingStrategy。盖锵吩飕战略。供给对 SQL 语句中的 =, >, <, >=, <=, IN 战 BETWEEN AND 的吩飕操纵撑持。ComplexShardingStrategy 撑持多吩飕键,因为多吩飕键之间的干系庞大,因而并已停止过量的启拆,而是间接将吩飕键值组开和吩飕操纵符透传至吩飕算法,完整由使用开辟者完成,供给最年夜的灵敏队耄

止表达式吩飕战略

洞喀 InlineShardingStrategy。利用 Groovy 的表达式,供给对 SQL 语句中的 = 战 IN的吩飕操纵撑持,只撑持单吩飕键。关于简朴的吩飕算法,能够经由过程简朴的设置利用,从而制止烦琐的Java代码开辟,如: t_user_$->{u_id % 8} 暗示 t_user 表按照 u_id 模 8,而分红 8 张表,表称号为 t_user_0 到 t_user_7。能够以为是准确吩飕算法的浅易完成

Hint吩飕战略

洞喀 HintShardingStrategy。经由过程 Hint 指定吩飕值而非从 SQL 中提与吩飕值的方法停止吩飕的战略。

散布式主键

用于正在散布式情况下,天生齐局独一的id。Sharding-JDBC 供给了内置的散布式主键天生器,比方 UUID、SNOWFLAKE。借抽离出散布式主键天生器的接心,便利映雩自止完成捉义的自删主键天生器。为了包管数据库机能,主键id借必需趋向递删,制止形成频仍的数据页里团结。

读写别离

供给一主多从的读写别离设置,可自力利用,也可共同分库非利用。

  • 统一线程且统一数据库毗连内,若有写进操纵,当前的读操纵均从主库读与,用于包管数据分歧性
  • 基于Hint的强迫主库路由。
  • 主从模子中,事件中读写均用主库。


施行流程

Sharding-JDBC 的道理总结起去很简朴: 中心由 SQL剖析 => 施行器劣化 => SQL路由 => SQL改写 => SQL施行 => 成果合并的流程构成。



spring-boot项目真战

引进依靠

  1. <dependency>
  2.     <groupId>org.apache.shardingsphere</groupId>
  3.     <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
  4.     <version>4.0.1</version>
  5. </dependency>
赶钙代码

数据源设置

假如利用sharding-jdbc-spring-boot-starter, 而且数据源和数据吩飕皆利用shardingsphere停止设置,洞喀的数据源会主动创立并注进到spring容器中。
  1. spring.shardingsphere.datasource.names=ds0,ds1
  2. spring.shardingsphere.datasource.ds0.type=org.apache.commons.dbcp.BasicDataSource
  3. spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
  4. spring.shardingsphere.datasource.ds0.url=jdbc:mysql://localhost:3306/ds0
  5. spring.shardingsphere.datasource.ds0.username=root
  6. spring.shardingsphere.datasource.ds0.password=
  7. spring.shardingsphere.datasource.ds1.type=org.apache.commons.dbcp.BasicDataSource
  8. spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
  9. spring.shardingsphere.datasource.ds1.url=jdbc:mysql://localhost:3306/ds1
  10. spring.shardingsphere.datasource.ds1.username=root
  11. spring.shardingsphere.datasource.ds1.password=
  12. # 别的吩飕设置
赶钙代码


可是正在我们已有当鳖目中,数据源设置是零丁的。因而要禁用sharding-jdbc-spring-boot-starter内里的主动拆配,而是参考源码本人重写数据源设置。需求正在启动类上减上

  1. @SpringBootApplication(exclude = {org.apache.shardingsphere.shardingjdbc.spring.boot.SpringBootConfiguration.class})
赶钙代码


去解除。然后捉义设置类去拆配DataSource。


  1. @Configuration
  2. @Slf4j
  3. @EnableConfigurationProperties({
  4.         SpringBootShardingRuleConfigurationProperties.class,
  5.         SpringBootMasterSlaveRuleConfigurationProperties.class, SpringBootEncryptRuleConfigurationProperties.class, SpringBootPropertiesConfigurationProperties.class})
  6. @AutoConfigureBefore(DataSourceConfiguration.class)
  7. public class DataSourceConfig implements ApplicationContextAware {
  8.     @Autowired
  9.     private SpringBootShardingRuleConfigurationProperties shardingRule;
  10.     @Autowired
  11.     private SpringBootPropertiesConfigurationProperties props;
  12.     private ApplicationContext applicationContext;
  13.     @Bean("shardingDataSource")
  14.     @Conditional(ShardingRuleCondition.class)
  15.     public DataSource shardingDataSource() throws SQLException {
  16.         // 获得别的方法设置的数据源
  17.         Map<String, DruidDataSourceWrapper> beans = applicationContext.getBeansOfType(DruidDataSourceWrapper.class);
  18.         Map<String, DataSource> dataSourceMap = new HashMap<>(4);
  19.         beans.forEach(dataSourceMap::put);
  20.         // 创立shardingDataSource
  21.         return ShardingDataSourceFactory.createDataSource(dataSourceMap, new ShardingRuleConfigurationYamlSwapper().swap(shardingRule), props.getProps());
  22.     }
  23.     @Bean
  24.     public SqlSessionFactory sqlSessionFactory() throws SQLException {
  25.         SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
  26.         // 将shardingDataSource设置到SqlSessionFactory中
  27.         sqlSessionFactoryBean.setDataSource(shardingDataSource());
  28.         // 别的设置
  29.         return sqlSessionFactoryBean.getObject();
  30.     }
  31. }
赶钙代码


散布式id天生器设置
Sharding-JDBC供给了UUID、SNOWFLAKE天生器,借撑持映雩完成捉义id天生器。好比能够完成了type为SEQ的散布式id天生器,挪用同一的散布式id效劳获得id。
  1. @Data
  2. public class SeqShardingKeyGenerator implements ShardingKeyGenerator {
  3.     private Properties properties = new Properties();
  4.     @Override
  5.     public String getType() {
  6.         return "SEQ";
  7.     }
  8.     @Override
  9.     public synchronized Comparable<?> generateKey() {
  10.        // 获得散布式id逻辑
  11.     }
  12. }
赶钙代码
因为扩大ShardingKeyGenerator是经由过程JDK的serviceloader的SPI机造完成的,因而借需求正在resources/META-INF/services目次下设置org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator文件。 文件内容便是SeqShardingKeyGenerator类的齐途径名。如许利用的时分,指定散布式主键天生器的type为SEQ就行了。

至此,Sharding-JDBC便整开进spring-boot项目中了,前面就能够停止数据吩飕相干的设置了。

数据吩飕真战

假如项目早期就可以预估出表的数据量级,固然能够一开端便根据那个预估值停止分库非处置。可是年夜大都状况下,我们一开端其实不能筹办预估出数目级。这时候候凡是的做法是:

  • 线沙慢据某张表查询机能开端降落,排查下去是由于数据量过年夜招致的。
  • 按照汗青数据量预估出将来的数据量级,并分离详细营业场景肯定分库非战略。
  • 主动分库非代码完成。


上面便以一个详细事例,论述详细数据吩飕真战。好比有张表数据构造以下:

  1. CREATE TABLE `hc_question_reply_record` (
  2.   `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自删ID',
  3.   `reply_text` varchar(500) NOT NULL DEFAULT '' COMMENT '复兴内容',
  4.   `reply_wheel_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '复兴工夫',
  5.   `ctime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫',
  6.   `mtime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫',
  7.   PRIMARY KEY (`id`),
  8.   INDEX `idx_reply_wheel_time` (`reply_wheel_time`)
  9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  10.   COMMENT='复兴明细记载';
赶钙代码


吩飕计划肯定

先查询今朝目的表月新删趋向:

  1. SELECT count(*), date_format(ctime, '%Y-%m') AS `日期`
  2. FROM hc_question_reply_record
  3. GROUP BY date_format(ctime, '%Y-%m');
赶钙代码

今朝月新删正在180w阁下,预估将来到达300w(根本以2苯云算)以擅埽希冀单表数据量没有超越1000w,可以使用reply_wheel_time做为吩飕键按季度回档。

吩飕设置spring:

  1. spring:
  2.   # sharing-jdbc设置
  3.   shardingsphere:
  4.     # 数据源称号
  5.     datasource:
  6.       names: defaultDataSource,slaveDataSource
  7.     sharding:
  8.       # 主从节面设置
  9.       master-slave-rules:
  10.         defaultDataSource:
  11.           # maser数据源
  12.           master-data-source-name: defaultDataSource
  13.           # slave数据源
  14.           slave-data-source-names: slaveDataSource
  15.       tables:
  16.         # hc_question_reply_record 分库非设置
  17.         hc_question_reply_record:
  18.           # 实在数据节面 hc_question_reply_record_2020_q1
  19.           actual-data-nodes: defaultDataSource.hc_question_reply_record_$->{2020..2025}_q$->{1..4}
  20.           # 表吩飕战略
  21.           table-strategy:
  22.             standard:
  23.               # 吩飕键
  24.               sharding-column: reply_wheel_time
  25.               # 准确吩飕算法 齐途径名
  26.               preciseAlgorithmClassName: com.xx.QuestionRecordPreciseShardingAlgorithm
  27.               # 范畴吩飕算法,用于BETWEEN,可选。。负绵需完成RangeShardingAlgorithm接心并供给无参数的机关器
  28.               rangeAlgorithmClassName: com.xx.QuestionRecordRangeShardingAlgorithm
  29.       # 默许散布式id天生器
  30.       default-key-generator:
  31.         type: SEQ
  32.         column: id
赶钙代码

吩飕算法完成

准确吩飕算法:
  1. public class QuestionRecordPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Date> {
  2.   /**
  3.    * Sharding.
  4.    *
  5.    * @param availableTargetNames available data sources or tables's names
  6.    * @param shardingValue sharding value
  7.    * @return sharding result for data source or table's name
  8.    */
  9.   @Override
  10.   public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {
  11.       return ShardingUtils.quarterPreciseSharding(availableTargetNames, shardingValue);
  12.   }
  13. }
赶钙代码


范畴吩飕算法:

  1. public class QuestionRecordRangeShardingAlgorithm implements RangeShardingAlgorithm<Date> {
  2.   /**
  3.    * Sharding.
  4.    *
  5.    * @param availableTargetNames available data sources or tables's names
  6.    * @param shardingValue sharding value
  7.    * @return sharding results for data sources or tables's names
  8.    */
  9.   @Override
  10.   public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Date> shardingValue) {
  11.       return ShardingUtils.quarterRangeSharding(availableTargetNames, shardingValue);
  12.   }
  13. }
赶钙代码


详细吩飕完成逻辑:


  1. @UtilityClass
  2. public class ShardingUtils {
  3.     public static final String QUARTER_SHARDING_PATTERN = "%s_%d_q%d";
  4.     /**
  5.     * logicTableName_{year}_q{quarter}
  6.     * 按季度范畴吩飕
  7.     * @param availableTargetNames 可用的┞峰表汇合
  8.     * @param shardingValue 吩飕值
  9.     * @return
  10.     */
  11.     public Collection<String> quarterRangeSharding(Collection<String> availableTargetNames, RangeShardingValue<Date> shardingValue) {
  12.         // 那里便史狴据范畴查询前提,挑选出婚配的┞峰表汇合
  13.     }
  14.     /**
  15.     * logicTableName_{year}_q{quarter}
  16.     * 按季度准确吩飕
  17.     * @param availableTargetNames 可用的┞峰表汇合
  18.     * @param shardingValue 吩飕值
  19.     * @return
  20.     */
  21.     public static String quarterPreciseSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {
  22.         // 那里便史狴据等值查询前提,计较出婚配的┞峰表
  23.     }
  24. }
赶钙代码




到那里,针对hc_question_reply_record表,利用reply_wheel_time做为吩飕键,根据季度吩飕的处置便完成了。另有一面要留意的便是,分库非以后,查询的时分最好皆带上吩飕键做为查询前提,不然便会利用齐库路由,机能很低。另有便是Sharing-JDBC对mysql的齐文索引撑持的没有是很好,项目有利用到的处所也要留意一下。总结来讲全部历程仍是比力简朴的,后绝碰着别的营业场景,信赖各人根据那个思绪必定皆能处理的。










本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

举报 使用道具

回复

评论 2

天音沙龙  vip终身会员  发表于 2020-12-22 19:19:33 | 显示全部楼层
围观 围观 沙发在哪里!!!

举报 使用道具

回复
安迪  vip终身会员  发表于 2020-12-22 21:10:09 | 显示全部楼层
看起来好像不错的样子

举报 使用道具

回复
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

0

关注

0

粉丝

138

主题
精彩推荐
热门资讯
网友晒图
图文推荐

Archiver|手机版|java学习基地 |网站地图

GMT+8, 2021-8-1 09:47 , Processed in 0.857455 second(s), 66 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.