分库分表ShardingJDBC最佳实践

1 添加依赖

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-core</artifactId>
    <version>${sharding.version}</version>
</dependency>

2 分库分表数选择

根据未来两年的业务量,估算两年的业务总量M,单表数据量不能超过N(需要看具体业务场景,字段少的可以适量多一些,可与架构师及部门经验丰富的同事探讨,最大不要超过1000W);

总的分表数量KM/N,且K值向上取接近的最小2的次幂。

例如业务总量M=10亿,单表数量N≤700W,则M/N≈143,向上取最小的2次幂为:143<2的8次方=256,故总的分表数量为256。

可将分表数设定的尽可能的小,一台服务器存放多个库,业务增长后,磁盘不足时,可将该服务器上的整个库的数据迁移到新的服务器。

如总的分表数量为1024,可分为32个库:db0~db31,每个库分32张表:tb0~tb31,共16台服务器:server0~server15。

具体划分如下:

  • server0上有db0库和db16库
  • server1上有db1库和db17库
  • ……
  • server15上有db15库和db31库

如果业务发展一段时间后出现磁盘不足,可在申请16台服务器:server16~server31

  • server0上的db16库迁移至server16
  • server1上的db17库迁移至server17
  • ……
  • server15上的db31库迁移至server31

迁移后磁盘释放50%空间

3 分片键(拆分键)选择

分片键的选择一定要具有明显的业务特征,具体表现为:

  • 常用的已知的具体值,查询条件中要包含分片键,需要根据分片键确定在哪个库表,如果查询条件里没有分片键,将会遍历所有库表,这种情况是不允许的;
  • 分片键分辨度较高,比较分散,不能选择分辨度不高的的字段作为分片键,容易造成数据分布不均匀;
  • 分片键不能有偏移,比如分片键某个值不能远远大于其他值,这种情况也会造成数据分布不均匀;
  • 大小写,如果分片键在某些表里不区分大小写,那么分片键要统一大写或小写后用于分片,因此其他条件要考虑统一大小写后的情况;

目前项目里使用的分片键为商家编码,并统一为大写,因为:

  • 在查询分库分表数据时,商家编码是已知的,所以查询条件中包含商家编码;
  • 商家编码较多,能够保证按照商家编码分片后数据基本是均匀分布的;
  • 系统中不存在某个或某几个商家编码的数据量占据大部分的数据量;

4 分片算法

inline: Groovy的Inline表达式,可以支持SQL语句中的=和IN的操作,InlineShardingStrategy只支持单分片键;这种方式是最常用的,也是目前项目中在使用的。

其他分片策略参考:Sharding-JDBC分片策略

4.1 分库算法

以32个库为例:Math.abs(库分片键.toUpperCase().hashCode()) % 32,将分片键商家编码统一大写后,取hashCode的绝对值,对32取模,32为分库数;

通用公式为:Math.abs(库分片键.toUpperCase().hashCode()) % 分库数

4.2 分表算法

以每个库32张表为例:(Math.abs(表分片键.toUpperCase().hashCode()) % 1024).intdiv(32),要保证分到某个库的数据,均匀分布到该库的每个表;

注意:此处不能直接用分库的公式,因为某个商家计算后分配到第i库,如果分表用同样的公式,则也只能落到第i表,导致一个库中只有一个表有数据。因此,需要先对一个较大的数取模,该较大的数为分库数*分表数,记为m,本例中=32*32=1024,将hash后的值分散到0-1023之间,在除以32,则最终结果被分配到0-31之间。

通用公式为:(Math.abs(表分片键.toUpperCase().hashCode()) % 分库数*分表数).intdiv(分库数)

5 全局(分布式)主键

分库分表,要求主键全局唯一,因为存在数据汇总的场景,如存放到ES、大数据等;因此不能使用数据库自增方式,需要一种全局唯一的主键生成方式;

5.1 shardingjdbc提供的主键

1. 添加maven依赖

<dependency>
    <groupId>com.dangdang</groupId>
    <artifactId>sharding-jdbc-self-id-generator</artifactId>
    <version>1.4.2</version>
</dependency>

2. 在@Configuration类中创建Bean对象

@Bean
public IdGenerator getIdGenerator() { 
    return new CommonSelfIdGenerator(); 
}

3. 使用

@Autowired 
private IdGenerator idGenerator; 
@Test 
public void generateId(){ 
    long id = idGenerator.generateId().longValue(); 
    ......
}

5.2 雪花算法生成主键

很多插件如sharding-jdbc, mybatis-plus提供了雪花算法生成器,目前常用的是这种方式,生产的id时间上基本是有序的,使用很方便。

6 查询条件没有分片键如何处理

不允许没有分片键的查库操作;

查询或更新库操作条件里必须有分片键,否则就需要将分库分表数据存放到ES或建立查询条件与分片键的中间表;

ES或分片键中间表是为个别场景如页面多条件查询,用户获取不到分片键时采取的折中方案,绝大多数场景是能获取到分片键的,如果获取不到则可认为分片键的选取是不合理的,需要根据实际场景调整分片键。

 

热门相关:最强狂兵   薄先生,情不由己   豪门闪婚:帝少的神秘冷妻   人间欢喜   豪门闪婚:帝少的神秘冷妻