当前位置: 首页 > news >正文

后台管理项目的多数据源方案

引言

在互联网开发公司中,往往伴随着业务的快速迭代,程序员可能没有过多的时间去思考技术扩展的相关问题,长久下来导致技术过于单一。为此最近在学习互联网思维,从相对简单的功能开始做总结,比如非常常见的基础数据的后台管理,那么涉及到多数据源的情况又会有哪些问题呢?

思考1:在业务中如何更加灵活方便的切换数据源呢?

思考2:多数据源之间的事务如何保证呢?

思考3:这种多数据源的分布式事务实现思路有哪些?

本篇文章的重点,也就是多数据源问题总结为以下三种方式:

使用Spring提供的AbstractRoutingDataSource

准备工作:

首先AbstractRoutingDataSource是jdbc包提供的,需要引入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

自定义一个类继承AbstractRoutingDataSource,重写关键方法:

@Component
@Primary
public class DynamicDatasource extends AbstractRoutingDataSource
{public static ThreadLocal<String> dataSourceName = new ThreadLocal<>();@AutowiredDataSource dataSource1;@AutowiredDataSource dataSource2;@Overrideprotected Object determineCurrentLookupKey(){return dataSourceName.get();}@Overridepublic void afterPropertiesSet(){Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put("W", dataSource1); //写数据库,主库targetDataSources.put("R", dataSource2); //读数据库,从库super.setTargetDataSources(targetDataSources);super.setDefaultTargetDataSource(dataSource1);super.afterPropertiesSet();}
}

配置多个数据源:

@Configuration
public class DataSourceConfig
{@Bean@ConfigurationProperties(prefix = "spring.datasource.datasource1")public DataSource dataSource1(){return DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "spring.datasource.datasource2")public DataSource dataSource2(){return DruidDataSourceBuilder.create().build();}@Beanpublic DataSourceTransactionManager transactionManager1(DynamicDatasource dataSource){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource);return dataSourceTransactionManager;}@Beanpublic DataSourceTransactionManager transactionManager2(DynamicDatasource dataSource){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource);return dataSourceTransactionManager;}
}

application.yml参考配置:

spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedatasource1:url: jdbc:mysql://127.0.0.1:3306/datasource1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: 123666initial-size: 1min-idle: 1max-active: 20test-on-borrow: truedriver-class-name: com.mysql.cj.jdbc.Driverdatasource2:url: jdbc:mysql://127.0.0.1:3306/datasource2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: 123666initial-size: 1min-idle: 1max-active: 20test-on-borrow: truedriver-class-name: com.mysql.cj.jdbc.Driver

min-idle,最小空闲连接,不会被销毁的数据库连接。注意:如果设置的太小,当客户端连接多的情况下,就需要新创建数据库连接,是一个比较耗时的操作,可能导致客户端连接超时。设置太大会占用系统资源

max-active:最大活跃数,官网建议配置:正在使用的数据库连接数 / 配置的这个值 = 85%

test-on-borrow:每次连接时都进行检查,生产上配置为true会影响性能,建议false,默认也是false

test-on-return:每次归还连接时进行检查,同样影响性能同上

生产上建议上面两个参数设置false,testWhileIdle设置为true,间隔一段时间检查连接是否可用:

空闲时间大于timeBetweenEvictionRunsMillis(默认1分钟)检查一次,检查发现连接失效也不会马上删除,而是空闲时间超过minEvictableIdleTimeMillis(最小空闲时间,默认30分钟)自动删除

maxEvictableIdleTimeMillis:最大空闲时间,默认7小时。空闲连接时间过长,数据库就会自动把连接关闭,Druid为了防止从连接池中拿到被数据库关闭的连接,设置了这个参数,超过时间强行关闭连接

使用测试:

方案一,直接在方法中设置数据源标识,简单实现功能,缺点也很明显

@Service
public class FriendServiceImpl implements FriendService
{@AutowiredFriendMapper friendMapper;@Overridepublic List<Friend> list(){DynamicDatasource.dataSourceName.set("R");return friendMapper.list();}@Overridepublic void save(Friend friend){DynamicDatasource.dataSourceName.set("W");friendMapper.save(friend);}
}

方案二,使用自定义注解+AOP实现,适合不同业务的多数据源场景

// 1、自定义注解:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WR
{String value() default "W";
}// 2、切面配置类
@Component
@Aspect
public class DynamicDataSourceAspect
{@Before("within(com.example.dynamicdatasource.service.impl.*) && @annotation(wr)")public void before(JoinPoint joinPoint, WR wr){String value = wr.value();DynamicDatasource.dataSourceName.set(value);System.out.println(value);}
}// 3、使用注解测试
@Service
public class FriendServiceImpl implements FriendService
{@AutowiredFriendMapper friendMapper;@WR("R")@Overridepublic List<Friend> list(){
//        DynamicDatasource.dataSourceName.set("R");return friendMapper.list();}@WR("W")@Overridepublic void save(Friend friend){
//        DynamicDatasource.dataSourceName.set("W");friendMapper.save(friend);}
}

方案三,使用MyBatis插件,适合相同业务读写分离的业务场景

import com.example.dynamicdatasource.DynamicDatasource;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;import java.util.Properties;@Component
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class,ResultHandler.class})})
public class DynamicDataSourcePlugin implements Interceptor
{@Overridepublic Object intercept(Invocation invocation)throws Throwable{Object[] objects = invocation.getArgs();MappedStatement ms = (MappedStatement)objects[0];if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)){DynamicDatasource.dataSourceName.set("R");}else{DynamicDatasource.dataSourceName.set("W");}return invocation.proceed();}@Overridepublic Object plugin(Object target){if (target instanceof Executor){return Plugin.wrap(target, this);}else{return target;}}@Overridepublic void setProperties(Properties properties){}
}

使用MyBatis注册多个sqlSessionFactory

实现思路:Spring集成多个MyBatis框架,指定不同的扫描包、不同的数据源

准备工作:

读库和写库分别添加配置类,扫描不同的包路径:

@Configuration
@MapperScan(basePackages = "com.example.dynamicmybatis.mapper.r", sqlSessionFactoryRef = "rSqlSessionFactory")
public class RMyBatisConfig
{@Bean@ConfigurationProperties(prefix = "spring.datasource.datasource2")public DataSource dataSource2(){return DruidDataSourceBuilder.create().build();}@Beanpublic DataSourceTransactionManager rTransactionManager(){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource2());return dataSourceTransactionManager;}@Beanpublic SqlSessionFactory rSqlSessionFactory()throws Exception{final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource2());return sqlSessionFactoryBean.getObject();}@Beanpublic TransactionTemplate rTransactionTemplate(){return new TransactionTemplate(rTransactionManager());}
}@Configuration
@MapperScan(basePackages = "com.example.dynamicmybatis.mapper.w", sqlSessionFactoryRef = "wSqlSessionFactory")
public class WMyBatisConfig
{@Bean@ConfigurationProperties(prefix = "spring.datasource.datasource1")public DataSource dataSource1(){return DruidDataSourceBuilder.create().build();}@Beanpublic DataSourceTransactionManager wTransactionManager(){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource1());return dataSourceTransactionManager;}@Beanpublic SqlSessionFactory wSqlSessionFactory()throws Exception{final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource1());return sqlSessionFactoryBean.getObject();}@Beanpublic TransactionTemplate wTransactionTemplate(){return new TransactionTemplate(wTransactionManager());}
}

写库和读库使用两个Mapper类,在不同的包下:

public interface RFriendMapper
{@Select("select * from friend")List<Friend> list();@Insert("insert into friend(name) values(#{name})")void save(Friend friend);
}public interface WFriendMapper
{@Select("select * from friend")List<Friend> list();@Insert("insert into friend(name) values(#{name})")void save(Friend friend);
}

使用测试:

@Service
public class FriendServiceImpl implements FriendService
{@AutowiredRFriendMapper rFriendMapper;@AutowiredWFriendMapper wFriendMapper;@Overridepublic List<Friend> list(){return rFriendMapper.list();}@Overridepublic void save(Friend friend){wFriendMapper.save(friend);}
}

思考:多数据源的事务问题

public void saveW(Friend friend)
{friend.setName("gaoW");wFriendMapper.save(friend);
}public void saveR(Friend friend)
{friend.setName("gaoR");rFriendMapper.save(friend);
}@Transactional
// @Transactional(transactionManager = "wTransactionManager")
@Override
public void saveAll(Friend friend)
{saveW(friend);saveR(friend);int a = 1 / 0;
}

存在多个事务管理器的情况直接使用@Transactional注解是不行的,Spring不知道使用哪个事务管理器会报错。但是指定了事务管理器后,仅当前事务管理器负责的部分支持回滚,还是存在问题。

在特定场景下,直接指定事务管理器名称的方式可以生效(保证数据一致的意思):

@Transactional(transactionManager = "wTransactionManager")
@Override
public void saveAll(Friend friend)
{saveW(friend);saveR(friend);int a = 1 / 0;
}

1、saveW方法内部异常,saveW发生异常事务不提交,数据一致

2、saveR方法内部异常,事务管理器回滚saveW的更新,saveR异常未提交,数据一致

3、saveW和saveR方法中间的业务发生异常,事务管理器回滚saveW的更新,saveR未提交,数据一致

4、saveW和saveR方法后面的业务发生异常,事务管理器回滚saveW的更新,saveR已提交,数据不一致

Spring提供的编程式事务解决方案:

@Service
public class FriendServiceImpl implements FriendService
{@AutowiredRFriendMapper rFriendMapper;@AutowiredWFriendMapper wFriendMapper;@AutowiredTransactionTemplate rTransactionTemplate;@AutowiredTransactionTemplate wTransactionTemplate;public void saveW(Friend friend){friend.setName("gaoW");wFriendMapper.save(friend);}public void saveR(Friend friend){friend.setName("gaoR");rFriendMapper.save(friend);}@Overridepublic void saveAll2(Friend friend){wTransactionTemplate.execute(wstatus -> {rTransactionTemplate.execute(rstatus -> {try{saveW(friend);saveR(friend);
//                    int a = 1 / 0;}catch (Exception e){e.printStackTrace();wstatus.setRollbackOnly();rstatus.setRollbackOnly();return false;}return true;});return true;});}
}

Spring支持的声明式事务解决方案(分布式事务变种实现):

@Service
public class FriendServiceImpl implements FriendService
{@AutowiredRFriendMapper rFriendMapper;@AutowiredWFriendMapper wFriendMapper;@AutowiredTransactionTemplate rTransactionTemplate;@AutowiredTransactionTemplate wTransactionTemplate;@Overridepublic List<Friend> list(){return rFriendMapper.list();}@Overridepublic void save(Friend friend){wFriendMapper.save(friend);}public void saveW(Friend friend){friend.setName("gaoW");wFriendMapper.save(friend);}public void saveR(Friend friend){friend.setName("gaoR");rFriendMapper.save(friend);}@Transactional(transactionManager = "wTransactionManager")@Overridepublic void saveAll1(Friend friend){FriendService friendService = (FriendService)AopContext.currentProxy();friendService.saveAllR(friend);}@Transactional(transactionManager = "rTransactionManager")@Overridepublic void saveAllR(Friend friend){saveW(friend);saveR(friend);
//        int a = 1 / 0;}
}@EnableAspectJAutoProxy(exposeProxy = true) //暴露代理对象
public class DynamicMybatisApplication {

注意:调用saveAllR方式时,需要使用代理对象,直接调用本类的其他方法事务不会生效

@Autowired自动注入自己获取代理对象,这种方式在springboot2.6以后有循环依赖报错,需要改配置,按照错误提示添加配置,设置参数为true即可

上面这两种事务的解决方式适用场景:

只涉及到两三个数据源,并且多数据源事务的场景不多,同时公司又不希望引入其他组件(安全性问题考虑),那么就可以使用这种方式实现分布式事务。当然分布式事务最好的解决方案肯定是通过第三方组件比如Seata

使用dynamic-datasource框架

dynamic-datasource是属于苞米豆生态圈的

基于Springboot的多数据源组件,功能强悍,支持Seata事务

  • 支持数据源分组,适用多库、读写分离、一主多从(实现了负载均衡,轮询/随机)等场景

  • 提供自定义数据源方案,比如从数据库加载

  • 提供项目启动后动态增加和删除数据源方案,可以添加管理后台页面灵活调整

  • 提供MyBatis环境下的纯读写分离方案

  • 提供本地多数据源事务方案

  • 提供基于Seata的分布式事务方案,注意不能和原生spring事务混用

  • 等等

多数据源实现方式:就是通过继承AbstractRoutingDataSource的这种方式

数据源切换:通过AOP+自定义注解实现的

使用示例:

@Service
public class FriendServiceImpl implements FriendService
{@AutowiredFriendMapper friendMapper;@Override@DS("slave")public List<Friend> list(){return friendMapper.list();}@Override@DS("master")@DSTransactionalpublic void save(Friend friend){friendMapper.save(friend);}@DS("master")@DSTransactionalpublic void saveAll(){// 执行多数据源的操作}
}

application.yml参考配置:

spring:datasource:dynamic:#设置默认的数据源或者数据源组,默认值即为masterprimary: master#严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源strict: falsedatasource:master:url: jdbc:mysql://127.0.0.1:3306/datasource1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: 123666initial-size: 1min-idle: 1max-active: 20test-on-borrow: truedriver-class-name: com.mysql.cj.jdbc.Driverslave_1:url: jdbc:mysql://127.0.0.1:3306/datasource2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: 123666initial-size: 1min-idle: 1max-active: 20test-on-borrow: truedriver-class-name: com.mysql.cj.jdbc.Driver

相关文章:

后台管理项目的多数据源方案

引言 在互联网开发公司中&#xff0c;往往伴随着业务的快速迭代&#xff0c;程序员可能没有过多的时间去思考技术扩展的相关问题&#xff0c;长久下来导致技术过于单一。为此最近在学习互联网思维&#xff0c;从相对简单的功能开始做总结&#xff0c;比如非常常见的基础数据的…...

视频美颜SDK趋势畅想:未来发展方向与应用场景

当下&#xff0c;视频美颜SDK正不断演进&#xff0c;本文将深入探讨视频美颜SDK的发展趋势&#xff0c;探讨未来可能的方向和广泛的应用场景。 1.深度学习与视频美颜的融合 未来&#xff0c;我们可以期待看到更多基于深度学习算法的视频美颜SDK&#xff0c;为用户提供更高质量…...

C++ const 限定符的全面介绍

C const 限定符的全面介绍 1. const 修饰基本数据类型 定义 const 修饰的基本数据类型变量&#xff0c;值不可改变。 语法 const type variable value;特点 不可变性&#xff0c;增加代码可读性。 作用 定义不可修改的常量。 使用场景 全局常量、配置项。 注意事项…...

Vue 中的 ref 与 reactive:让你的应用更具响应性(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…...

华为云CCE-集群内访问-根据ip访问同个pod

华为云CCE-集群内访问-根据ip访问同个pod 问题描述&#xff1a;架构如下&#xff1a;解决方法&#xff1a; 问题描述&#xff1a; 使用service集群内访问时&#xff0c;由于启用了两个pod&#xff0c;导致请求轮询在两个pod之间&#xff0c;无法返回正确的结果。 架构如下&am…...

Kasada p.js (x-kpsdk-cd、x-kpsdk-ct、integrity)

提供x-kpsdk-cd的API服务 详细请私信~ 可试用~ V:zhzhsgg 一、简述 integrity是通过身份验证Kasada检测机器人流量后获得的一个检测结果&#xff08;数据完整性&#xff09; x-kpsdk-cd 是经过编码计算等等获得。当你得到正确的解决验证码值之后&#xff0c;解码会看到如下图…...

Thinkphp 5框架学习

TP框架主要是通过URL实现操作 http://servername/index.php/模块/控制器/操作/参数/值.. index.php 为入口文件&#xff0c;在 public 目录内的 index.php 文件; 模块在 application 目录下默认有一个 index 目录&#xff0c;这就是一个模块; 而在 index 目录下有一个 contro…...

麒麟云增加计算节点

一、安装基座系统并配置好各项设置 追加的计算节点服务器&#xff0c;安装好系统&#xff0c;把主机名、网络网线&#xff08;网线要和其他网线插的位置一样&#xff09;、hosts这些配置好&#xff0c;在所有节点的/etc/hosts里面添加信息 在控制节点添加/kylincloud/multinod…...

使用Redis进行搜索

文章目录 构建反向索引 构建反向索引 在Begin-End区域编写 tokenize(content) 函数&#xff0c;实现文本标记化的功能&#xff0c;具体参数与要求如下&#xff1a; 方法参数 content 为待标记化的文本&#xff1b; 文本标记的实现&#xff1a;使用正则表达式提取全小写化后的…...

Oracle修改用户密码

文章目录 Oracle修改用户密码Oracle用户锁定常见的两种状态Oracle用户锁定和解锁 Oracle修改用户密码 使用sys或system使用sysdba权限登录&#xff0c;然后执行以下命令修改密码&#xff1a; alter user 用户名 identified by 密码;密码过期导致的锁定&#xff0c;也通过修改…...

​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 描述&#xff1a; 圣诞活动预…...

Vue解决跨域问错误:has been blocked by CORS policy 后端跨域配置

解决跨域问题后端跨域配置代码&#xff1a; /*** 作者 hua* 描述 跨域配置*/ Configuration public class WebConfiguration implements WebMvcConfigurer {/*** 跨域配置对象* return CorsConfiguration对象*/private CorsConfiguration corsConfig() {CorsConfiguration cor…...

【谷歌云】注册谷歌云 创建Compute Engine

文章目录 一、Google Cloud注册1.1 账号信息1.2 付款信息验证1.3 验证成功 二、Compute Engine创建2.1 启动Compute Engine API2.2 创建实例2.3 新建虚拟机实例2.4 等待实例创建完成2.5 查看虚拟机配置信息2.6 创建防火墙规则2.7 SSH远程连接虚拟机 三、参考链接 一、Google Cl…...

面试数据库八股文五问五答第四期

面试数据库八股文五问五答第四期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1&#xff09;什么情况下 mysql 会索引失效&#xff1f; 不使用索引列进行查询&a…...

2023 年中国金融级分布式数据库市场报告:TiDB 位列领导者梯队,创新能力与增长指数表现突出

近日&#xff0c;沙利文联合头豹研究院发布了中国数据库系列报告之《2023 年中国金融级分布式数据库市场报告》。 报告认为&#xff0c;金融行业对于分布式数据库信任度与认可度正在逐步提高&#xff0c;中国金融级分布式数据库市场正处于成熟落地的高增长阶段&#xff0c;行业…...

基于ExoPlayer的缓存方案实现

音视频APP 的一个必备功能就是在播放的时候会持续缓存完整个音频,同时进度条会更新缓存进度。但是目前Google推出的播放器ExoPlayer本身并没有提供什么方便的接口去实现这个功能,因此大多数的开发者可能会使用AndroidVideoCache 开源库来实现缓存。 AndroidVideoCache 的原理…...

前缀和算法 -- 寻找数组的中心坐标

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 本题链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 输入描述 给定一个数组&#xff0c;接口为int pivotIndex(vector<int>& nums) 输出描述 我们以示例1为例画图解释&#xf…...

autograd与逻辑回归

一、autograd—自动求导系统 torch.autograd.backward() torch.autograd.backward()是PyTorch中用于计算梯度的函数。以下是对该函数的参数的解释&#xff1a; 功能&#xff1a;自动求取梯度 • tensors: 用于求导的张量&#xff0c;如 loss • retain_graph : 保存计算图 •…...

Xshell 从github克隆项目:使用ssh方式。

接上文&#xff1a; https://blog.csdn.net/liu834189447/article/details/135247868 是能克隆项目了&#xff0c;但是速度太磕碜了&#xff0c;磕碜到难以直视。 找到另外一种办法&#xff0c;使用SSH克隆项目 速度嘎嘎猛。 首先得能进得去github网站&#xff0c;不能点上边…...

C++:通过erase删除map的键值对

map是经常使用的数据结构,erase可以删除map中的键值对。 可以通过以下几种方式使用erase 1.通过迭代器进行删除 #include <iostream> #include <map> #include <string> using namespace std;void pMap(const string& w, const auto& m) {cout&l…...

华为月薪25K的自动化测试工程师到底要会那些技能!

​前言 3年自动化测试软件测试工程师职业生涯中&#xff0c;我所经历过的项目都是以自动化测试为主的。由于自动化测试是一个广泛的领域&#xff0c;我将自己的经验整理了一下分享给大家&#xff0c;话不多说&#xff0c;直接上干货。 自动化测试的目标和实践选择合适的自动化…...

diffusers 源码待理解之处

一、训练DreamBooth时&#xff0c;相关代码的细节小计 ** class_labels timesteps 时&#xff0c;模型的前向传播怎么走&#xff1f;待深入去看 ** 利用class_prompt去生成数据&#xff0c;而不是instance_prompt class DreamBoothDataset(Dataset):"""A dat…...

正则表达式 详解,10分钟学会

大家好&#xff0c;欢迎来到停止重构的频道。 本期我们讨论正则表达式。 正则表达式是一种用于匹配和操作文本的工具&#xff0c;常用于文本查找、文本替换、校验文本格式等场景。 正则表达式不仅是写代码时才会使用&#xff0c;在平常使用的很多文本编辑软件&#xff0c;都…...

【排序算法】归并排序与快速排序:深入解析与比较

文章目录 1. 引言2. 归并排序&#xff08;Merge Sort&#xff09;3. 快速排序&#xff08;Quick Sort&#xff09;4. 归并排序与快速排序的比较5. 结论 1. 引言 排序算法是计算机科学中最基本且至关重要的概念之一。它们不仅是理解更复杂算法和数据结构的基石&#xff0c;而且…...

万字长文谈自动驾驶bev感知(一)

文章目录 prologuepaper listcamera bev :1. Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D2. M2BEV: Multi-Camera Joint 3D Detection and Segmentation with Unified Birds-Eye View Representation3. BEVDet: High-Pe…...

cfa一级考生复习经验分享系列(十七)

考场经验&#xff1a; 1.本人在Prometric广州考试中心&#xff0c;提前一天在附近住下&#xff0c;地方比较好找&#xff0c;到了百汇广场北门&#xff0c;进去就可以看见电梯直达10楼。进去之后需要现场检查行程卡和健康码&#xff0c;然后会问最近你有没有发烧咳嗽等问题&…...

机器人活动区域 - 华为OD统一考试

OD统一考试 题解: Java / Python / C++ 题目描述 现有一个机器人,可放置于 M x N 的网格中任意位置,每个网格包含一个非负整数编号,当相邻网格的数字编号差值的绝对值小于等于 1 时机器人可以在网格间移动。 问题: 求机器人可活动的最大范围对应的网格点数目。 说明: 网格…...

三、HTML元素

一、HTML元素 HTML 文档由 HTML 元素定义。 *开始标签常被称为起始标签&#xff08;opening tag&#xff09;&#xff0c;结束标签常称为闭合标签&#xff08;closing tag&#xff09;。 二、HTML 元素语法 HTML 元素以开始标签起始。HTML 元素以结束标签终止。元素的内容是…...

置顶> 个人学习记录一览

个人学习记录一览表 写个说明   知识学的好&#xff0c;不如笔记记得好&#xff0c;知识点的遗忘在所难免&#xff0c;这里记录我个人的学习过程&#xff0c;以备后面二次学习使用。 Linux 操作系统 Linux 操作系统 001-介绍 Linux 操作系统 002-VMware Workstation的相关操…...

c++重载操作符

支持重载操作符是c的一个特性&#xff0c;先不管好不好用&#xff0c;这起码能让它看起来比其他语言NB很多&#xff0c;但真正了解重载操作符后&#xff0c;就会发现这个特性...就这&#xff1f;本文分两个部分 重载操作符简介和使用——适用新手重载操作符的原理和sao操作——…...

营销型高端网站建设价格/百度竞价冷门产品

欢迎关注”生信修炼手册”!对于任意的表达量数据&#xff0c;定量加差异分析都是一套经典的组合拳。当我们想要展示特定基因的组间差异结果时&#xff0c;下面这种图表就派上了用场横坐标为基因&#xff0c;纵坐标是基因表达量&#xff0c;每一组的表达量采用了箱体图的形式来展…...

我想找个郑州做网站的/免费域名注册

一、源码安装 1、下载nginx源码包 curl -O https://nginx.org/download/nginx-1.12.2.tar.gz 2、安装nginx所需的依赖&#xff1a; zip模块需要 zlib 库 ​ rewrite模块需要 pcre 库 ​ ssl 功能需要openssl库 yum -y install gcc* pcre pcre-devel zlib zlib-devel o…...

余姚建设公司网站/关键字c语言

项目地址 github: https://github.com/Delgan/loguru 文档&#xff1a;https://loguru.readthedocs.io/en/stable/index.html 安装 pip install loguru 1、输出日志 from loguru import logger logger.debug("这是一条debug日志") 终端执行后出现带颜色的日志&am…...

做外贸需要用什么网站/seo收费还是免费

第四章 Generating DocumentationVP-UML提供了几种产生项目报告的工具。ReportWriter通过跟踪和同步你的项目来简化你的工作。它可以输出HTML或PDF格式的项目报告&#xff0c;还自带了精致的打印工具来打印你建立的视图。在本章中&#xff0c;主要有以下内容&#xff1a;&#…...

做门户网站cms/二级域名免费分发

1. 一点感言 突然就开始了这个角色的扮演&#xff1a;来跟大家介绍作为一个测试人员的角色定位&#xff0c;以及刚入门需要了解的相关知识和心态方面的问题。说实话&#xff0c;感觉到很为难&#xff0c;有时候有些事情做起来感觉并不是很难&#xff0c;但是要把它转化为文字的…...

济南网站建设要多少钱/潍坊网站建设平台

线-小箭头&#xff0c;中间的线区分&#xff1a;->关联 &#xff1a;类中使用了对方的指针&#xff0c;或者引用--->依赖 &#xff1a;函数参数中使用了对方&#xff0c;指针方式或者引用方式&#xff0c;可以相互依赖。菱形-线-小箭头&#xff0c;开始端的菱形区分&…...