# 分库分表实战


<!--more-->


**实战代码示例:** [https://gitee.com/ixinglan/mysql-demo.git](https://gitee.com/ixinglan/mysql-demo.git)

**sharding-jdbc-demo**: **详见README.md 介绍**

## 1. 分库实战(这里是水平分库)
我们演示position表的分库
```mysql
CREATE TABLE `position` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `salary` varchar(10) DEFAULT NULL,
  `city` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4;
```
1. 创建两个库 sharding-demo1, sharding-demo2模拟两台服务器
2. 分别执行position表的创建sql
3. 创建与数据库对应实体entity: Position
   提示: @GeneratedValue(strategy = GenerationType.IDENTITY) 应先注释掉,便于手动造id,观察数据写库策略情况
4. 创建数据库操作类: PositionRepository
5. 配置application.yml
6. 配置application-sharding-database.yml
```yml
# datasource, 配置两个数据源, 这里配置我们刚创建的两个库
spring:
  shardingsphere:
    datasource:
      names: ds0,ds1
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class: com.mysql.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/sharding-demo1
        username: root
        password: 123456
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class: com.mysql.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/sharding-demo2
        username: root
        password: 123456
    # sharding-database 先采用 inline 行表达式的形式配置分库策略: 模2
    sharding:
      tables:
        position:
          databaseStrategy:
            inline:
              shardingColumn: id
              algorithmExpression: ds$->{id % 2}
```
7. 创建测试类: TestShardingDatabase, 见test1方法

## 2. 主键生成器
我们在1.1中, 主键是手动指定生成的, 这节我们改造一下,使用主键生成器
1. 配置文件添加id生成策略配置
```yml
    # sharding-database
    sharding:
      tables:
        position:
          databaseStrategy:
            inline:
              shardingColumn: id
              algorithmExpression: ds$->{id % 2}
          # 主键生成器, 此处我们使用内置的雪花算法
          keyGenerator:
            column: id
            type: SNOWFLAKE
```
2. 将  @GeneratedValue(strategy = GenerationType.IDENTITY) 注释放开,不然会使用数据库自身的自增
3. TestShardingDatabase: test1 方法测试, 注释掉设置主键值, 如果写不进去,我们需要将主键int类型修改为bigint

## 3. 自定义主键生成器
1.2 我们演示了内置主键生成器, 这里我们演示自定义主键生成器
1. 创建id生成类 MyId,实现ShardingKeyGenerator接口
2. spi配置: 创建META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator,里面指定MyId路径
3. 配置文件修改为 MyId
```yaml
keyGenerator:
   column: id
   type: MyId
```
4. TestShardingDatabase: test1 方法测试

## 4. 垂直分表+水平分库
如果一个表里字段太多, 往往将不常用字段拆分出来到一或多张表
这里我们演示 position_detail表: pid与主表主键id关联
```mysql
CREATE TABLE `position_detail` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `pid` bigint(20) DEFAULT NULL,
    `description` varchar(255) DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
1. 分别执行position表的创建sql
2. 创建与数据库对应实体entity: PositionDetail
3. 创建数据库操作类: PositionDetailRepository
4. 配置文件application-sharding-database.yml 添加 分片字段+主键策 略配置, 注意分片主键变为了 pid
```yaml
    # sharding-database
    sharding:
      tables:
        # 主表
        position:
          databaseStrategy:
            inline:
              shardingColumn: id
              algorithmExpression: ds$->{id % 2}
          # 主键生成器
          keyGenerator:
            column: id
#            type: SNOWFLAKE
            type: MyId
        # 副表
        position_detail:
          databaseStrategy:
            inline:
              shardingColumn: pid
              algorithmExpression: ds$->{id % 2}
          # 主键生成器
          keyGenerator:
            column: id
            type: MyId
```
5. 测试方法 TestShardingDatabase: test2
6. 测试查询 TestShardingDatabase: test3

## 5. 广播表
演示city表
```mysql
CREATE TABLE `city` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `province` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
1. 创建对应entity: City
2. 创建repository: CityRepository
3. 配置文件添加以下
```yaml
        #广播表
        city:
           # 主键生成器
           keyGenerator:
              column: id
              type: SNOWFLAKE
        #广播表
        broadcast-tables:
           - city
```
4. 测试方法 TestShardingDatabase: test4

## 6. 水平分表+水平分库
这里演示order表
- 按 company_id 水平分库, 保证同一company_id在同一个库
- 再按 id 同一个库中水平分表
由于order为关键字, 我们命名表名为 b_order  
由于要水平分表,我们分别在两个库建表: b_order0, b_order1
```mysql
CREATE TABLE `b_order` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `is_del` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否被删除',
  `company_id` int(11) NOT NULL COMMENT '公司id',
  `position_id` int(11) NOT NULL COMMENT '职位id',
  `user_id` int(11) NOT NULL COMMENT '用户id',
  `publish_user_id` int(11) NOT NULL COMMENT '职位发布者id',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
1. 创建对应entity: Order
2. 创建repository: OrderRepository
3. 配置文件配置: 分库策略, 分表策略, id策略
```yaml
        # order表
        b_order:
          # 分库策略
          databaseStrategy:
            inline:
              shardingColumn: company_id
              algorithmExpression: ds$->{company_id % 2}
          #分表策略
          tableStrategy:
            inline:
              shardingColumn: id
              algorithmExpression: b_order${id % 2}
          #映射数据节点: 库+表 笛卡尔积
          actualDataNodes: ds${0..1}.b_order${0..1}
          # 主键生成器
          keyGenerator:
            column: id
            type: SNOWFLAKE
```
4. 测试方法 TestShardingDatabase: test5

