MySQL主从数据库(主读从写)

news2024/10/6 20:37:36

MySQL多数据源

  • MySQL主从数据库(主读从写)
    • 1. 多数据源的实现原理
      • 1.1 配置多数据源yml文件
      • 1.2 创建配置类
      • 1.3 动态切换数据源类继承AbstractRoutingDataSource 类
      • 1.4 测试类测试
      • 1.5 附枚举代码
      • 1.6 总结
    • 2. 多数据源切换方式(优化)
      • 2.1 MyBatis插件(读写分离)
        • 2.1.1 创建插件实现数据源的动态切换
        • 2.1.2 在配置类中添加数据源动态切换的插件bean对象
      • 2.2 AOP方式切换数据源(业务复杂数据量大)
        • 2.2.1 使用AOP首先需要程序的核心启动类上添加注解
        • 2.2.2 自定义注解
        • 2.2.3 切面类
        • 2.2.4 使用注解

MySQL主从数据库(主读从写)

带薪学习:本文主要介绍主从数据库,在SpringBoot项目中我们需要连接多个数据源,多个数据库可能存在不同的服务上边,比如张三对MySQLA数据库只有读取数据的权限,对MySQLB数据库只有写数据的权限。这样的话一个项目中读取数据就要配置A的数据源,写入数据要配置B的数据源,这样就构成了多数据源切换问题。

1. 多数据源的实现原理

对于大多数的Java应用,都使用了spring架,spring-jdbc模块提供了AbstractRoutingDataSource,其内部可以包含了多个DataSoure,,然后在运行时来动态切换的访问数据库。这种方式对访问数据库的架构图如下所示:

在这里插入图片描述

应用直接操作的是AbstractRoutingDataSource的实现类,告诉AbstractRoutingDataSource访问哪个数据库,然后由AbstractRoutingDataSource从事先配置好的数据源(dbs1、dbs2)选择一个,来访问对应的数据库。

在这里插入图片描述

  1. 配置多数据源和AbstractRoutingDataSource的自定义实现类:DynamicDataSource

1.1 配置多数据源yml文件

application.yaml


spring:
  datasource:
    #主数据源
    master:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://----:7788/guoguo?characterEncoding=utf-8&serviceTimezone=UTC
      username: root
      password: root
      type: com.alibaba.druid.pool.DruidDataSource
      initialSize: 10
      minIdle: 10
      maxActive: 30
    #从数据库
    slave:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://-----.mysql.rds.aliyuncs.com:3306/platform_crawler?characterEncoding=utf-8&serviceTimezone=UTC
      username: -----
      password: -----
      type: com.alibaba.druid.pool.DruidDataSource
      initialSize: 10
      minIdle: 10
      maxActive: 30


    masterslave:
      # 读写分离配置
      load-balance-algorithm-type: round_robin
      # 最终的数据源名称
      name: dataSource
      # 主库数据源名称
      master-data-source-name: master
      # 从库数据源名称列表,多个逗号分隔
      slave-data-source-names: slave
    props:
      # 开启SQL显示,默认false
      sql:
        show: true

1.2 创建配置类

DataSourceConfig.java

package com.guo.mysql.config;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Slf4j
@Configuration
public class DataSourceConfig {

    /**
     * 主数据源
     * @return DataSource
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        //底层自动拿到Spring.datasource中的配置,创建一个DruidDatasource
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 从 数据源
     * @return DataSource
     */
    @Bean  //
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        //底层自动拿到Spring.datasource中的配置,创建一个DruidDatasource
        return DruidDataSourceBuilder.create().build();
    }
}

1.3 动态切换数据源类继承AbstractRoutingDataSource 类

DynamicDataSource.java

package com.guo.mysql;

import com.guo.mysql.enums.DataSourceEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Component
@Primary   //将该Bean设置为主要注入Bean  implements DataSource, InitializingBean
public class DynamicDataSource extends AbstractRoutingDataSource {
    //当前使用的数据源标识
    public static ThreadLocal<String> name = new ThreadLocal<>();

    //主写
    @Autowired
    DataSource masterDataSource;

    //从读
    @Autowired
    DataSource slaveDataSource;

    //返回当前数据源标识
    @Override
    public Object determineCurrentLookupKey() {
        return name.get();
    }

    /**
     * spring容器启动的时候被调用
     */
    @Override
    public void afterPropertiesSet() {
        //为targetDataSources初始化所有数据源
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceEnum.主库.getName(), masterDataSource);
        targetDataSources.put(DataSourceEnum.从库.getName(), slaveDataSource);
        super.setTargetDataSources(targetDataSources);
        //为defaultTargetDataSource设置默认的数据源
        super.setDefaultTargetDataSource(slaveDataSource);   //从读
        super.afterPropertiesSet();
    }
}

1.4 测试类测试

SpringbootSynchronizeMysqlApplicationTests.java
测试类,根据指定数据库名称,切换数据源连接,进行操作不同的数据库

package com.guo.mysql;

import com.guo.mysql.enums.DataSourceEnum;
import com.guo.mysql.service.UserOsskeyService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootSynchronizeMysqlApplicationTests {

    @Test
    void contextLoads() {
    }

    @Autowired
    private UserOsskeyService userOsskeyService;
    @Test
    public void test(){
        DynamicDataSource.name.set(DataSourceEnum.从库.getName());
        System.out.println(userOsskeyService.count());


        DynamicDataSource.name.set(DataSourceEnum.主库.getName());
        System.out.println(userOsskeyService.count());
    }
}

输出:

2022-11-15 11:03:11.165  INFO 11692 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
569548
2022-11-15 11:03:20.525  INFO 11692 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} inited
1

1.5 附枚举代码

DataSourceEnum.java
为了提高代码的可用性,在代码中切换数据源按照名称切换,这里把名称写在枚举中,后续如有修改直接在枚举类中修改就可以了,不需要去每个类中去修改,提高代码的可用性

package com.guo.mysql.enums;

import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;

public enum DataSourceEnum {
    主库("master", "w"),
    从库("slave", "r"),
    NULL("-1", "");

    private String name;
    private String exec;

    DataSourceEnum(String name, String exec) {
        this.name = name;
        this.exec = exec;
    }

    public String getName() {
        return name;
    }

    public String getExec() {
        return exec;
    }

    private static final Map<String, DataSourceEnum> codeMap = new HashMap<>((int) (values().length / .75f) + 1);

    static {
        for(DataSourceEnum item : values()) {
            codeMap.put(item.name, item);
        }
    }

    public static DataSourceEnum fromCode(String name) {
        DataSourceEnum item = codeMap.get(name);
        return item == null ? NULL : item;
    }

    public static Collection<DataSourceEnum> all() {
        EnumSet<DataSourceEnum> enumSet = EnumSet.allOf(DataSourceEnum.class);
        enumSet.remove(NULL);
        return enumSet;
    }
}

1.6 总结

以上代码虽然可以实现基本数据源的切换,还存在一些问题

  1. 数据源表示设置代码耦合性过高,对后期维护造成很大的麻烦

2. 多数据源切换方式(优化)

  1. MyBatis插件(读写分离)
  2. AOP方式切换数据源(业务复杂数据量大)

2.1 MyBatis插件(读写分离)

为了解决数据库的读性能瓶颈(读比写性能更高,写锁会影响读阻塞,从而影响读的性能)
很对数据拥有主从架构,也就是,一台数据库服务器,是对外提供增删改业务的生产服务器;另外一(多)台数据库服务器,主要进行操作。
可以通过中间件(ShardingSphere, mycat,mysql-proxy,TDDL …)

这里的架构上类似。不同的是,在读写分离中,主库和从库的数据库是一致的(不考虑主从延迟)。数据更新操作(insert,update,delete)都是在主库上进行,主库将数据变更信息同步给从库。在查询时,可以在从库上进行,从而分担主库的压力
在这里插入图片描述

2.1.1 创建插件实现数据源的动态切换

DynamicDataSourcePlugin.java

package com.guo.mysql.plugin;

import com.guo.mysql.DynamicDataSource;
import com.guo.mysql.enums.DataSourceEnum;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.Properties;
//Mybatis拦截器注解
//@Intercepts:标识该类是一个拦截器
//@Signature:拦截器相关属性设置
@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 {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //拿到当前方法(update, query)所有参数
        Object[] objects = invocation.getArgs();
        //MappedStatement 封装SQL
        MappedStatement ms = (MappedStatement) objects[0];
        //读方法
        if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)){
            DynamicDataSource.name.set(DataSourceEnum.从库.getName());
        }else {
            //写操作
            DynamicDataSource.name.set(DataSourceEnum.主库.getName());
        }
        // 修改当前线程要选择的数据源的Key
        return invocation.proceed();
    }
    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }
}

2.1.2 在配置类中添加数据源动态切换的插件bean对象

package com.guo.mysql.config;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.guo.mysql.plugin.DynamicDataSourcePlugin;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.plugin.Interceptor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Slf4j
@Configuration
public class DataSourceConfig {

    /**
     * 主数据源
     * @return DataSource
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        //底层自动拿到Spring.datasource中的配置,创建一个DruidDatasource
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 从 数据源
     * @return DataSource
     */
    @Bean  //
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        //底层自动拿到Spring.datasource中的配置,创建一个DruidDatasource
        return DruidDataSourceBuilder.create().build();
    }


    //mybatisplus 注入切换数据源的插件bean对象
    @Bean
    public Interceptor dynamicDataSourcePlugin(){
        return new DynamicDataSourcePlugin();
    }
}

核心代码解读

//拿到当前方法(update, query)所有参数
Object[] objects = invocation.getArgs();
//MappedStatement 封装SQL
MappedStatement ms = (MappedStatement) objects[0];
 //读方法
if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)){
    DynamicDataSource.name.set(DataSourceEnum.从库.getName());
}else {
    //写操作
    DynamicDataSource.name.set(DataSourceEnum.主库.getName());
}

通过学习spring底层源码了解到,当我们操作查询SELECT操作的时候调用从库(从库读取数据),操作添加更新删除操作的时候 INSERT, UPDATE, DELETE,调用我们的主库(主库用来增删改操作);
通过上述判断修改当前线程要选择的数据源的Key

2.2 AOP方式切换数据源(业务复杂数据量大)

数据分布在不同的数据库中,数据库拆了,应用没有拆,一个公司多个子项目,各用各分数据库,涉及数据共享…

不同业务的数据源:一般利用AOP,结合自定义注解动态切换数据源
AOP+自定义注解
在这里插入图片描述

2.2.1 使用AOP首先需要程序的核心启动类上添加注解

@EnableAspectJAutoProxy //启动AOP

package com.guo.mysql;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy  //启动AOP
public class SpringbootSynchronizeMysqlApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootSynchronizeMysqlApplication.class, args);
    }
}

2.2.2 自定义注解

WR.java

package com.guo.mysql.annotation;

import com.guo.mysql.enums.DataSourceEnum;

import javax.xml.bind.Element;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义注解
 * 允许在方法和类型上使用该注解
 */
@Target({ElementType.METHOD,ElementType.TYPE})
//保留方式   SOURCE不会编译在class文件中   CLASS:会编译不会被JVM加载,通过反射获取不到   RUNTIME:可以通过反射调用
@Retention(RetentionPolicy.RUNTIME)
public @interface WR {
    String value() default "slave";   //默认是从库(大多数场景都是在读取数据)
}

2.2.3 切面类

DynamicDataSourceAspect .java

package com.guo.mysql.aspect;

import com.guo.mysql.DynamicDataSource;
import com.guo.mysql.annotation.WR;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class DynamicDataSourceAspect {
    //前置通知   within指定包下的所有类
    @Before("within(com.guo.mysql.service.impl.*) && @annotation(wr)")
    public void before(JoinPoint point, WR wr){
        String name = wr.value();
        DynamicDataSource.name.set(name);
        System.out.println(name);
    }
}

2.2.4 使用注解

上边切面类中自定义切面的时指定的包为接口实现类中,所以这里需要在指定的包下使用注解,实现数据源的动态切换

package com.guo.mysql.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guo.mysql.annotation.WR;
import com.guo.mysql.entity.UserOsskey;
import com.guo.mysql.mapper.UserOsskeyMapper;
import com.guo.mysql.service.UserOsskeyService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

@Service
public class UserOsskeyServiceImpl extends ServiceImpl<UserOsskeyMapper, UserOsskey> implements UserOsskeyService {
    @Resource
    private UserOsskeyMapper userOsskeyMapper;

    // 从库查询
    @Override
    @WR("slave")
    public List<UserOsskey> queryList(QueryWrapper<UserOsskey> queryWrapper) {
        return userOsskeyMapper.selectList(queryWrapper);
    }
    //主库插入
    @Override
    @WR("master")
    public int saveData(UserOsskey userOsskey) {
        return userOsskeyMapper.insert(userOsskey);
    }
}

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

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

相关文章

HTML做一个简单漂亮的宠物网页(纯html代码) 带视频 带音乐 带报告

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 宠物网页设计 | 保护动物网页 | 鲸鱼海豚主题 | 保护大象 | 等网站的设计与制作 | HTML宠物网页设计 | HTML期末大学生网页设计作业 HTML&#xff1a;…

酒水推荐商城|基于Springboot实现酒水商城系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路 关注作者有好处 文末获取源…

FLP、CAP和BASE

FLP不可能原理 FLP定理 FLP Impossibility&#xff08;FLP 不可能性&#xff09;是分布式领域中一个非常著名的定理&#xff0c;定理的论文是由 Fischer, Lynch and Patterson 三位作者于1985年发表 It is impossible to have a deterministic protocol that solves consens…

通过WebSocket实现实时系统通知,以后再也不能装作没看到老板的通知了~~

&#x1f4de; 文章简介&#xff1a;WebSocket实时通知Demo &#x1f4a1; 创作目的&#xff1a;因为公司正在从零搭建CRM&#xff0c;其中有一个需求是系统通知管理&#xff0c;老板发布通知给员工。简单的用数据库实现感觉缺少一些实时性&#xff0c;不是那么生动。于是想到了…

向毕业妥协系列之深度学习笔记(三)DL的实用层面(上)

目录 一.训练_开发_测试集 二.方差与偏差 三.正则化 四.Dropout正则化 五.其他正则化方法 本篇文章大部分又是在ML中学过的&#xff0c;除了Dropout正则化及之后的部分。 一.训练_开发_测试集 在配置训练、验证和测试数据集的过程中做出正确决策会在很大程度上帮助大家创…

[Spring MVC 8]高并发实战小Demo

本项目基于Spring MVC进行关于点赞项目的开发&#xff0c;从传统的点赞到高并发缓存开发最后到消息队列异步开发&#xff0c;可谓是令人大开眼界。 本篇博客全部代码已经放出&#xff0c;本博客重点是后端操作&#xff0c;所以对于前端就十分简单的页面。讲述了关于Redis,Quart…

软件安装教程1——Neo4j下载与安装

Neo4j的下载地址Neo4j Download Center - Neo4j Graph Data Platform 我下载的是Neo4j社区版&#xff08;免费&#xff09;【企业版收费】 解压后的目录如下&#xff1a; 接下来配置环境变量 进入bin目录&#xff0c;复制路径&#xff1a;E:\neo4j\neo4j-community-5.1.0-win…

决策树——预剪枝和后剪枝

一、 为什么要剪枝 1、未剪枝存在的问题 决策树生成算法递归地产生决策树&#xff0c;直到不能继续下去为止。这样产生的树往往对训练数据的分类很准确&#xff0c;但对未知的测试数据的分类却没有那么准确&#xff0c;即容易出现过拟合现象。解决这个问题的办法是考虑决策树…

【Lua基础 第2章】lua遍历table的方式、运算符、math库、字符串操作方法

文章目录&#x1f4a8;更多相关知识&#x1f447;一、lua遍历table的几种方式&#x1f342;pairs遍历&#x1f342;ipairs遍历&#x1f342;i1,#xxx遍历&#x1f31f;代码演示&#x1f342;pairs 和 ipairs区别二、如何打印出脚本自身的名称三、Lua运算符&#x1f538;算术运算…

微服务治理-含服务线上稳定性保障建设治理

微服务的概念 任何组织在设计一套系统&#xff08;广义概念上的系统&#xff09;时&#xff0c;所交付的设计方案在结构上都与该组织的沟通结构保持一致。 —— 康威定律 微服务是一种研发模式。换句话理解上面这句康威定律&#xff0c;就是说 一旦企业决定采用微服务架构&am…

Js逆向教程-12FuckJs

Js逆向教程-12FuckJs 它利用了js的语法特性&#xff1a; 一、特性1 任何一个js类型的变量结果 加上一个字符串 &#xff0c;只会变成字符串。 数组加上字符串&#xff1a; [0]"" 0true加上字符串 true "" true数字加上字符串 1"" 1二、特性…

14天学习训练营之 初识Pygame

目录 学习知识点 PyGame 之第一个 PyGame 程序 导入模块 初始化 ​​1.screen 2. 游戏业务 学习笔记 当 init () 的时候&#xff0c;它在干什么&#xff1f; init () 实际上检查了哪些东西呢&#xff1f; 它到底 init 了哪些子模块&#xff1f; 总结 14天学习训练营导…

2023年计算机毕设选题推荐

同学们好&#xff0c;这里是海浪学长的毕设系列文章&#xff01; 对毕设有任何疑问都可以问学长哦! 大四是整个大学期间最忙碌的时光,一边要忙着准备考研,考公,考教资或者实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越…

·工业 4.0 和第四次工业革命详细介绍

工业 4.0 是制造/生产及相关行业和价值创造过程的数字化转型。 目录 工业 4.0 指南 工业 4.0 与第四次工业革命互换使用&#xff0c;代表了工业价值链组织和控制的新阶段。 网络实体系统构成了工业 4.0 的基础&#xff08;例如&#xff0c;「智慧机器」&#xff09;。他们使用…

基于SpringBoot+Vue的疫苗接种管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端&#xff1a;SpringBoot 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7 数据库管理工具&#xff1a;Navicat 12 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / MyEclipse 是否Maven项…

实验二 帧中继协议配置

计算机网络实验实验二 帧中继协议配置一、实验目的二、实验内容三、实验条件四、实验步骤4.1 连接帧中继交换网4.2 创建DLCI4.3 创建串行接口间的虚电路映射关系4.4 配置路由器的串行接口七、思考题实验二 帧中继协议配置 一、实验目的 掌握路由器上配置帧中继协议的方法 掌握…

SSM整合(一)

SSM整合之简单使用通用mapper 1.准备工作 1.1 在java文件夹下面创建所需要的目录 1.2 导入SSM整合时所需要的所有依赖 <properties><!--这个是统一一些spring插件的包名,避免因为版本不一样而报错--><spring.version>5.3.22</spring.version></p…

SAP S4 FI 后台详细配置教程文档 PART2 (财务会计的基本设置篇)

本篇是系列文章的第二部分&#xff0c;目标是家在配置“字段状态变式”和“年度与期间的配置” 目录 1、 字段状态变式 1.1定义字段状态变式 1.2 向字段状态变式分配公司代码 2、会计年度与记账期间 2.1维护会计年度变式 2.2 向一个会计年度变式分配公司代码 2.3定义未结…

服务器虚拟化有什么好处

服务器虚拟化是一种逻辑角度出发的资源配置技术&#xff0c;是物理实际的逻辑抽象。对于用户&#xff0c;虚拟化技术实现了软件跟硬件分离&#xff0c;用户不需要考虑后台的具体硬件实现&#xff0c;而只需在虚拟层环境上运行自己的系统和软件。 说起服务器虚拟化这个技术&…

你的新进程是如何被内核调度执行到的?(下)

接上文你的新进程是如何被内核调度执行到的&#xff1f;&#xff08;上&#xff09; 四、新进程加入调度 进程在 copy_process 创建完毕后&#xff0c;通过调用 wake_up_new_task 将新进程加入到就绪队列中&#xff0c;等待调度器调度。 //file:kernel/fork.c long do_fork(.…