以商城商品为例
垂直分表:将一个表按照字段分成多表,每个表存储其中一部分字段
它带来的提升是:
1、为了避免IO争抢并减少锁表的几率,查看详情的用户与商品信息浏览相互不影响
2、充分发挥热门数据的操作效率,商品信息的操作的高效率不会被商品描述的低效率所拖累
垂直分库:
垂直分库是指按照业务将表进行分类,分页到不同的数据库上面,每个库可以放在不同的服务器上,它的核心理念是专库专用
它带来的提升是:
1、解决业务层面的耦合,业务清晰
2、能对不同业务的数据进行分组管理,维护,监控,扩展等
3、高并发场景下,垂直分库一定程度的提升IO,数据库连接数,降低单机硬件资源的瓶颈
垂直分库通过将表按业务分类,然后分布在不同数据库,并且可以将这些数据库部署在不同服务器上,从而达到多个服务器共同分摊压力的效果,但是依然没有解决单表数据量过大的问题。
水平分库:
是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。
它带来的提升是:
1、解决了单库大数据,高并发的性能瓶颈
2、提高了系统的稳定性及可用性。稳定性体现在IO冲突少,锁定减少,可用性指某个库出问题,部分数据可用
水平分表:
水平分表是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中
它带来的提升是:
1、优化单一表数据量过大而产生的性能问题
2、避免IO争抢并减少锁表的几率
分库分表带来的问题:
1、事务一致性问题
由于分库分表把数据分页在不同库甚至不同服务器,不可避免分带来分页式事务问题
2、跨节点关联查询
3、跨节点分页、排序、函数
4、主键重复问题
在分库分表环境中,由于表中数据同时存在不同的数据库中,主键值平时使用的自增长 将无用武之地,某个分区数据库生成的ID无法保证全局唯一。因此需要单独设计全局主键,以避免跨库主键重复问题
5、公共表
实际的应用场景中,参数表,数据字典表等都是数据量较少,变动少,而且属于高频联合查询的依赖表。
可以将这类表在每个数据库都保存一份,所有对公共表的更新操作都同时发送到所有分库执行
由于分库分表之后,数据被分散在不同的数据库、服务器。因此,对数据的操作也就无法通过常规方式完成,并且它还带来了一系列的问题。
Sharding-jdbc介绍
sharding-jdbc是当当网研发的开源分页式数据库中间件,从3.0开始Sharding-jdbc被包含在Sharding-Sphere,之后该项目进入 Apache,4.0版本之后的版本为Apache版本
Sharding-jdbc它定位为级Java框架,在java的JDBC层提供的额外服务。它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
Sharding-jdbc的核心功能为数据分片和读写分离,通过Sharding-JDBC,应用可以透明的使用jdbc访问已经分库分表、读写分离的多个数据源,而不用关心数据源的数量以及数据如何分页。
与jdbc性能对比:
1、性能损耗测试:服务器资源充足、并发数相同,比较JDBC和Sharding-JDBC性能损耗,sharding-jdbc相对JDBC损耗不超过7%
2、性能对比测试:服务器资源使用到极限,相同场景JDBC与sharding-jdbc的吞吐量相当
3、性能对比测试:服务器资源使用到极限,sharding-jdbc采用分库分表后,sharding-jdbc吞吐量较JDBC不分表有接近2倍提升
sharding-jdbc快速入门
使用sharding-jdbc完成对订单表的水平分表,通过快速入门程序的开发,快速体验sharding-jdbc的使用方法。
人工创建两张表,t_order_1和t_order_2,这两张表是订单表拆分后的表,通过sharding-jdbc向订单表插入数据,按照一定的分片规则,主键为偶数的进入t_order_1,另一部分数据进入t_order_2,通过sharding-jdbc查询数据,根据SQL语句的内容从t_order_1或t_order_2查询数据。
代码mode(水平分表):
配置yml
- server:
- port: 8082
- servlet:
- context-path: /sharding-jdbc-simple-demo
-
- spring:
- application:
- name: sharding-jdbc-simple-demo
- http:
- encoding:
- enabled: true
- charset: UTF-8
- force: true
- #shrading-jdbc配置
- shardingsphere:
- datasource:
- names: m1
- m1:
- type: com.alibaba.druid.pool.DruidDataSource
- driver-class-name: com.mysql.jdbc.Driver
- url: jdbc:mysql://localhost:3306/shareding?useUnicode=true
- username: root
- password: 123456
- sharding: #指定t_order表的数据分页情况,配置数据节点 m1.t_order_1 m1.t_order_2
- tables:
- t_order:
- actual-data-nodes: m1.t_order_$->{1..2}
- key-generator:
- column: id #主键字段
- type: SNOWFLAKE #指定t_order表主键生成策略
- table-strategy: #指定t_order表的分片策略,分片策略包括分片键和分片算法
- inline:
- sharding-column: id #分片键
- algorithm-expression: t_order_$->{id%2+1} #分片算法
- props:
- sql:
- show: true #输出sharding-jdbc的真实sql
-
- mybatis:
- configuration:
- map-underscore-to-camel-case: true
-
- swagger:
- enable: true
- logging:
- level:
- root: info
- org:
- springframeword:
- web: info
- cn:
- ping: debug
- druid:
- sql: debug
maven:
- "1.0" encoding="UTF-8"?>
"http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
-
org.springframework.boot -
spring-boot-starter-parent -
2.0.3.RELEASE -
-
4.0.0 -
-
sharding-jdbc-simple -
-
-
-
org.springframework.boot -
spring-boot-starter-web -
-
-
-
com.alibaba -
druid-spring-boot-starter -
1.2.11 -
-
-
-
mysql -
mysql-connector-java -
5.1.46 -
-
-
-
com.baomidou -
mybatis-plus-boot-starter -
3.5.1 -
-
-
-
org.apache.shardingsphere -
sharding-jdbc-spring-boot-starter -
4.0.0-RC1 -
-
-
-
org.projectlombok -
lombok -
-
-
-
io.springfox -
springfox-swagger-ui -
2.8.0 -
-
-
-
org.springframework.boot -
spring-boot-starter-test -
-
-
-
mysql创建表:
- CREATE TABLE `t_order_1` (
- `id` bigint(20) NOT NULL COMMENT '订单id',
- `price` decimal(10,2) NOT NULL COMMENT '订单价格',
- `user_id` bigint(20) NOT NULL COMMENT '下单用户id',
- `status` varchar(50) NOT NULL COMMENT '订单状态',
- PRIMARY KEY (`id`) USING BTREE
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
- CREATE TABLE `t_order_2` (
- `id` bigint(20) NOT NULL COMMENT '订单id',
- `price` decimal(10,2) NOT NULL COMMENT '订单价格',
- `user_id` bigint(20) NOT NULL COMMENT '下单用户id',
- `status` varchar(50) NOT NULL COMMENT '订单状态',
- PRIMARY KEY (`id`) USING BTREE
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
实体类:
- package cn.ping.entity;
-
- import com.baomidou.mybatisplus.annotation.TableName;
- import lombok.Builder;
- import lombok.Data;
-
- import java.math.BigDecimal;
-
- /**
- * @author: yejianping
- * @date: 2022/12/5 15:12:25
- * @email: 1152665905@qq.com
- * @Description:
- */
- @Data
- @Builder
- @TableName("t_order")
- public class Order {
-
- private Long id;
-
- private BigDecimal price;
-
- private Long userId;
-
- private String status;
- }
mapper类:
- package cn.ping.mapper;
-
- import cn.ping.entity.Order;
- import com.baomidou.mybatisplus.core.mapper.BaseMapper;
- import org.apache.ibatis.annotations.Param;
- import org.apache.ibatis.annotations.Select;
-
- import java.util.List;
-
- /**
- * @author: yejianping
- * @date: 2022/12/5 15:14:17
- * @email: 1152665905@qq.com
- * @Description:
- */
- public interface OrderMapper extends BaseMapper
{ -
-
- List
selectOrderList(@Param("position") Integer startPosition, @Param("size")Integer size); - }
mapper.xml
- "1.0" encoding="UTF-8"?>
- "-//mybatis.org//DTD Mapper 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
"cn.ping.mapper.OrderMapper"> -
- select * from t_order order by price limit #{position} ,#{size}
service
- package cn.ping.server;
-
- import cn.ping.entity.Order;
- import com.baomidou.mybatisplus.core.metadata.IPage;
- import com.baomidou.mybatisplus.extension.service.IService;
-
- import java.util.List;
-
- /**
- * @author: yejianping
- * @date: 2022/12/5 15:16:52
- * @email: 1152665905@qq.com
- * @Description:
- */
- public interface IOrderService extends IService
{ -
- /**
- * 通过ids查询Order
- * @param ids
- * @return
- */
- List
getOrderByIds(List ids) ; -
- IPage
getOrderPage(Integer page,Integer size); -
- List
getOrderList(Integer page,Integer size); - }
serviceImpl
- package cn.ping.server.impl;
-
- import cn.ping.entity.Order;
- import cn.ping.mapper.OrderMapper;
- import cn.ping.server.IOrderService;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.baomidou.mybatisplus.core.metadata.IPage;
- import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.stereotype.Service;
- import org.springframework.util.CollectionUtils;
-
- import java.util.List;
-
- /**
- * @author: yejianping
- * @date: 2022/12/5 15:21:06
- * @email: 1152665905@qq.com
- * @Description:
- */
- @Service
- @Slf4j
- public class OrderServiceImpl extends ServiceImpl
implements IOrderService { -
- @Override
- public List
getOrderByIds(List ids) { -
- if(CollectionUtils.isEmpty(ids)){
- return null;
- }
- return this.listByIds(ids);
- }
-
- @Override
- public IPage
getOrderPage(Integer page, Integer size) { -
- LambdaQueryWrapper
lqw = new LambdaQueryWrapper(); - lqw.orderByDesc(Order::getPrice);
-
-
- return page(new Page<>(page,size),lqw);
-
- }
-
- @Override
- public List
getOrderList(Integer page,Integer size){ - return this.baseMapper.selectOrderList((page-1)*size,size);
-
- }
-
- }
测试类
- package cn.ping;
-
- import cn.ping.entity.Dog;
- import cn.ping.entity.Order;
- import cn.ping.server.IDogService;
- import cn.ping.server.IOrderService;
- import com.baomidou.mybatisplus.core.metadata.IPage;
- import lombok.extern.slf4j.Slf4j;
- import net.bytebuddy.asm.Advice;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.test.context.SpringBootTest;
- import org.springframework.test.context.junit4.SpringRunner;
-
- import java.math.BigDecimal;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.List;
- import java.util.Random;
-
- /**
- * @author: yejianping
- * @date: 2022/12/5 15:22:35
- * @email: 1152665905@qq.com
- * @Description:
- */
- @RunWith(SpringRunner.class)
- @SpringBootTest(classes = {ShardingJdbcSimpleApplication.class})
- @Slf4j
- public class OrderTest {
-
- @Autowired
- private IOrderService orderService;
-
- @Autowired
- private IDogService dogService;
-
- @Test
- public void testOrderInsert(){
-
- List
orderList = new ArrayList<>(); - for (int i = 0; i < 20; i++) {
-
- Order order = Order.builder()
- .price(new BigDecimal(new Random().nextInt(100)))
- .status("SUCCESS")
- .userId(1L)
- .build();
- orderList.add(order);
- }
- orderService.saveBatch(orderList);
- /*Order order = Order.builder()
- .price(new BigDecimal(10))
- .status("SUCCESS")
- .userId(1L)
- .build();
- orderService.save(order);*/
- }
-
-
- @Test
- public void testFindByIds(){
- Long[] ids = {1599673041937334275L,1599673040175726594L};
- List
orderByIds = orderService.getOrderByIds(Arrays.asList(ids)); - for (Order order : orderByIds) {
- log.info(order.toString());
- }
- }
-
- @Test
- public void testOrderPage(){
- IPage
orderPage = orderService.getOrderPage(1, 10); - for (Order order : orderPage.getRecords()) {
- log.info(order.toString());
- }
- }
-
- @Test
- public void testOrderList(){
- List
orderList = orderService.getOrderList(1, 10); - for (Order order : orderList) {
- log.info(order.toString());
- }
- }
- }
sharding-jdbc和mybatis plus分页冲突,不能使用分页插件,查询总数有问题