springboot第55集:思维导图Sharding-JDBC,事务,微服务分布式架构周刊

事务相关知识,你知道多少?

事务定义

在数据库管理系统中,事务是单个逻辑或工作单元,有时由多个操作组成,在数据库中以一致模式完成的逻辑处理称为事务。一个例子是从一个银行账户转账到另一个账户:完整的交易需要减去从一个账户转账的金额,然后将相同的金额添加到另一个账户。

事务特性

  1. 原子性(Atomicty)

  2. 一致性(Consistency)

  3. 隔离性(Isolation)

  4. 持久性(Durability)

原子性( atomicty)

事务中的全部操作在数据库中是不可分割的,要么全部完成,要么全部不执行。

一致性(consistency)

事务的执行不能破坏数据库数据的完整性和一致性。一致性指数据满足所有数据库的条件,比如字段约束、外键约束、触发器等,事务从一致性开始,以一致性结束。

隔离性( isolation)

事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务是透明的。

持久性(durability)

对于提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。

注: DBMS一般采用日志来保证事务的原子性、一致性和持久性。

事务隔离级别

并发事务带来的问题

脏读Dirty read

当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。

丢失修改Lost to modif

指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据,这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1并提交,事务2也修改A=A-1并提交,最终结果A=19,事务1的修改被丢失。

不可重复读Unrepeatable read

指在一个事务内多次读同一数据,在这个事务还没有结束时,另一个事务也访问该数据。在第一个事务中的两次读数据之间,由于第二个事务的修改并提交导致第一个事务两次读取的数据可能不一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。

幻读Phantom read

幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据并提交。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

不可重复读的重点是数据修改场景,幻读的重点在于新增或者删除场景。

事务隔离级别

SQL92标准定义了4种隔离级别的事务

读未提交Read uncommitted

事务可以读取未提交的数据。事务中的修改,即使没有提交,对其他事务也都是可见的(解决 脏读 不可重复读 幻读)

读已提交Read committed

一个事务开始后,只能看到已经提交的事务所做的修改(解决 不可重复读 幻读)

可重复读Repeatable read

在同一个事务中多次读取同样的记录的结果是一致的。它解决了脏读和不可重复读问题,但还是无法解决幻读问题

可串行化Serializable

通过强制事务串行,避免了前面说的幻读问题,实际应用中很少

大多数数据库系统如oracle的默认隔离级别都是 Read committed,mysql默认为可重复读,InnoDB 和 XtraDB 存储引擎通过多版并发控制(MVCC,Multivesion Concurrency Control)解决了幻读问题,Repeatable read 是 Mysql 默认的事务隔离级别,其中 InnoDB主 要通过使用 MVVC 获得高并发,使用一种被称为 next-key-locking 的策略来避免幻读。

事务模型

事务提交模型

显式事务: 又称自定义事务,是指用显式的方式定义其开始和结束的事务,当使用start transaction和 commit语句时表示发生显式事务。

隐式事务: 隐式事务是指每一条数据操作语句都自动地成为一个事务,事务的开始是隐式的,事务的结束有明确的标记。即当用户进行数据操作时,系统自动开启一个事务,事务的结束则需手动调用 commit或 rollback语句来结束当前事务,在当前事务结束后又自动开启一个新事务。

自动事务: 自动事务是指能够自动开启事务并且能够自动结束事务。在事务执行过程中,如果没有出现异常,事务则自动提交;当执行过程产生错误时,则事务自动回滚;一条SQL语句一个事务。

事务编程模型

本地事务模型: 事务由本地资源管理器来管理。简单理解就是直接使用JDBC的事务API。

connection.setAutoCommit(false);// 自动提交关闭
//XXXX数据库的增删改查操作
connection.commit(); //提交事务

编程式事务模型: 事务通过JTA以及底层的JTS实现来管理,对于开发人员而言,管理的是“事务”,而非“连接”。简单理解就是使用事务的API写代码控制事务。

示例一、JTA的API编程

UserTransaction txn = sessionCtx.getUserTransaction();
txn.begin();
txn.commit();

示例二、Spring的事务模版

transactionTemplate.execute(new TransactionCallback<Object>() {@Overridepublic Object doInTransaction(TransactionStatus status) {// 事务相关处理return null;}}
);

声明式事务: 事务由容器进行管理,对于开发人员而言,几乎不管理事务。简单理解就是加个事务注解或做个AOP切面。

@Transactional(rollbackFor = Exception.class)
public void updateStatus(String applyNo){cashierApplyMapper.updateStatus(applyNo, CANCEL_STATUS, CANCEL_STATUS_DESC);
}

本地事务模型   自主可控   侵入性较大,开发人员时刻关注事务边界,写大量commit代码,不支持XA

编程式事务模型   模型高了一个层级,自主可控性强   需要写代码,不优雅

声明式事务模型   简单易用   事务边界必须是方法级别,依赖AOP的机制

SQL的全称: Structured Query Language。中文翻译:结构化查询语言

以Spring+Mybatis+JDBC+Mysql为例,常见的事务类请求的调用链路如下图。请求调用应用服务,应用服务中开启事务并进行业务操作,操作过程中调用Mybatis进行数据库类操作,Mybatis通过JDBC驱动与底层数据库交互。

请求方->请求接受入口->Spring事务->业务操作->Mybatis->JDBC->数据库MySql

因此接下来先按Mysql、JDBC、Mybatis、Spring来介绍各层的事务相关知识;最后进行全链路的调用分析。

Mysql事务相关

Mysql逻辑架构

架构图如下(InnoDB存储引擎):

  • 客户端

  • 连接线程处理(连接处理,授权认证,安全)

  • 查询缓存(分析器->优化器->执行器)查询,解析,分析,优化,缓存

  • InnoDB存储引擎(Buffer Pool缓冲池)(Redo Log Buffer),undo日志文件,磁盘文件,redo日志文件,binlog文件(存储和读取数据)

MySQL事务是由存储引擎实现的,MySQL支持事务的存储引擎有InnoDB、NDB Cluster等,其中InnoDB的使用最为广泛,其他存储引擎如MyIsam、Memory等不支持事务。

Mysql的事务保证

Mysql的4个特性中有3个与 WAL(Write-Ahead Logging,先写日志,再写磁盘)有关系,需要通过 Redo、Undo 日志来保证等,而一致性需要通过DBMS的功能逻辑及原子性、隔离性、持久性共同来保证。

日志机制(Redo/Undo):原子性,隔离性,持久性

DBMS功能逻辑:一致性

利用Hutool+自定义注解实现数据脱敏

首先我们要先根据以上Hutool中提供的脱敏类型来编写我们自己的类型 或者 直接使用DesensitizedUtil中的DesensitizedType

public enum DataMaskingType {/*** 用户ID*/USER_ID,/*** 中文名*/CHINESE_NAME,/*** 身份证号*/ID_CARD,/*** 座机*/FIXED_PHONE,/*** 手机号*/MOBILE_PHONE,/*** 地址*/ADDRESS,/*** 邮箱*/EMAIL,/*** 密码*/PASSWORD,/*** 中国大陆车牌号*/CAR_LICENSE,/*** 银行卡号*/BANK_CARD,/*** IPv4地址*/IPV4,/*** IPv6地址*/IPV6,/*** 自定义类型*/CUSTOM;}

编写自定义注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DataMaskingSerialize.class)
public @interface DataMasking {/*** 数据脱敏类型*/DataMaskingType type() default DataMaskingType.CUSTOM;/*** 脱敏开始位置(包含)*/int start() default 0;/*** 脱敏结束位置(不包含)*/int end() default 0;}

需要注意的是:当DataMaskingType为 CUSTOM 时,才需要填写 start 和 end ,且这两个参数才会生效,且 start 中是包含当前下标的字符的,而 end 不包含当前下标的字符。

编写自定义序列化类

@AllArgsConstructor
@NoArgsConstructor
public class DataMaskingSerialize extends JsonSerializer implements ContextualSerializer {private DataMaskingType type;private Integer start;private Integer end;@Overridepublic void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {String value = (String) o;switch (type) {//userIdcase USER_ID:jsonGenerator.writeString(String.valueOf(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.USER_ID)));break;//中文名case CHINESE_NAME:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.CHINESE_NAME));break;//身份证号case ID_CARD:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.ID_CARD));break;//座机case FIXED_PHONE:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.FIXED_PHONE));break;//手机号case MOBILE_PHONE:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.MOBILE_PHONE));break;//地址case ADDRESS:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.ADDRESS));break;//邮箱case EMAIL:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.EMAIL));break;case BANK_CARD:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.BANK_CARD));break;//密码case PASSWORD:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.PASSWORD));break;//中国大陆车牌号case CAR_LICENSE:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.CAR_LICENSE));break;case IPV4:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.IPV4));break;case IPV6:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.IPV6));break;//自定义case CUSTOM:jsonGenerator.writeString(CharSequenceUtil.hide(value, start, end));break;default:break;}}@Overridepublic JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {if (Objects.nonNull(beanProperty)) {//判断是否为string类型if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {DataMasking anno = beanProperty.getAnnotation(DataMasking.class);if (Objects.isNull(anno)) {anno = beanProperty.getContextAnnotation(DataMasking.class);}if (Objects.nonNull(anno)) {return new DataMaskingSerialize(anno.type(), anno.start(), anno.end());}}return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);}return serializerProvider.findNullValueSerializer(null);}
}

继承于 JsonSerializer 并实现了 ContextualSerializer 中的方法,并对我们自定义注解声明的字段进行拦截和脱敏加密操作

@Data
@Builder
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class TestEntity {@DataMasking(type = DataMaskingType.USER_ID)private Integer userId;@DataMasking(type = DataMaskingType.CHINESE_NAME)private String userName;@DataMasking(type = DataMaskingType.ADDRESS)private String address;@DataMasking(type = DataMaskingType.ID_CARD)private String idCard;@DataMasking(type = DataMaskingType.FIXED_PHONE)private String fixedPhone;@DataMasking(type = DataMaskingType.MOBILE_PHONE)private String mobilePhone;@DataMasking(type = DataMaskingType.EMAIL)private String email;@DataMasking(type = DataMaskingType.PASSWORD)private String password;@DataMasking(type = DataMaskingType.CAR_LICENSE)private String carLicense;@DataMasking(type = DataMaskingType.BANK_CARD)private String bankCard;@DataMasking(type = DataMaskingType.IPV4)private String ipv4;@DataMasking(type = DataMaskingType.IPV6)private String ipv6;@DataMasking(type = DataMaskingType.CUSTOM,start = 3,end = 9)private String custom;/*** 不进行数据脱敏的字段*/private String noMask;}
@RestController
public class TestController {@GetMapping("/test")public TestEntity test() {return TestEntity.builder().userId(1234567890).userName("张三").password("12").address("河南省郑xxx区").email("xxxx@xx.com").fixedPhone("08xxxxx792").mobilePhone("138xxxx888").carLicense("豫P3xxx3").bankCard("167xxxxx740").idCard("4127xxxx6677").ipv4("192xxxx.236").ipv6("abcd:12xxxxx567:089:0:0000").custom("2xxxx58794").noMask("我是不需要数据脱敏的字段").build();}}

使用了Hutool的DesensitizedUtil中的 desensitized 方法来实现数据脱敏,在 CUSTOM 类型的脱敏字段中,start 和 end 两个属性是必填的,且 start 包含当前下标,而 end 不包含当前下标。

浅谈Mysql读写分离的坑以及应对的方案

一、主从架构

为什么我们要进行读写分离?个人觉得还是业务发展到一定的规模,驱动技术架构的改革,读写分离可以减轻单台服务器的压力,将读请求和写请求分流到不同的服务器,分摊单台服务的负载,提高可用性,提高读请求的性能。

一个基础的Mysql的主从架构,1主1备3从。这种架构是客户端主动做的负载均衡,数据库的连接信息一般是放到客户端的连接层,也就是说由客户端来选择数据库进行读写

一个带proxy的主从架构,客户端只和proxy进行连接,由proxy根据请求类型和上下文决定请求的分发路由。 两种架构方案各有什么特点:

1.客户端直连架构,由于少了一层proxy转发,所以查询性能会比较好点儿,架构简单,遇到问题好排查。但是这种架构,由于要了解后端部署细节,出现主备切换,库迁移的时候客户端都会感知到,并且需要调整库连接信息

2.带proxy的架构,对客户端比较友好,客户端不需要了解后端部署细节,连接维护,后端信息维护都由proxy来完成。这样的架构对后端运维团队要求比较高,而且proxy本身也要求高可用,所以整体架构相对来说比较复杂

但是不论使用哪种架构,由于主从之间存在延迟,当一个事务更新完成后马上发起读请求,如果选择读从库的话,很有可能读到这个事务更新之前的状态,我们把这种读请求叫做过期读。出现主从延迟的情况有多种,有兴趣的同学可以自己了解一下,虽然出现主从延迟我们同样也有应对策略,但是不能100%避免,这些不是我们本次讨论的范围,我们主要讨论一下如果出现主从延迟,刚好我们的读走的都是从库,我们应该怎么应对?

首先我把应对的策略总结一下:

强制走主库

sleep方案

判断主从无延迟

等主库位点

等GTID方案

ThreadLocal:线程中的全局变量

最近接了一个新需求,业务场景上需要在原有基础上新增2个字段,接口新增参数意味着很多类和方法的逻辑都需要改变,需要先判断是否属于该业务场景,再做对应的逻辑。原本的打算是在入口处新增变量,在操作数据的时候进行逻辑判断将变量进行存储或查询。

如果全链路都变更入参和结构,很明显代码上很不优雅,后续如果还要增加业务场景,又需要再改一遍。如果有一个方法可以传递全局变量,而且仅限于当前线程就好了。

到此,会想到有两种解决方案:之前用的比较少的ThreadLocal或者使用redis缓存。考虑到新增字段都是些增删改查的操作,没有必要存到redis中,故使用ThreadLocal。

一、ThreadLocal定义

以微服务架构为例,服务提供方在收到调用方的请求后,会把这个请求分配给一个线程进行处理。一般来说,一个请求会一直由同一个线程处理,中间不会切换线程,所以如果有一个线程中共享的变量,可以当全局变量使用。

ThreadLocal实现的就是一个线程中的全局变量,与真正的全局变量的区别在于ThreadLocal的变量是每个线程中的全局变量,也就是说不同线程访问到的值是不一样的。其填充的变量属于当前线程,该变量对于其他线程是隔离的。

由定义可以发现,ThreadLocal有两个特性:每个Thread的变量只能由当前Thread使用;由于其他线程不可访问,则不存在多线程间共享的问题。

二、修饰

ThreadLocal提供了线程本地的实例,它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。

ThreadLocal变量通常被private static修饰,这样的好处是当一个线程结束时,它所使用的ThreadLocal实例副本都可被回收,避免重复创建。坏处就是这样做可能正好导致内存泄漏。

三、底层实现

ThreadLocal最朴素的内部实现是Map<threadlocal, Object>,这是一个HashMap,又称为ThreadLocalMap。但Java源码并不是Map<threadlocal, Object>的实现。这是因为如果多个线程访问同一个map,这个map需要是线程安全的,构造比较麻烦。Java采用了更简单粗暴的做法:每个线程都有自己的ThreadLocal专属map,里面可以存放多个ThreadLocal变量,这样就解决了多线程同时操作一个map带来的多线程并发问题。

因为要把ThreadLocal的变量当做全局变量使用,需要把变量与初始化函数写在通用的类中,如DDD领域模型中写在Common模块。

具体的实现如下:

public class ThreadLocalUtil {private static ThreadLocal<Integer> THREAD_LOCAL = new ThreadLocal<>();public static Integer getScene() {return THREAD_LOCAL.get();}public static void initScene(Integer scene) {if (THREAD_LOCAL == null) {THREAD_LOCAL = new ThreadLocal<>();}THREAD_LOCAL.set(scene);}public static void remove() {THREAD_LOCAL.remove();}
}

四、致命点

上面提到了的ThreadLocal会带来内存泄露的问题,深入分析下:

一个ThreadLocal实例对应当前线程的一个对象实例,如果把ThreadLocal声明为某个类的实例变量不是静态变量,那么每次创建一个该类的实例就会导致一个新的对象实例被创建。而这些被创建的实例是同一个类的实例,于是同一个线程可能会访问到同一个类的不同实例,这即使不会导致错误,也会导致重复创建同样的对象。如果使用static修饰后,只要相应的类没有被垃圾回收掉,那么这个类就会持有相对应的ThreadLocal实例引用。

ThreadLocal自身并不存储值,而是作为一个key来让线程从ThreadLocal中获取value。ThreadLocalMap中的key是弱引用,所以jvm在垃圾回收时如果外部没有强引用来引用它,ThreadLocal必然会被回收。但是,作为ThreadLocalMap中的key,ThreadLocal被回收后,ThreadLocalMap就会存在null,但value却不为null。如果当前线程一直不结束或者线程结束后不被你销毁,这会产生内存泄露(已分配空间的堆内存由于某种原因未释放或无法释放导致系统内存浪费或程序运行变慢甚至系统奔溃)。

因此,key弱引用并不是导致内存泄露的原因,而是因为ThreadLocalMap的生命周期与当前线程一样长,并且没有手动删除对应的value。 解决的方法也很简单,只需要打破引用路径中的ThreadLocalMap对对象实例的引用即可。也就是在使用完ThreadLocal之后,必须调用ThreadLocal.remove()。

延伸:

为什么要将Map中的key设置为弱引用呢?

实际上,设置key为弱引用能预防大多数内存泄露的情况。如果key使用强引用,引用的ThreadLocal对象被回收,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,也会导致内存泄露。设置为弱引用后,引用的ThreadLocal对象被回收,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被java GC回收。value在下一次ThreadLocalMap调用set、get、remove的时候会被清除。

一、治理背景

数据库系统性能问题会对应用程序的性能和用户体验产生负面影响。慢查询可能导致应用程序响应变慢、请求堆积、系统负载增加等问题,甚至引发系统崩溃或不可用的情况。慢SQL治理是在数据库系统中针对执行缓慢的SQL查询进行优化和改进的一项重要工作。

但原有的治理节奏,一般在大促备战期间,集中投入人力紧急治理,日常对慢SQL的关注度不高;即使研发团队想着手治理,实例下的SQL明细筛选繁琐,趋势不明,缺少系统化,数字化的治理方案。

所以为了保证系统稳定性,预防潜在慢SQL导致应急事故,发起慢SQL常态化备战专项,下文主要描述专项的实践及落地情况。

Sharding-JDBC

Sharding-JDBC关系型数据库的水平扩张框架。

表 goods_0
goods_id bigint(20)
goods_name varchar(100)
goods_type bigint(20)

数据库表中字段goods_type的数值的奇偶进行判断,奇数使用goods_1表,偶数使用goods_0表。在应用程序中我们操作虚拟表goods,但是当真正操作数据库的时候,会根据我们的分库分表规则进行匹配然后操作。

创建表和数据库:

CREATE DATABASE database0;
use database0;
drop table if exists `goods_0`;
crate table `goods_0` (`goods_id` bigint(20) not null,`goods_name` varchar(100) collate utf8_bin not null,`goods_type` bigint(20) default null,primary key(`goods_id`)) engine = InnoDB default charset = utf8 collate = utf8_bin;drop table exists `goods_1`;
create table `goods_1`(`goods_id` bigint(20) not null,`goods_name` varchar(100) collate utf8_bin not null,`goods_type` bigint(20) default null,primary key (`goods_id`)
) engine = InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

加入当当的sharding-jdbc-core依赖和druid连接池

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="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"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.3.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.xx</groupId><artifactId>springboot2_shardingjdbc_fkfb</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot2_shardingjdbc_fkfb</name><description>springboot2_shardingjdbc_fkfb</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.9</version></dependency><!-- sharding-jdbc --><dependency><groupId>com.dangdang</groupId><artifactId>sharding-jdbc-core</artifactId><version>1.5.4</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
##Jpa配置
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none##数据库配置
##数据库database0地址
database0.url=jdbc:mysql://localhost:3306/database0?characterEncoding=utf8&useSSL=false
##数据库database0用户名
database0.username=root
##数据库database0密码
database0.password=root
##数据库database0驱动
database0.driverClassName=com.mysql.jdbc.Driver
##数据库database0名称
database0.databaseName=database0##数据库database1地址
database1.url=jdbc:mysql://localhost:3306/database1?characterEncoding=utf8&useSSL=false
##数据库database1用户名
database1.username=root
##数据库database1密码
database1.password=root
##数据库database1驱动
database1.driverClassName=com.mysql.jdbc.Driver
##数据库database1名称
database1.databaseName=database1
@SpringBootApplication @EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) @EnableTransactionManagement(proxyTargetClass = true) @EnableConfigurationProperties public class Springboot2ShardingjdbcFkfbApplication { public static void main(String[] args) { SpringApplication.run(Springboot2ShardingjdbcFkfbApplication.class, args); } }
@Entity @Table(name="goods") @Data public class Goods { @Id private Long goodsId; private String goodsName; private Long goodsType; }
@Data
@ConfigurationProperties(prefix = "database0")
@Component
public class Database0Config {private String url;private String username;private String password;private String driverClassName;private String databaseName;public DataSource createDataSource() {DruidDataSource result = new DruidDataSource();result.setDriverClassName(getDriverClassName());result.setUrl(getUrl());result.setUsername(getUsername());result.setPassword(getPassword());return result;}
}

新建DataSourceConfig用于创建数据源和使用分库分表策略,其中分库分表策略会调用分库算法类和分表算法类,DataSourceConfig类代码如下所示。

@Configuration
public class DataSourceCoding {@Autowiredprivate Database0Config database0Config;@Autowiredprivate Database1Config database1Config;@Autowiredprivate TableShardingAlgorithm tableShardingAlgorithm;@Beanpublic DataSource getDataSoure() throws SQLException [return buildDataSource();}private DataSource buildDataSource() throws SQLException {// 分库设置Map<String, DataSource> dataSourceMap = new HashMap<>(2);// 添加两个数据库 database0 和 database1dataSourceMap.put(database0Config.getDatabaseName(), database0Config.createDataSource());dataSourceMap.put(database1Config.getDatabaseName(), database1Config.createDataSource());// 设置默认数据库DataSourceRule dataSourceRule = new DataSourceRule(dataSourceMap, database0Config.getDatabaseName());//分表设置,大致思想就是将查询虚拟表goods根据一定规则映射到真实表中去TableRule orderTableRule = TableRule.builder("goods").actualTables(Arrays.asList("goods_0", "goods_1")).dataSourceRule(dataSourceRule).build();//分库分表策略ShardingRule shardingRule = ShardingRule.builder().dataSourceRule(dataSourceRule).tableRules(Arrays.asList(orderTableRule)).databaseShardingStrategy}@Beanpublic KeyGenerator keyGenerator() {return new DefaultKeyGenerator();}
}

加群联系作者vx:xiaoda0423

仓库地址:https://github.com/webVueBlog/JavaGuideInterview

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ChatGPT】的定价模式:免费还是收费?

ChatGPT的定价模式&#xff1a;免费还是收费&#xff1f; 人工智能技术的快速发展正在为我们的生活带来巨大的变化&#xff0c;而OpenAI最近推出的ChatGPT模型引发了人们对它的定价模式的关注。这篇文章将探讨ChatGPT是免费还是收费的问题&#xff0c;并对这个话题进行深入分析…

阿里云ECS香港服务器性能强大、cn2高速网络租用价格表

阿里云香港服务器中国香港数据中心网络线路类型BGP多线精品&#xff0c;中国电信CN2高速网络高质量、大规格BGP带宽&#xff0c;运营商精品公网直连中国内地&#xff0c;时延更低&#xff0c;优化海外回中国内地流量的公网线路&#xff0c;可以提高国际业务访问质量。阿里云服务…

免费chatgpt使用

基本功能如下&#xff1a; https://go.aigcplus.cc/auth/register?inviteCode3HCULH2UD

[嵌入式系统-25]:RT-Thread -12- 内核组件编程接口 - 网络组件 - HTTP编程

目录 一、HTTP编程概述 1.1 概述 1.2 HTTP 服务器和 HTTP 客户端 二、HTTP Client 2.1 如何配置HTTP Client 2.2 HTTP Client代码实例1&#xff1a;socket发送http报文 2.3 HTTP Client代码实例2&#xff1a;httpc_xx接口收发HTTP报文 2.3.1 接口函数描述 2.3.2 代码实…

中科大计网学习记录笔记(十二):TCP 套接字编程

前前言&#xff1a;大家看到这一章节的时候一定不要跳过&#xff0c;虽然标题是编程&#xff0c;但实际上是对 socket 的运行机制做了详细的讨论&#xff0c;对理解 TCP 有很大的帮助&#xff1b;但是由于本节涉及到了大量的编程知识&#xff0c;对于一些朋友来说不是很好理解&…

【深度学习】S3 线性神经网络 P1 线性回归(未完)

目录 线性回归基本元素基本名词线性模型 机器学习领域&#xff0c;大多数任务最终的目标都是预测。而预测的结果大致分为两大类&#xff0c;一种是需要估计连续数值的回归预测&#xff0c;另一种是确定离散类别的分类预测。本节博文将围绕线性回归内容。 线性回归基本元素 基…

Nginx (window)2024版 笔记 下载 安装 配置

前言 Nginx (engine x) 是一款轻量级的 Web 服务器 、反向代理&#xff08;Reverse Proxy&#xff09;服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。 反向代理方式是指以代理服务器来接受 internet 上的连接请求&#xff0c;然后将请求转发给内部网络上的服…

[AIGC_coze] Kafka 的主题分区之间的关系

Kafka 的主题分区之间的关系 在 Kafka 中&#xff0c;主题&#xff08;Topics&#xff09;和分区&#xff08;Partitions&#xff09;是两个重要的概念&#xff0c;它们之间存在着密切的关系。 主题是 Kafka 中用于数据发布和订阅的逻辑单元。每个主题可以包含多个分区&#x…

【退役之重学前端】关于在控制台得到undefined的事

在浏览器控制台中&#xff0c;undefined 会时不时地&#xff0c;在我不想看到的地方出现。如果你遇到相同的问题&#xff0c;在这篇博客中你会得到答案。 先来看代码块 function test(){} test()//undefined再看下一个代码块 function test(){return 1; } test()//1再来看一个…

BUGKU-WEB eval

题目描述 题目截图如下&#xff1a; 进入场景看看&#xff1a; <?phpinclude "flag.php";$a $_REQUEST[hello];eval( "var_dump($a);");show_source(__FILE__); ?>解题思路 PHP代码审计咯 相关工具 百度搜索PHP相关知识 解题步骤 分析脚…

OpenAI全新发布文生视频模型:Sora!

OpenAI官网原文链接&#xff1a;https://openai.com/research/video-generation-models-as-world-simulators#fn-20 我们探索视频数据生成模型的大规模训练。具体来说&#xff0c;我们在可变持续时间、分辨率和宽高比的视频和图像上联合训练文本条件扩散模型。我们利用对视频和…

【C++初阶】第三站:类和对象(中) -- 日期计算器

目录 前言 日期类的声明.h 日期类的实现.cpp 获取某年某月的天数 全缺省的构造函数 拷贝构造函数 打印函数 日期 天数 日期 天数 日期 - 天数 日期 - 天数 前置 后置 前置 -- 后置-- 日期类中比较运算符的重载 <运算符重载 运算符重载 ! 运算符重载 …

【Webpack】生产模式

生产模式介绍 生产模式是开发完成代码后&#xff0c;我们需要得到代码将来部署上线。 这个模式下我们主要对代码进行优化&#xff0c;让其运行性能更好。 优化主要从两个角度出发: 优化代码运行性能优化代码打包速度 生产模式准备 我们分别准备两个配置文件来放不同的配置…

用c语言做一个心算小游戏

有加减和乘法3种运算&#xff0c;由于除法涉及到浮点数存储有误差&#xff0c;所以比较难实现&#xff0c;改程序还有判定分数机制&#xff0c;根据难度给定合适的分数&#xff0c;随机抽取运算题目和符号。下面的代码适合Linux和安卓上的编译器&#xff0c;因为用了ANSI转义字…

SG5032EAN规格书

SG5032EAN 晶体振荡器结合了相位锁定环&#xff08;PLL&#xff09;技术和AT切割晶体单元&#xff0c;提供了73.5 MHz至700 MHz的广泛频率范围&#xff0c;以满足高速数字应用的需求。高性能的LV-PECL输出&#xff0c;2.5V和3.3V电源电压&#xff0c;可灵活适配不同设计的电源需…

layui表格中使用cascader后导致表格滚动条消失

修改前&#xff0c;受影响页面 修改后最终想要的效果 修改方法

《Go 简易速速上手小册》第8章:网络编程(2024 最新版)

文章目录 8.1 HTTP 客户端与服务端编程 - Go 语言的网络灯塔与探航船8.1.1 基础知识讲解服务端编程客户端编程 8.1.2 重点案例&#xff1a;简易博客服务服务端实现客户端实现运行示例 8.1.3 拓展案例 1&#xff1a;增加文章评论功能功能描述服务端实现客户端实现 8.1.4 拓展案例…

Python爬虫之Splash详解

爬虫专栏&#xff1a;http://t.csdnimg.cn/WfCSx Splash 的使用 Splash 是一个 JavaScript 渲染服务&#xff0c;是一个带有 HTTP API 的轻量级浏览器&#xff0c;同时它对接了 Python 中的 Twisted 和 QT 库。利用它&#xff0c;我们同样可以实现动态渲染页面的抓取。 1. 功…

sql常用语句小结

创建表&#xff1a; create table 表名&#xff08; 字段1 字段类型 【约束】【comment 字段1注释】&#xff0c; //【】里面的东西可以不用加上去 字段2 字段类型 【约束】【comment 字段2注释】 &#xff09;【comment 表注释】 约束&#xff1a;作用于表中字段上的规则…

【leetcode题解C++】455.分发饼干 and 376.摆动序列

455. 分发饼干 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口的饼干的最小尺寸&#xff1b;并且每块饼干 j&#xff0c;都…