在实际业务中,高并发的数据库中,单个数据库性能受限,尤其是在有读写夹杂在一起的时候。我们都知道,系统中大部分都是读操作,写操作是占用比较小的部分的,所有就有了读写分离的操作,读可以专门读的数据库,而写操作(CUD)就专门在主数据库中进行。
而分表则是为了避免单个的表的数据量太大,根据ali开发规范来说,一个表的上限是2G,数据量上限为500w,但是通常来说虽然我们不一定完全遵循,但是2000w条数据,对于系统来说也是比较大了,而2000w数据来说是非常正常的,一些比如日志表啥的,动不动是10G+以上的数据,所以分表也是非常必要的。
仅仅是主从分离的话,可以使用mybatis plus系列的dynamic-datasource,参考data-source来实现主从分离,非常方便,但是暂时没有分表功能。所以我们使用mybaits plus + shardingsphere方案
方案 | 特点 |
---|---|
dynamic-datasource | 简单方便,支持开发指定从库的数据查询 ,不支持分表 |
shardingshpere | 功能强大,比较复杂,支持分表,暂时不支持指定库操作 |
mybatis plus是国内基于mybatis做出一个开源工具包,
是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
简单来说就是代替mybatis的一个orm访问工具,但是功能更强大,这里不多赘述,因为我们就是用它的orm功能,其他的查看文档。
官方文档
官方文档
Apache ShardingSphere 是一款分布式的数据库生态系统, 可以将任意数据库转换为分布式数据库,并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。
Apache ShardingSphere 设计哲学为 Database Plus,旨在构建异构数据库上层的标准和生态。 它关注如何充分合理地利用数据库的计算和存储能力,而并非实现一个全新的数据库。 它站在数据库的上层视角,关注它们之间的协作多于数据库自身。
shardingSphere的功能非常强大,但是我们暂时只需要使用到sharding-jdbc,sharding-jdbc时什么呢?
ShardingSphere-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务
我的理解就是jdbc api的重写和封装。
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
mysql
mysql-connector-java
com.baomidou
mybatis-plus-boot-starter
org.apache.shardingsphere
sharding-jdbc-spring-boot-starter
4.1.1
org.apache.shardingsphere
sharding-core-common
4.1.1
com.alibaba
druid
1.1.9
在开始之前我们,我们需要创建sql,数据结构如下:
order (1) —order_id----> order_item(n),请将idx换成0~1
CREATE TABLE `t_order_idx` (
`order_id` bigint NOT NULL,
`order_no` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
`create_name` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
`price` decimal(10,2) DEFAULT NULL,
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
CREATE TABLE `t_order_item_0` (
`item_id` bigint NOT NULL,
`order_no` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
`item_name` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
`price` decimal(10,2) DEFAULT NULL,
`order_id` bigint DEFAULT NULL,
PRIMARY KEY (`item_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
如果上述步骤完成后,那么需要做的仅仅做些配置了,yaml注册文件如下:
spring:
shardingsphere:
# 数据库名称
datasource:
names: master,slave-0,slave-1
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/asx?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
slave-0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3307/asx?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
slave-1:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3308/asx?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
# 配置分片规则
sharding:
tables:
t_order:
key-generator:
column: order_id
type: SNOWFLAKE
actual-data-nodes: ms.t_order_$->{0..1}
database-strategy:
inline:
sharding-column: order_id
algorithm-expression: ms
table-strategy:
inline:
sharding-column: order_id
algorithm-expression: t_order_$->{order_id % 2}
t_order_item:
key-generator:
column: item_id
type: SNOWFLAKE
actual-data-nodes: ms.t_order_item_$->{0..1}
database-strategy:
inline:
sharding-column: order_id
algorithm-expression: ms
table-strategy:
inline:
sharding-column: order_id
algorithm-expression: t_order_item_$->{order_id % 2}
binding-tables: t_order,t_order_item
broadcast-tables: t_config
# default-data-source-name: master
defaultTableStrategy:
none:
# 配置主从规则
masterSlaveRules:
ms:
masterDataSourceName: master
slaveDataSourceNames:
- slave-0
- slave-1
loadBalanceAlgorithmType: ROUND_ROBIN
props:
sql:
show: true # 打印SQL
编写insert case
@Test
public void testCreateOrder() {
int start = 0;
for (int i = start; i < start + 10; i++) {
BtOrderModel order = new BtOrderModel();
order.setOrderNo("A000" + i);
order.setCreateName("订单 " + i);
order.setPrice(new BigDecimal("" + i));
btOrderMapper.insert(order);
BtOrderItemModel orderItem = new BtOrderItemModel();
orderItem.setOrderId(order.getOrderId());
orderItem.setOrderNo("A000" + i);
orderItem.setItemName("服务项目" + i);
orderItem.setPrice(new BigDecimal("" + i));
btOrderItemMapper.insert(orderItem);
}
}
查看日志
INFOLogic SQL: INSERT INTO t_order ( order_id,
order_no,
create_name,
price ) VALUES ( ?,
?,
?,
? )
INFOSQLStatement: InsertStatementContext(super=CommonSQLStatementContext(sqlStatement=org.apache.shardingsphere.sql.parser.sql.statement.dml.InsertStatement@3a5e2525, tablesContext=org.apache.shardingsphere.sql.parser.binder.segment.table.TablesContext@6546371), tablesContext=org.apache.shardingsphere.sql.parser.binder.segment.table.TablesContext@6546371, columnNames=[order_id, order_no, create_name, price], insertValueContexts=[InsertValueContext(parametersCount=4, valueExpressions=[ParameterMarkerExpressionSegment(startIndex=75, stopIndex=75, parameterMarkerIndex=0), ParameterMarkerExpressionSegment(startIndex=78, stopIndex=78, parameterMarkerIndex=1), ParameterMarkerExpressionSegment(startIndex=81, stopIndex=81, parameterMarkerIndex=2), ParameterMarkerExpressionSegment(startIndex=84, stopIndex=84, parameterMarkerIndex=3)], parameters=[1584134049671151617, A00011, 订单 11, 11])], generatedKeyContext=Optional[GeneratedKeyContext(columnName=order_id, generated=false, generatedValues=[1584134049671151617])])
INFOActual SQL: master ::: INSERT INTO t_order_1 ( order_id,
order_no,
create_name,
price ) VALUES (?, ?, ?, ?) ::: [1584134049671151617, A00011, 订单 11, 11]
actual sql中采用的是master数据库
编写一个url为:
GET http://localhost:9380/master/slave/sharding/order/{id}
的接口,让后手动修改数据库中的值,调用两次,就会发现结果不同,当然你也可以查看日志
问题1: 插入insert的时候,主键不回写,如上面的orderId,orderId是根据雪花算法生成
解决方案: Model中增加主键注释
@TableId(type = IdType.ASSIGN_ID)
spring-data-sharding