表设计如下:
CREATE TABLE `order_XXX` (
`order_id` bigint(20) unsigned NOT NULL,
`user_id` int(11) DEFAULT '0' COMMENT '订单id',
`status` int(11) DEFAULT '0' COMMENT '订单状态',
`booking_date` datetime DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`order_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_bdate` (`booking_date`),
KEY `idx_ctime` (`create_time`),
KEY `idx_utime` (`update_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
000<= XXX <= 255
如果有需要,可自行更改规则
全局唯一ID设计要求:
全局唯一
粗略有序
可反解出库编号
组成:1bit + 39bit时间差 + 8bit 机器号 + 8bit用户编号(库号) + 8bit自增序列
在mysql master实例分别建库
172.30.1.21(order_db_1)
172.30.1.22(order_db_2)
172.30.1.23(order_db_3)
172.30.1.24(order_db_4)
所使用到的pom依赖:
<!-- mango 分库分表中间件 -->
<dependency>
<groupId>org.jfaster</groupId>
<artifactId>mango-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<!-- 分布式ID生成器 -->
<dependency>
<groupId>com.bytearch</groupId>
<artifactId>fast-cloud-id-generator</artifactId>
<version>${version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
/**
* 分库分表策略常用常量
*/
public class ShardingStrategyConstant {
/**
* database 逻辑名称 ,真实库名为 order_db_XXX
*/
public static final String LOGIC_ORDER_DATABASE_NAME = "order_db";
/**
* 分表数 256,一旦确定不可更改
*/
public static final int SHARDING_TABLE_NUM = 256;
/**
* 分库数, 不建议更改, 可以更改,但是需要DBA迁移数据
*/
public static final int SHARDING_DATABASE_NODE_NUM = 4;
}
主从数据库的配置:
mango:
scan-package: com.bytearch.fast.cloud.mysql.sharding.dao
datasources:
- name: order_db_1
master:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://172.30.1.21:3306/order_db_1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
user-name: root
password: bytearch
maximum-pool-size: 10
connection-timeout: 3000
slaves:
- driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://172.30.1.31:3306/order_db_1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
user-name: root
password: bytearch
maximum-pool-size: 10
connection-timeout: 3000
- name: order_db_2
master:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://172.30.1.22:3306/order_db_2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
user-name: root
password: bytearch
maximum-pool-size: 10
connection-timeout: 3000
slaves:
- driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://172.30.1.32:3306/order_db_2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
user-name: root
password: bytearch
maximum-pool-size: 10
connection-timeout: 3000
- name: order_db_3
master:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://172.30.1.23:3306/order_db_3?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
user-name: root
password: bytearch
maximum-pool-size: 10
connection-timeout: 3000
slaves:
- driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://172.30.1.33:3306/order_db_3?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
user-name: root
password: bytearch
maximum-pool-size: 10
connection-timeout: 3000
- name: order_db_4
master:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://172.30.1.24:3306/order_db_4?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
user-name: root
password: bytearch
maximum-pool-size: 10
connection-timeout: 3000
slaves:
- driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://172.30.1.34:3306/order_db_4?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
user-name: root
password: bytearch
maximum-pool-size: 10
connection-timeout: 300
根据order_id为shardKey分库分表策略
import com.bytearch.fast.cloud.mysql.sharding.common.ShardingStrategyConstant;
import com.bytearch.id.generator.IdEntity;
import com.bytearch.id.generator.SeqIdUtil;
import org.jfaster.mango.sharding.ShardingStrategy;
/**
* 订单号分库分表策略
*/
public class OrderIdShardingStrategy implements ShardingStrategy<Long, Long> {
@Override
public String getDataSourceFactoryName(Long orderId) {
if (orderId == null || orderId < 0L) {
throw new IllegalArgumentException("order_id is invalid!");
}
IdEntity idEntity = SeqIdUtil.decodeId(orderId);
if (idEntity.getExtraId() >= ShardingStrategyConstant.SHARDING_TABLE_NUM) {
throw new IllegalArgumentException("sharding table Num is invalid, tableNum:" + idEntity.getExtraId());
}
//1. 计算步长
int step = ShardingStrategyConstant.SHARDING_TABLE_NUM / ShardingStrategyConstant.SHARDING_DATABASE_NODE_NUM;
//2. 计算出库编号
long dbNo = Math.floorDiv(idEntity.getExtraId(), step) + 1;
//3. 返回数据源名
return String.format("%s_%s", ShardingStrategyConstant.LOGIC_ORDER_DATABASE_NAME, dbNo);
}
@Override
public String getTargetTable(String logicTableName, Long orderId) {
if (orderId == null || orderId < 0L) {
throw new IllegalArgumentException("order_id is invalid!");
}
IdEntity idEntity = SeqIdUtil.decodeId(orderId);
if (idEntity.getExtraId() >= ShardingStrategyConstant.SHARDING_TABLE_NUM) {
throw new IllegalArgumentException("sharding table Num is invalid, tableNum:" + idEntity.getExtraId());
}
// 基于约定,真实表名为 logicTableName_XXX, XXX不足三位补0
return String.format("%s_%03d", logicTableName, idEntity.getExtraId());
}
}
dao层编写如下
import com.bytearch.fast.cloud.mysql.sharding.common.ShardingStrategyConstant;
import com.bytearch.fast.cloud.mysql.sharding.pojo.entity.OrderEntity;
import com.bytearch.fast.cloud.mysql.sharding.strategy.OrderIdShardingStrategy;
import org.jfaster.mango.annotation.*;
@DB(name = ShardingStrategyConstant.LOGIC_ORDER_DATABASE_NAME, table = "order")
@Sharding(shardingStrategy = OrderIdShardingStrategy.class)
public interface OrderPartitionByIdDao {
@SQL("INSERT INTO #table (order_id, user_id, status, booking_date, create_time, update_time) VALUES" +
"(:orderId,:userId,:status,:bookingDate,:createTime,:updateTime)"
)
int insertOrder(@TableShardingBy("orderId") @DatabaseShardingBy("orderId") OrderEntity orderEntity);
@SQL("UPDATE #table set update_time = now()" +
"#if(:bookingDate != null),booking_date = :bookingDate #end " +
"#if (:status != null), status = :status #end" +
"WHERE order_id = :orderId"
)
int updateOrderByOrderId(@TableShardingBy("orderId") @DatabaseShardingBy("orderId") OrderEntity orderEntity);
@SQL("SELECT * FROM #table WHERE order_id = :1")
OrderEntity getOrderById(@TableShardingBy @DatabaseShardingBy Long orderId);
@SQL("SELECT * FROM #table WHERE order_id = :1")
@UseMaster
OrderEntity getOrderByIdFromMaster(@TableShardingBy @DatabaseShardingBy Long orderId);
}