【Mysql多数据源实现读写分离的几种方案】

news2024/11/14 23:38:27

文章目录

    • 一.什么是MySQL 读写分离
    • 二.读写分离的几种实现方式(手动控制)
      • 1.基于Spring下的AbstractRoutingDataSource
        • 1.yml
        • 2.Controller
        • 3.Service实现
        • 4.Mapper层
        • 5.定义多数据源
        • 6.继承Spring的抽象路由数据源抽象类,重写相关逻辑
        • 7. 自定义注解@WR,用于指定当前操作使用哪个库
        • 8. 切面逻辑
        • 9.源码简单分析
        • 10. 开始测试
      • 2.基于Mybatis的SqlSessionFactory
        • 1.yml
        • 2.Controller
        • 3.Service实现
        • 4.Mapper层
        • 5.配置类
          • 1. 指定哪些Mapper接口使用读数据源:
          • 2. 指定哪些Mapper接口使用写数据源
        • 6. 开始测试
      • 3.基于baomidou动态数据源实现读写分离(最简单)
        • 1. maven依赖
        • 2.yml
        • 3.Controller
        • 4.Service
        • 5.Mapper层
        • 6.开始测试
    • 三.小结

一.什么是MySQL 读写分离

我记得实习的第一家公司,就用过mysql多数据源的读写分离方案,但那时候是我同事弄的,完全不懂怎么实现的,觉得他好厉害。从此成了心里的一道坎,很久之前就了解了,一直想着要写篇博客记录下,ok,那赶紧开始吧~
先了解下概念什么是读写分离、优势、实现方式、注意事项、和使用场景。如果项目里面有用到数据库集群,开始有性能方面问题,结合业务场景及综合衡量下去考虑是否适用数据库读写分离方案。
以下解释来自chatgpt,我觉得说的没问题

MySQL 读写分离是一种数据库优化策略,通过将数据库的读操作和写操作分开,分别交由不同的数据库实例处理,以提高系统的性能和扩展性。具体来说,读写分离通常涉及一个主数据库(Master)和一个或多个从数据库(Slave),它们通过复制机制保持数据的一致性。
以下是读写分离的核心概念:

  1. 主从复制(Master-Slave Replication)
  • 主库(Master):负责处理所有的写操作(INSERT、UPDATE、DELETE 等),也可以处理读操作。
  • 从库(Slave):主要用于处理读操作(SELECT),不会直接接收写操作。从库通过复制机制从主库同步数据,确保数据一致性。
  1. 读写分离的优势
  • 提高读性能:由于从库处理读操作,可以通过增加从库实例来扩展系统的读性能,减轻主库的负担。
    减少主库压力:写操作集中在主库,从库处理大部分的读操作,主库的压力减少,有助于提高写操作的响应速度。
    容错性:在某些情况下,从库可以用作备份,如果主库出现故障,可以临时将从库提升为主库以保持服务的可用性。
  1. 实现方式
    读写分离可以通过多种方式实现,包括:
  • 手动分离:应用程序通过逻辑代码,手动决定读请求发送到从库,写请求发送到主库。
  • 代理层(中间件):使用数据库中间件(如 MySQL Proxy、MaxScale、MyCat等),在应用和数据库之间自动实现读写分离和负载均衡。
  • 连接池支持:某些数据库连接池(如 Druid、HikariCP)可以自动支持主从库的读写分离。
  1. 注意事项
  • 数据一致性问题:由于复制存在延迟,从库上的数据可能会比主库滞后。如果应用程序对实时数据一致性要求较高,需谨慎处理。
  • 负载均衡:要合理分配读请求到不同的从库,避免单个从库成为瓶颈。
  • 主库故障恢复:需要设计可靠的故障转移机制,确保主库出现问题时,从库能够及时接管。
  1. 使用场景
    读写分离适用于读操作远多于写操作的场景,例如电商平台、社交媒体网站等。在这些场景中,读请求往往占大多数,通过读写分离可以有效提升系统的扩展性和性能。

二.读写分离的几种实现方式(手动控制)

这里只介绍手动分离读写库:应用程序通过逻辑代码,手动决定读请求发送到从库,写请求发送到主库的几种实现方式。

1.基于Spring下的AbstractRoutingDataSource

根据大家平常开发习惯,我还是从controller层开始吧。

1.yml

我的yml配置如下:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    datasource1:
      url: jdbc:mysql://127.0.0.1:3306/tl_mall_master?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
      username: root
      password: 123456
      initial-size: 1
      min-idle: 1
      max-active: 20
      test-on-borrow: true
      driver-class-name: com.mysql.cj.jdbc.Driver
    datasource2:
      url: jdbc:mysql://127.0.0.1:3306/tl_mall_slave?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
      username: root
      password: 123456
      initial-size: 1
      min-idle: 1
      max-active: 20
      test-on-borrow: true
      driver-class-name: com.mysql.cj.jdbc.Driver
2.Controller
@RestController
@RequestMapping("friend")
@Slf4j
public class FriendController {

    @Autowired
    private FriendService friendService;

    @GetMapping(value = "select")
    public List<Friend> select(){
        return friendService.list();
    }


    @GetMapping(value = "insert")
    public String in(){
        Friend friend = new Friend();
        friend.setName("jinbiao666");
        friendService.save(friend);
        return "主库插入成功";
    }
}
3.Service实现
@Service
public class FriendImplService implements FriendService {

    @Autowired
    FriendMapper friendMapper;

    @Override
    @WR("R")        // 库2
    public List<Friend> list() {
        return friendMapper.list();
    }

    @Override
    @WR("W")        // 库1
    public void save(Friend friend) {
        friendMapper.save(friend);
    }
}
4.Mapper层
public interface FriendMapper {
    @Select("SELECT * FROM friend")
    List<Friend> list();

    @Insert("INSERT INTO  friend(`name`) VALUES (#{name})")
    void save(Friend friend);
}
5.定义多数据源
@Configuration
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource1")
    public DataSource dataSource1() {
        // 底层会自动拿到spring.datasource中的配置, 创建一个DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource2")
    public DataSource dataSource2() {
        // 底层会自动拿到spring.datasource中的配置, 创建一个DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }
}
6.继承Spring的抽象路由数据源抽象类,重写相关逻辑
  1. 继承 org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource抽象类。
  2. 重写determineCurrentLookupKey方法,设置当前db操作应使用的数据源key
  3. 重写afterPropertiesSet方法,设置多数据源和默认数据源。
@Component
@Primary
public class DynamicDataSource extends AbstractRoutingDataSource {
    
    /**
     *  通过ThreadLocal设置当前线程所使用的数据源key
     */
    public static ThreadLocal<String> name = new ThreadLocal<>();

    // 写
    @Autowired
    DataSource dataSource1;
    
    // 读
    @Autowired
    DataSource dataSource2;
    
    // 返回当前数据源标识,根据返回的key决定最终使用的数据源
    @Override
    protected Object determineCurrentLookupKey() {
        return name.get();

    }

    /**
     * InitializingBean 是 Spring 框架中的一个接口,用于在 Bean 初始化完成后执行特定的操作。它定义了一个方法 afterPropertiesSet(),当 Bean 的属性设置完成后会被调用。
     * Spring 容器会在实例化该 Bean 并设置完属性后,自动调用 afterPropertiesSet() 方法来执行一些初始化操作
     */
    @Override
    public void afterPropertiesSet() {

        // 为targetDataSources初始化所有数据源
        Map<Object, Object> targetDataSources=new HashMap<>();
        targetDataSources.put("W",dataSource1);
        targetDataSources.put("R",dataSource2);

        super.setTargetDataSources(targetDataSources);

        // 为defaultTargetDataSource 设置默认的数据源
        super.setDefaultTargetDataSource(dataSource1);

        super.afterPropertiesSet();
    }
}
7. 自定义注解@WR,用于指定当前操作使用哪个库
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WR {
    String value() default "W";
}
8. 切面逻辑
@Component
@Aspect
public class DynamicDataSourceAspect implements Ordered {
    // 前置
    @Before("within(com.tuling.dynamic.datasource.service.impl.*) && @annotation(wr)")
    public void before(JoinPoint point, WR wr){
        // 设置数据源key为注解值(determineCurrentLookupKey()方法里面会去取这个key)
        DynamicDataSource.name.set(wr.value());
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
9.源码简单分析

简单看下AbstractRoutingDataSource里面的determineTargetDataSource决定目标数据源方法。
在这里插入图片描述

10. 开始测试
  1. 给master写库的friend表清空,写入写库
    在这里插入图片描述

  2. 写库写入成功
    在这里插入图片描述

  3. 给slave读库的friend表插入一条数据rise,仅查询到读库的内容,成功实现读写分离。
    在这里插入图片描述

2.基于Mybatis的SqlSessionFactory

一样还是从yml开始吧,目录结果清晰些。

1.yml
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    datasource1:
      url: jdbc:mysql://127.0.0.1:3306/tl_mall_master?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
      username: root
      password: 123456
      initial-size: 1
      min-idle: 1
      max-active: 20
      test-on-borrow: true
      driver-class-name: com.mysql.cj.jdbc.Driver
    datasource2:
      url: jdbc:mysql://127.0.0.1:3306/tl_mall_slave?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
      username: root
      password: 123456
      initial-size: 1
      min-idle: 1
      max-active: 20
      test-on-borrow: true
      driver-class-name: com.mysql.cj.jdbc.Driver
server:
  port: 8080
2.Controller
@RestController
@RequestMapping("friend")
@Slf4j
public class FriendController {

    @Autowired
    private FriendService friendService;

    @GetMapping(value = "select")
    public List<Friend> select(){
        return friendService.select();
    }


    @GetMapping(value = "insert")
    public void insert(){
        Friend friend = new Friend();
        friend.setName("jinbiao666");
        friendService.insert(friend);
    }
}
3.Service实现
/***
 * 读数据源配置:
 * 1. 指定扫描的mapper接口包(从库)
 * 2. 指定使用sqlSessionFactory是哪个(从库)
 */
@Service
public class FriendImplService implements FriendService {

    @Autowired
    private RFriendMapper rFriendMapper;

    @Autowired
    private WFriendMapper wFriendMapper;

    // 读-- 读库
    @Override
    public List<Friend> select() {
        return rFriendMapper.select();
    }

    // 保存-- 写库
    @Override
    public void insert(Friend friend) {
        wFriendMapper.insert(friend);
    }
 }
4.Mapper层

在mapper层做的读写区分。

public interface RFriendMapper {

    @Select("SELECT * FROM friend")
    List<Friend> select();

    @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 insert(Friend friend);
}
5.配置类
1. 指定哪些Mapper接口使用读数据源:
  • 通过@MapperScan注解扫对应的mapper接口,然后设置数据源为从数据源构造一个SqlSessionFactory 对象。
  • 事务管理器用作事务回滚,暂不测试事务回滚了,都是可成功的.
/***
 * 写数据源配置:
 * 1. 指定扫描的mapper接口包(主库)
 * 2. 指定使用sqlSessionFactory是哪个(主库)
 */
@Configuration
@MapperScan(basePackages = "com.tuling.datasource.dynamic.mybatis.mapper.r", sqlSessionFactoryRef="rSqlSessionFactory")
public class RMyBatisConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource2")
    public DataSource dataSource2() {
        // 底层会自动拿到spring.datasource中的配置, 创建一个DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public SqlSessionFactory rSqlSessionFactory() throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        // 指定主库
        sessionFactory.setDataSource(dataSource2());
        // 指定主库对应的mapper.xml文件
        /*sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/r/*.xml"));*/
        return sessionFactory.getObject();
    }

    @Bean
    public DataSourceTransactionManager rTransactionManager(){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource2());
        return dataSourceTransactionManager;
    }

    @Bean
    public TransactionTemplate rTransactionTemplate(){
        return new TransactionTemplate(rTransactionManager());
    }
}
2. 指定哪些Mapper接口使用写数据源
@Configuration
@MapperScan(basePackages = "com.tuling.datasource.dynamic.mybatis.mapper.w", sqlSessionFactoryRef="wSqlSessionFactory")
public class WMyBatisConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource1")
    public DataSource dataSource1() {
        // 底层会自动拿到spring.datasource中的配置, 创建一个DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public SqlSessionFactory wSqlSessionFactory() throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        // 指定主库
        sessionFactory.setDataSource(dataSource1());
        // 指定主库对应的mapper.xml文件
        /*sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/order/*.xml"));*/
        return sessionFactory.getObject();
    }

    @Bean
    @Primary
    public DataSourceTransactionManager wTransactionManager(){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource1());
        return dataSourceTransactionManager;
    }


    @Bean
    public TransactionTemplate wTransactionTemplate(){
        return new TransactionTemplate(wTransactionManager());
    }
}
6. 开始测试

1.写写库,成功,之前只有1条现在2条。ok,基于Mybatis在mapper层面的读写分离也成功了
在这里插入图片描述
2.读读库
在这里插入图片描述
其他场景比如写库失败回滚都是可以的,因为我们给DataSourceTransactionManager注入了写库的数据源。这里不展示了。

3.基于baomidou动态数据源实现读写分离(最简单)

1. maven依赖
  <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.0</version>
 </dependency>
2.yml

一主两从

spring:
  datasource:
    dynamic:
      #设置默认的数据源或者数据源组,默认值即为master
      primary: master
      #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: false
      datasource:
        master:
          url: jdbc:mysql://127.0.0.1:3306/tl_mall_master?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
          username: root
          password: 123456
          initial-size: 1
          min-idle: 1
          max-active: 20
          test-on-borrow: true
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave_1:
          url: jdbc:mysql://127.0.0.1:3306/tl_mall_slave?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
          username: root
          password: 123456
          initial-size: 1
          min-idle: 1
          max-active: 20
          test-on-borrow: true
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave_2:
          url: jdbc:mysql://127.0.0.1:3306/tl_mall_user?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
          username: root
          password: 123456
          initial-size: 1
          min-idle: 1
          max-active: 20
          test-on-borrow: true
          driver-class-name: com.mysql.cj.jdbc.Driver
server:
  port: 8080
3.Controller
@RestController
@RequestMapping("frend")
@Slf4j
public class FriendController {

    @Autowired
    private FriendService friendService;

    @GetMapping(value = "select")
    public List<Friend> select(){
        return friendService.select();
    }

    @GetMapping(value = "insert")
    public void insert(){
        Friend friend = new Friend();
        friend.setName("jinbiao666");
        friendService.insert(friend);
    }
}   
4.Service
@Service
public class FriendImplService implements FriendService {

    @Autowired
    FriendMapper friendMapper;

    @Override
    @DS("slave2")  // 从库2
    public List<Friend> select() {
        return friendMapper.select();
    }

    @Override
    @DS("master")  // 主库
    //@DS("#session.userID")  基于session里面的用户id取数据源,sass化,数据源动态根据用户选择。
    @DSTransactional   //开启事务操作
    public void insert(Friend friend) {
        friendMapper.insert(friend);
    }
}
5.Mapper层
public interface FriendMapper {

    @Select("SELECT * FROM friend")
    List<Friend> select();

    @Insert("INSERT INTO  friend(`name`) VALUES (#{name})")
    void insert(Friend friend);
}
6.开始测试

使用是不是超级简单,省去了很多自己注入的步骤,如使用@DS注解选择数据源、@DSTransactional注解回滚对应的数据源事务等等都由baomidou帮我们实现了。

  1. 写写库,成功,之前只有2条现在3条。ok,基于baomidou动态数据源实现读写分离也成功了
    在这里插入图片描述
  2. 读slave_2(tl_mall_user),可以看到数据库3条数据:
    在这里插入图片描述
    接口测试:查询从库slave_2,没问题, 事务回滚暂不在这里做测试了,替大家测过了的,没问题~
    在这里插入图片描述

三.小结

  • 经过上面3种方式介绍,多数据源读写分离是不是很简单。
  • 不过上面都是对单数据源操作的,回滚使用@Transactional或者@DSTransactional帮我们可以回滚单数据源的事务。
  • 如果设计到多数据源的写入需要统一提交回滚怎么实现呢?小伙伴们不妨也思考一下这个问题,这其实就是相当于是分布式事务的回滚了。

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

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

相关文章

客户端数JSON据库SQL操作功能实现代码-———未来之窗行业应用跨平台架构

一、前端json结构化查询优点 以下是前端本地化查询的一些优点&#xff1a; 1. 快速响应&#xff1a;无需通过网络请求从服务器获取数据&#xff0c;查询结果能够立即返回&#xff0c;提供了几乎零延迟的用户体验&#xff0c;使应用更加流畅和响应迅速。 2. 离线可用性&#x…

9.4 Linux_I/O_访问目录、文件属性

访问目录 1、打开关闭目录 打开目录函数声明如下&#xff1a; //1.直接打开指定路径的目录文件 DIR *opendir(const char *name); //2.先用open打开目录文件&#xff0c;再用该函数访问目录文件 DIR *fdopendir(int fd); 返回值&#xff1a;成功返回指向打开的目录文件的结…

ELK-05-skywalking监控SpringCloud服务日志

文章目录 前言一、引入依赖二、增加日志配置文件三、打印日志四、skywalking网页查询链路五、日志收集5.1 修改logback-spring.xml5.2 重启SpringCloud服务并请求test接口5.3 查看skywalking网页的Log 总结 前言 基于上一章节&#xff0c;现在使用skywalkin监控SpringCloud服务…

JWT令牌技术介绍及使用

一、JWT介绍 JWT是JSON Web Token的缩写&#xff0c;即JSON Web令牌&#xff0c;是一种自包含令牌。 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。 JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息&#xff0c;以便于从资源服务…

D. Determine Winning Islands in Race (cf div2,dp、图论最短路)

D. Determine Winning Islands in Race 思路: bfs找到E到达每个点的最短时间t[i]。 如果E要超过B&#xff0c;那么一定要借助辅助桥&#xff0c;从而获胜。 假设有u->v的辅助桥&#xff0c;E能通过这个桥超过B的条件是: s>u 且 t[v] < v-s 即 s的取值要为[u1,v-t[v]-…

C++核心编程和桌面应用开发 第七天(运算符重载 智能指针)

目录 1.数组类 2.运算符重载 2.1加号运算符 2.1.1成员函数实现 2.1.2全局函数实现 2.1.3加号重载 2.2左移运算符 2.3递增运算符 2.4指针运算符 2.5赋值运算符 1.数组类 //默认构造函数 MyArray::MyArray() {m_Size 0;m_Capacity 100;pAddress new int[m_Capacity]…

【有啥问啥】深度解析迁移学习(Transfer Learning)

深度解析迁移学习&#xff08;Transfer Learning&#xff09; 在机器学习领域&#xff0c;迁移学习&#xff08;Transfer Learning&#xff09;作为一种强大的技术&#xff0c;正广泛应用于各种实际场景中。本文将详细解析迁移学习的基本概念、原理、分类、应用场景以及具体实…

vue3中storeToRefs让store中的结构出来的数据也能变成响应式

1、首先需要安装pinia 具体安装和使用教程参考 2、创建 src/stores/counter.js 文件&#xff0c;其内容如下&#xff1a; import {defineStore} from "pinia"; import {ref} from "vue";export const useCounterStore defineStore(counter,()>{const…

C语言程序设计题目十九:编写一万年历系统

文章目录 题目十九&#xff1a;编写一万年历系统calendar.hcalendar.ctest.c 题目十九&#xff1a;编写一万年历系统 要求&#xff1a; 模仿现实中的挂历&#xff0c;显示当前月的每一天及星期几&#xff0c;当系统日期变为下一个月时&#xff0c;自动翻页到下一个月。 calend…

【第3期】INFINI Easysearch 免费认证培训开放报名

探索 Easysearch 的无限可能&#xff0c;与 INFINI Labs 共赴搜索技术前沿&#xff01; 随着数字化转型的加速&#xff0c;搜索技术已成为企业数据洞察的核心。INFINI Labs 作为搜索创新技术的引领者&#xff0c;诚邀所有对 Easysearch 搜索引擎感兴趣的开发者、技术爱好者及合…

安卓13禁止待机 永不休眠 android13永不休眠

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.彩蛋1.前言 设置 =》显示 =》屏幕超时 =》 永不。 我们通过修改系统待机时间配置,来达到设置屏幕超时的配置。像网上好多文章都只写了在哪里改,改什么东西,但是实际上并未写明为什么要改那…

[智能控制】【第2 章 模糊控制的理论基础】

目录 第2章 模糊控制的理论基础 2.1 概述——模糊控制的提出 2.1 概述——模糊控制的特点 2.2 模糊集合 2.2.1 模糊集合 1 特征函数和隶属函数 2 模糊集合的表示 例2.1 例2.2 2.2.2 模糊集合的运算 1 模糊集合的基本运算 &#xff08;1&#xff09;空集…

docker搭建clickhouse并初始化用户名密码

1、新建挂载目录 mkdir -p /home/clickhouse-server/ mkdir -p /home/clickhouse-server/etc/2、拉取镜像 docker pull clickhouse/clickhouse-server3、创建临时容器 docker run -d --rm --name clickhouse-server --ulimit nofile262144:262144 clickhouse/clickhouse-ser…

目标检测流程

流程概述 背景&#xff1a;梳理目标检测标注&#xff0c;训练&#xff0c;部署全流程。供后续初学者快速上手 方案&#xff1a;Ubuntu&#xff08;PC端&#xff09;进行标注&#xff0c;基于OpenMMLab进行训练得到pt模型&#xff0c;pt模型通过转换rknn并部署。 1. 数据集 公…

成都睿明智科技有限公司可靠吗?

在这个短视频风靡的时代&#xff0c;抖音已不仅仅是一个娱乐平台&#xff0c;它更是无数商家眼中的蓝海市场&#xff0c;是电商领域的新宠儿。在这场流量与转化的盛宴中&#xff0c;成都睿明智科技有限公司以其敏锐的市场洞察力和专业的服务能力&#xff0c;正逐步成为抖音电商…

使用three.js 实现着色器草地的效果

使用three.js 实现着色器草地的效果 在线预览https://z2586300277.github.io/three-cesium-examples/#/codeMirror?navigationThreeJS&classifyshader&idgrassShader 在 https://threehub.cn 中还有很多案例 import * as THREE from three import { OrbitControls…

Java基础(Arrays工具类)(asList()方法)(详细)

目录 一、Arrays工具类 &#xff08;1&#xff09;引言 &#xff08;2&#xff09;基本介绍 &#xff08;3&#xff09;主要功能&#xff08;提供的方法&#xff09; &#xff08;I&#xff09;排序&#xff08;Arrays.sort()&#xff09; &#xff08;II&#xff09;搜索(查找…

IDEA自动清理类中未使用的import包

目录 1.建议清理包的理由 2.清理未使用包的方式 2.1 手动快捷键清理 2.2 设置自动清理 1.建议清理包的理由 有时候项目类文件中会有很多包被引入了&#xff0c;但是并没有被使用&#xff0c;这会增加项目的编译时间并且代码可读性也会变差。在开发过程中&#xff0c;建议设…

ISP去噪(3)_图像的高频分量和低频分量

#图像分析# 总是不好确认头发和毛绒玩具到底是低频还是高频分量。现在得出结论&#xff0c;头发和毛绒玩具都是高频信息&#xff0c;因为细节很多。 目录 图像的频率 (1) 什么是低频? (2) 什么是高频? &#xff08;3&#xff09;低频和高频对比 &#xff08;4&#xf…

谷歌今天发布了两款升级版Gemini模型:Gemini-1.5-Pro-002和Gemini-1.5-Flash-002

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…