《微服务实战》 第三十一章 ShardingSphere - ShardingSphere-JDBC

前言

Apache ShardingSphere 是一款分布式的数据库生态系统, 可以将任意数据库转换为分布式数据库,并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。

Apache ShardingSphere 设计哲学为 Database Plus,旨在构建异构数据库上层的标准和生态。 它关注如何充分合理地利用数据库的计算和存储能力,而并非实现一个全新的数据库。 它站在数据库的上层视角,关注它们之间的协作多于数据库自身。

1、ShardingSphere-JDBC

ShardingSphere-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。

1.1、应用场景

Apache ShardingSphere-JDBC 可以通过Java 和 YAML 这 2 种方式进行配置,开发者可根据场景选择适合的配置方式。

  • 数据库读写分离
  • 数据库分表分库

1.2、原理

  • Sharding-JDBC中的路由结果是通过分片字段和分片方法来确定的,如果查询条件中有 id 字段的情况还好,查询将会落到某个具体的分片
  • 如果查询没有分片的字段,会向所有的db或者是表都会查询一遍,让后封装结果集给客户端。
    在这里插入图片描述

1.3、spring boot整合

1.3.1、添加依赖

<!-- 分表分库依赖 -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.1.1</version>
</dependency>

1.3.2、添加配置

spring:
  main:
    # 一个实体类对应多张表,覆盖
    allow-bean-definition-overriding: true
  shardingsphere:
    datasource:
      ds0:
        #配置数据源具体内容,包含连接池,驱动,地址,用户名和密码
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/account?autoReconnect=true&allowMultiQueries=true
        password: root
        type: com.zaxxer.hikari.HikariDataSource
        username: root
      ds1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/account?autoReconnect=true&allowMultiQueries=true
        password: root
        type: com.zaxxer.hikari.HikariDataSource
        username: root
      # 配置数据源,给数据源起名称
      names: ds0,ds1
    props:
      sql:
        show: true
    sharding:
      tables:
        user_info:
          #指定 user_info 表分布情况,配置表在哪个数据库里面,表名称都是什么
          actual-data-nodes: ds0.user_info_${0..9}
          database-strategy:
            standard:
              preciseAlgorithmClassName: com.xxxx.store.account.config.PreciseDBShardingAlgorithm
              rangeAlgorithmClassName: com.xxxx.store.account.config.RangeDBShardingAlgorithm
              sharding-column: id
          table-strategy:
            standard:
              preciseAlgorithmClassName: com.xxxx.store.account.config.PreciseTablesShardingAlgorithm
              rangeAlgorithmClassName: com.xxxx.store.account.config.RangeTablesShardingAlgorithm
              sharding-column: id

1.3.3、制定分片算法

1.3.3.1、精确分库算法

/**
 * 精确分库算法
 */
public class PreciseDBShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    /**
     *
     * @param availableTargetNames 配置所有的列表
     * @param preciseShardingValue 分片值
     * @return
     */
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> preciseShardingValue) {
        Long value = preciseShardingValue.getValue();
        //后缀 0,1
        String postfix = String.valueOf(value % 2);

        for (String availableTargetName : availableTargetNames) {
            if(availableTargetName.endsWith(postfix)){
                return availableTargetName;
            }
        }

        throw new UnsupportedOperationException();
    }

}

1.3.3.2、范围分库算法

/**
 * 范围分库算法
 */
public class RangeDBShardingAlgorithm implements RangeShardingAlgorithm<Long> {


    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {
        return collection;
    }
}

1.3.3.3、精确分表算法

/**
 * 精确分表算法
 */
public class PreciseTablesShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    /**
     *
     * @param availableTargetNames 配置所有的列表
     * @param preciseShardingValue 分片值
     * @return
     */
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> preciseShardingValue) {
        Long value = preciseShardingValue.getValue();
        //后缀
        String postfix = String.valueOf(value % 10);

        for (String availableTargetName : availableTargetNames) {
            if(availableTargetName.endsWith(postfix)){
                return availableTargetName;
            }
        }

        throw new UnsupportedOperationException();
    }

}

1.3.3.4、范围分表算法

/**
 * 范围分表算法
 */
public class RangeTablesShardingAlgorithm implements RangeShardingAlgorithm<Long> {

    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {

        Collection<String> result = new ArrayList<>();
        Range<Long> valueRange = rangeShardingValue.getValueRange();
        Long start = valueRange.lowerEndpoint();
        Long end = valueRange.upperEndpoint();

        Long min = start % 10;
        Long max = end % 10;

        for (Long i = min; i < max +1; i++) {
            Long finalI = i;
            collection.forEach(e -> {
                if(e.endsWith(String.valueOf(finalI))){
                    result.add(e);
                }
            });
        }
        return result;
    }

}

1.3.4、数据库建表

DROP TABLE IF EXISTS `user_info_0`;
CREATE TABLE `user_info_0` (
  `id` bigint(20) NOT NULL,
  `account` varchar(255) DEFAULT NULL,
  `user_name` varchar(255) DEFAULT NULL,
  `pwd` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

1.3.5、业务应用

1.3.5.1、定义实体类

@Data
@TableName(value = "user_info")
public class UserInfo {
    /**
     * 主键
     */
    private Long id;
    /**
     * 账号
     */
    private String account;
    /**
     * 用户名
     */
    private String userName;
    /**
     * 密码
     */
    private String pwd;

}

1.3.5.2、定义接口

public interface UserInfoService{
    /**
     * 保存
     * @param userInfo
     * @return
     */
    public UserInfo saveUserInfo(UserInfo userInfo);

    public UserInfo getUserInfoById(Long id);

    public List<UserInfo> listUserInfo();
}

1.3.5.3、实现类

@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Override
    @Transactional
    public UserInfo saveUserInfo(UserInfo userInfo) {
        userInfo.setId(IdUtils.getId());
        this.save(userInfo);
        return userInfo;
    }

    @Override
    public UserInfo getUserInfoById(Long id) {

        return this.getById(id);
    }

    @Override
    public List<UserInfo> listUserInfo() {
        QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();
        userInfoQueryWrapper.between("id",1623695688380448768L,1623695688380448769L);
        return this.list(userInfoQueryWrapper);
    }
}

1.3.6、生成ID - 雪花算法

package com.xxxx.tore.common.utils;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;

/**
 * 生成各种组件ID
 */
public class IdUtils {

    /**
     * 雪花算法
     * @return
     */
    public static long getId(){
        Snowflake snowflake = IdUtil.getSnowflake(0, 0);
        long id = snowflake.nextId();
        return id;
    }
}
 

1.4、seata与sharding-jdbc整合

https://github.com/seata/seata-samples/tree/master/springcloud-seata-sharding-jdbc-mybatis-plus-samples

1.4.1、common中添加依赖

<!--seata依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <version>2021.0.4.0</version>
</dependency>
<!-- sharding-jdbc整合seata分布式事务-->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-transaction-base-seata-at</artifactId>
    <version>4.1.1</version>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>2021.0.4.0</version>
    <exclusions>
        <exclusion>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-client</artifactId>
    <version>1.4.2</version>
</dependency>

1.4.2、改造account-service服务

@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {
    @Autowired
    private OrderService orderService;
    @Autowired
    private StorageService storageService;

    /**
     * 存放商品编码及其对应的价钱
     */
    private static Map<String,Integer> map = new HashMap<>();
    static {
        map.put("c001",3);
        map.put("c002",5);
        map.put("c003",10);
        map.put("c004",6);

    }
    @Override
    @Transactional
    @ShardingTransactionType(TransactionType.BASE)
    public void debit(OrderDTO orderDTO) {
        //扣减账户余额
        int calculate = this.calculate(orderDTO.getCommodityCode(), orderDTO.getCount());

        AccountDTO accountDTO = new AccountDTO(orderDTO.getUserId(), calculate);

        QueryWrapper<Account> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.eq("id",1);
        objectQueryWrapper.eq(accountDTO.getUserId() != null,"user_id",accountDTO.getUserId());

        Account account = this.getOne(objectQueryWrapper);
        account.setMoney(account.getMoney() - accountDTO.getMoney());
        this.saveOrUpdate(account);

        //扣减库存
        this.storageService.deduct(new StorageDTO(null,orderDTO.getCommodityCode(),orderDTO.getCount()));
        //生成订单
        this.orderService.create(orderDTO);      
    }

    /**
     * 计算购买商品的总价钱
     * @param commodityCode
     * @param orderCount
     * @return
     */
    private int calculate(String commodityCode, int orderCount){
        //商品价钱
        Integer price = map.get(commodityCode) == null ? 0 : map.get(commodityCode);
        return price * orderCount;
    }
}

注意:调单生成调用的逻辑修改,减余额->减库存->生成订单。调用入口方法注解加上:@ShardingTransactionType(TransactionType.BASE)

1.4.3、修改business-service服务

@Service
public class BusinessServiceImpl implements BusinessService {
    @Autowired
    private OrderService orderService;
    @Autowired
    private StorageService storageService;
    @Autowired
    private AccountService accountService;

    @Override
    public void purchase(OrderDTO orderDTO) {
        //扣减账号中的钱
        accountService.debit(orderDTO);        
    }
}

1.4.4、修改order-service服务

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper,Order> implements OrderService {

    /**
     * 存放商品编码及其对应的价钱
     */
    private static Map<String,Integer> map = new HashMap<>();
    static {
        map.put("c001",3);
        map.put("c002",5);
        map.put("c003",10);
        map.put("c004",6);
    }
    @Override
    @Transactional
    @ShardingTransactionType(TransactionType.BASE)
    public Order create(String userId, String commodityCode, int orderCount) {
        int orderMoney = calculate(commodityCode, orderCount);

        Order order = new Order();
        order.setUserId(userId);
        order.setCommodityCode(commodityCode);
        order.setCount(orderCount);
        order.setMoney(orderMoney);
        
        //保存订单
        this.save(order);
        try {
            TimeUnit.SECONDS.sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if(true){
            throw new RuntimeException("回滚测试");
        }
        return order;
    }

    /**
     * 计算购买商品的总价钱
     * @param commodityCode
     * @param orderCount
     * @return
     */
    private int calculate(String commodityCode, int orderCount){
        //商品价钱
        Integer price = map.get(commodityCode) == null ? 0 : map.get(commodityCode);
        return price * orderCount;
    }
}

1.4.5、配置文件参考

server:
  port: 8090

spring:
  main:
    # 一个实体类对应多张表,覆盖
    allow-bean-definition-overriding: true
  shardingsphere:
    datasource:
      ds0:
        #配置数据源具体内容,包含连接池,驱动,地址,用户名和密码
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/account?autoReconnect=true&allowMultiQueries=true
        password: root
        type: com.zaxxer.hikari.HikariDataSource
        username: root
      ds1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/account?autoReconnect=true&allowMultiQueries=true
        password: root
        type: com.zaxxer.hikari.HikariDataSource
        username: root
      # 配置数据源,给数据源起名称
      names: ds0,ds1
    props:
      sql:
        show: true
    sharding:
      tables:
        account_tbl:
          actual-data-nodes: ds0.account_tbl_${0..1}
          database-strategy:
            standard:
              preciseAlgorithmClassName: com.xxxx.store.account.config.PreciseDBExtShardingAlgorithm
              #rangeAlgorithmClassName: com.xxxx.store.account.config.RangeDBShardingAlgorithm
              sharding-column: id
          table-strategy:
            standard:
              preciseAlgorithmClassName: com.xxxx.store.account.config.PreciseTablesExtShardingAlgorithm
              #rangeAlgorithmClassName: com.xxxx.store.account.config.RangeTablesShardingAlgorithm
              sharding-column: id

        user_info:
          #指定 user_info 表分布情况,配置表在哪个数据库里面,表名称都是什么
          actual-data-nodes: ds0.user_info_${0..9}
          database-strategy:
            standard:
              preciseAlgorithmClassName: com.xxxx.store.account.config.PreciseDBShardingAlgorithm
              rangeAlgorithmClassName: com.xxxx.store.account.config.RangeDBShardingAlgorithm
              sharding-column: id
          table-strategy:
            standard:
              preciseAlgorithmClassName: com.xxxx.store.account.config.PreciseTablesShardingAlgorithm
              rangeAlgorithmClassName: com.xxxx.store.account.config.RangeTablesShardingAlgorithm
              sharding-column: id




  #以上是sharding-jdbc配置
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        namespace: 1ff3782d-b62d-402f-8bc4-ebcf40254d0a
  application:
    name: account-service  #微服务名称
#  datasource:
#    username: root
#    password: root
#    url: jdbc:mysql://127.0.0.1:3306/account
#    driver-class-name: com.mysql.cj.jdbc.Driver

seata:
  enabled: true
  enable-auto-data-source-proxy: false
  application-id: account-service
  tx-service-group: default_tx_group
  service:
    vgroup-mapping:
      default_tx_group: default
    disable-global-transaction: false
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      namespace: 1ff3782d-b62d-402f-8bc4-ebcf40254d0a
      group: SEATA_GROUP
      username: nacos
      password: nacos
  config:
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: 1ff3782d-b62d-402f-8bc4-ebcf40254d0a
      group: SEATA_GROUP
      username: nacos
      password: nacos

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/29959.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python 随练】统计字符类型个数

题目&#xff1a; 输入一行字符&#xff0c;分别统计出其中英文字母、空格、数字和其它字符的个数。 简介&#xff1a; 在本篇博客中&#xff0c;我们将解决一个字符统计问题&#xff1a;输入一行字符&#xff0c;统计其中英文字母、空格、数字和其他字符的个数。我们将提供…

NoSQLBooster 8.0.11 for MongoDB

MongoDB最智能的IDE。 NoSQLBooster 是适用于 MongoDB Server 3.6-6.0 的跨平台 GUI 工具&#xff0c;它提供了内置的 MongoDB 脚本调试器、全面的服务器监控工具、链接流畅查询、SQL 查询、查询代码生成器、任务调度、ES2020 支持和高级 IntelliSense 体验。 嵌入式MongoDB S…

强化学习从基础到进阶-常见问题和面试必知必答[1]:强化学习概述、序列决策、动作空间定义、策略价值函数、探索与利用、Gym强化学习实验

【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧&#xff08;调参、画图等、趣味项目实现、学术应用项目实现 专栏详细介绍&#xff1a;【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧…

redis键值对映射关系存储-Dict

基本概述 Redis是一个键值型&#xff08;Key-Value Pair&#xff09;的数据库&#xff0c;可以根据键实现快速的增删改查。而键与值的映射关系正是通过Dict来实现的。 Dict由三部分组成&#xff0c;分别是&#xff1a;哈希表&#xff08;DictHashTable&#xff09;、哈希节点&a…

redis安装

在官网下载: https://redis.io/download/ 或者直接下载: ​wget https://download.redis.io/releases/redis-6.2.12.tar.gz 解压到/usr/local/下 [rootlocalhost ~]# tar redis-6.2.12.tar.gz -C /usr/local/ [rootlocalhost ~]# cd /usr/local/redis-6.2.12 [rootlocalho…

C++算法————二分查找

又是鸽了三千万年 马上要打csp了&#xff0c;开始回流学j组的知识了&#xff0c;浅说一下二分吧&#xff08;&#xff09; --------------------------------------------------------------------------------------------------------------------------------- 二分查找 …

了解MVC、MVP、MVVM模式

前言 在Android开发中&#xff0c;当你梳理完需求后&#xff0c;你要做的并不是马上写下你的第一行代码&#xff0c;而是需先设计好整个项目的技术框架今天&#xff0c;我将全面介绍Android开发中主流的技术框架MVC、MVP 与 MVVM模式&#xff0c;并实例讲解MVP模式&#xff0c…

面试篇:SpringCloud

一、SpringCloud常见的组件有什么&#xff1f; 1、常见微服务功能架构图 2、阿里巴巴SpringCloud常用组件 注册中心/配置中心&#xff1a;Nacos负载均衡&#xff1a;Ribbon服务调用&#xff1a;Feign服务保护&#xff1a;Sentinel服务网关&#xff1a;Gateway 二、服务注册…

Compose Desktop 实战 宝可梦图鉴

Compose Desktop 实战 宝可梦图鉴 前言 阅读本文需要一定compose基础&#xff0c;如果没有请移步Jetpack Compose入门详解&#xff08;实时更新&#xff09; 接口数据来源于pokeapi 项目源代码 如果你觉得不错&#xff0c;请给我一个star&#xff0c;THKS 实现效果 闲话不…

php通过cURL爬取数据(3):CURLINFO_HTTP_CODE返回0的排查和解决方案

CURLINFO_HTTP_CODE返回0的排查和解决方案 一、curl本地服务器需要DNS解析域名二、如何排查错误原因三、无法解析 DNS的程序升级方案四、宝塔配置DNS的操作方法1.etc/resolv.conf2.通过GUI界面 一、curl本地服务器需要DNS解析域名 在使用 curl 命令发送请求到域名地址&#xf…

测试(二)

1.软件测试的生命周期 需求分析→测试计划→ 测试设计→ 测试开发→ 测试执行→ 测试评估 2.如何描述一个Bug 3.Bug的优先级 1、Blocker&#xff08;崩溃&#xff09;&#xff1a; 阻碍开发或测试工作的问题&#xff1b;造成系统崩溃、死机、死循环&#xff0c;导致数据库数…

Unity基础 视频组件VideoPlayer,视频的播放与控制

在Unity中&#xff0c;视频播放功能具有广泛的应用&#xff0c;以下是一些视频播放在Unity中的常见用途&#xff1a; 游戏引入和过场动画&#xff1a;使用视频播放可以在游戏开始或过场动画中添加引人注目的视频&#xff0c;为游戏制造氛围和引起玩家的兴趣。这种方式可以通过播…

CSS基础学习--11 padding(填充)

一、定义 CSS padding&#xff08;填充&#xff09;是一个简写属性&#xff0c;定义元素边框与元素内容之间的空间&#xff0c;即上下左右的内边距。 当元素的 padding&#xff08;填充&#xff09;内边距被清除时&#xff0c;所释放的区域将会受到元素背景颜色的填充。 单独使…

Linux运维监控学习笔记1

1. 监控系统的概念&#xff1a; 监控系统&#xff0c;将所有需要监控的服务器及其各种各种需要的状态数据都实时地收集&#xff0c;并图形化地展示&#xff0c;并可以进行报警&#xff0c;让机器主动及时地与人沟通。 2. 为什么要监控&#xff1f; 答&#xff1a;实时地收集数…

kubernetes(k8s)理论篇

注意&#xff1a;kubeadm与docker是有版本要求的。 如果版本不兼容&#xff0c;初始化 kubeadm是会出现以下问题。 学习k8s掌握知识 基础概念 什么是 Pod 控制器类型 K8S 网络通讯模式 Kubernetes 构建 K8S 集群 资源清单 资源 掌握资源清单的语法 编写 Pod 掌握 Pod 的…

JVM知识点整理

JVM 回收哪个区域&#xff1f;关联面试题&#xff1a;fullgc会回收方法区&#xff08;元空间&#xff09;吗? 怎么判断对象可以被回收了关联面试题&#xff1a;哪些对象可以作为 GC Root &#xff08;两栈两方法&#xff09; JVM GC什么时候执行&#xff1f;分代回收机制思考&…

docker容器启动的问题 - docker容器和虚拟机的比较 - docker的底层隔离机制

目录 一、docker容器启动的问题&#xff1f; 二、什么是docker仓库&#xff1f; 三、虚拟机和docker容器的区别&#xff1a; docker的优势&#xff1a; docker的缺点&#xff1a; 对比&#xff1a; 四、docker的底层隔离机制 参考文献&#xff1a;LXC linux容器简介——…

图像 检测 - CenterNet: Objects as Points (arXiv 2019)

CenterNet: Objects as Points - 目标作为点&#xff08;arXiv 2019&#xff09; 摘要1. 引言2. 相关工作3. 准备工作4. 目标作为点4.1 3D 检测4.2 人体姿态估计 5. 实施细节6. 实验6.1 目标检测6.1.1 附加实验 6.2 3D 检测6.3 姿态估计 7. 结论References附录A&#xff1a;模型…

【C数据结构】动态顺序表_SeqList

目录 【1】数据结构概述 【1.1】什么是数据结构&#xff1f; 【1.2】数据结构分类 【1.3】数据结构术语 【2】数据结构特点 【2】动态顺序表 【2.1】动态顺序表定义数据结构和接口 【2.1】动态顺序表创建初始化 【2.2】动态顺序表初始化 【2.3】动态顺序表内存释放 【…

mac电脑储存内存越来越小如何清理释放空间?

如果你是一位Mac系统的用户&#xff0c;可能会发现你的电脑储存空间越来越小。虽然Mac系统设计得非常优秀&#xff0c;但是系统数据和垃圾文件也会占据大量的储存空间。在这篇文章中&#xff0c;我们将探讨mac系统数据怎么这么大&#xff0c;以及mac清理系统数据怎么清理。 一…