2 数据源配置_论多数据源(读写分离)的实现方案

好的,作为一个合格的bug生产者,我们直接进入主题,多数据源和读写分离实现方案。

首先多数据源和读写分离什么时候我们才需要呢?

多数据源:一个单体项目过于复杂,需要操作多个业务库的时候,就需要多数据源操作不同的数据

a9221ffeba51be25922aa11cb46bb432.png
读写分离:数据库压力较大时,我们考虑读写分离,主库写,从库读,减少数据库的压力。多个库数据是一样的。

1a460f9b46ff760d7e528b46b11d7491.png

理解完使用场景后,再入主题,怎么实现呢?这里说三种实现方式

1、扩展Spring的AbstractRoutingDataSource
2、通过Mybatis 配置不同的 Mapper 使用不同的 SqlSessionTemplate
3、分库分表中间件,比如Sharding-JDBC 、Mycat等。

好的,再让我们直入主题

扩展Spring的AbstractRoutingDataSource

多数据源

基于Spring AbstractRoutingDataSource做扩展,通过继承AbstractRoutingDataSource抽象类,实现一个管理多个 DataSource的数据源管理类。Spring 在获取数据源时,可以通过 数据源管理类 返回实际的 DataSource 。

然后我们可以定义一个注解,添加到service、dao上,表示一个实际的对应的datasource。

不过这个方式,对于spring事物的支持不好,多个数据源无法保障事物。这个问题是多数据源的通用问题了。

废话不多说,下面我们说下具体实现把,首先pom要引入的依赖的话很简单,就是一个springboot项目。

pom.xml

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.2</version>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions>
</dependency>
<!--实现对 Druid 连接池的自动化配置-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.21</version>
</dependency>

application配置多数据源

server:port: 8080spring:application:name: dynamicdatasource:mall:url: jdbc:mysql://rm-xxxxx.mysql.rds.aliyuncs.com/luu_mall?useSSL=false&useUnicode=true&characterEncoding=UTF-8driver-class-name: com.mysql.jdbc.Driverusername: root # 数据库账号password: root0319@ # 数据库密码type: com.alibaba.druid.pool.DruidDataSource # 设置类型为 DruidDataSourcemin-idle: 0 # 池中维护的最小空闲连接数,默认为 0 个。max-active: 20 # 池中最大连接数,包括闲置和使用中的连接,默认为 8 个。# 用户数据源配置users:url: jdbc:mysql://rm-xxxxxx.mysql.rds.aliyuncs.com/luu_user_center?useSSL=false&useUnicode=true&characterEncoding=UTF-8driver-class-name: com.mysql.jdbc.Driverusername: root # 数据库账号password: root0319@ # 数据库密码type: com.alibaba.druid.pool.DruidDataSource # 设置类型为 DruidDataSource# Druid 自定义配置,对应 DruidDataSource 中的 setting 方法的属性min-idle: 0 # 池中维护的最小空闲连接数,默认为 0 个。max-active: 20 # 池中最大连接数,包括闲置和使用中的连接,默认为 8 个。# Druid 自定义配置,对应 DruidDataSource 中的 setting 方法的属性druid: # 设置 Druid 连接池的自定义配置。然后 DruidDataSourceAutoConfigure 会自动化配置 Druid 连接池。filter:stat: # 配置 StatFilter ,对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatFilterlog-slow-sql: true # 开启慢查询记录slow-sql-millis: 5000 # 慢 SQL 的标准,单位:毫秒merge-sql: true # SQL合并配置stat-view-servlet: # 配置 StatViewServlet ,对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AEenabled: true # 是否开启 StatViewServletlogin-username: root # 账号login-password: root # 密码mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.luu.druid.druid_demo.entity.*

配置多数据源DynamicDataSourceConfig

@Configuration
public class DynamicDataSourceConfig {/*** 创建 orders 数据源*/@Bean(name = "mallDataSource")@ConfigurationProperties(prefix = "spring.datasource.mall") // 读取 spring.datasource.orders 配置到 HikariDataSource 对象public DataSource ordersDataSource() {return DruidDataSourceBuilder.create().build();}/*** 创建 users 数据源*/@Bean(name = "usersDataSource")@ConfigurationProperties(prefix = "spring.datasource.users")public DataSource usersDataSource() {return DruidDataSourceBuilder.create().build();}@Bean@Primarypublic DynamiDataSource dataSource(DataSource mallDataSource, DataSource usersDataSource) {Map<Object, Object> targetDataSources = new HashMap<>(2);targetDataSources.put(DataSourceFlag.DATA_SOURCE_FLAG_MALL, mallDataSource);targetDataSources.put(DataSourceFlag.DATA_SOURCE_FLAG_USER, usersDataSource);// 还有数据源,在targetDataSources中继续添加System.out.println("DataSources:" + targetDataSources);//默认的数据源是oneDataSourcereturn new DynamiDataSource(mallDataSource, targetDataSources);}}

常量DataSourceFlag装载这我们区分数据源的key

public interface DataSourceFlag {

    public static String DATA_SOURCE_FLAG_MALL = "mall";
    public static String DATA_SOURCE_FLAG_USER = "user";

}

DynamiDataSource用来继承Spring AbstractRoutingDataSource来实现数据源切换,并且设置默认数据源。

public class DynamiDataSource extends AbstractRoutingDataSource {/*** 配置DataSource, defaultTargetDataSource为主数据库*/public DynamiDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {//设置默认数据源super.setDefaultTargetDataSource(defaultTargetDataSource);//设置数据源列表super.setTargetDataSources(targetDataSources);super.afterPropertiesSet();}@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceHolder.getRouteKey();}
}

通过DynamicDataSourceHolder操作ThreadLocal来保存当前线程操作的哪个数据源

/*** 数据源管路由*/
public class DynamicDataSourceHolder {private static ThreadLocal<String> routeKey = new ThreadLocal<String>();/*** 获取当前线程的数据源路由的key*/public static String getRouteKey() {String key = routeKey.get();return key;}/*** 绑定当前线程数据源路由的key* 使用完成后必须调用removeRouteKey()方法删除*/public static void setRouteKey(String key) {routeKey.set(key);}/*** 删除与当前线程绑定的数据源路由的key*/public static void removeRouteKey() {routeKey.remove();}
}

到这里配置基本完成来,那要怎么用呢,如何切换数据源呢,这里我们上面有说到,通过注解,来切换数据源。所以定义一个注解ChangeDataSource,不同的key切换不同的数据源

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ChangeDataSource {String value() default DataSourceFlag.DATA_SOURCE_FLAG_MALL;
}

我们把注解用在mapper方法上

@Mapper
public interface TestMapper {int test();String mallNoAnno();@ChangeDataSource(DataSourceFlag.DATA_SOURCE_FLAG_MALL)String mallExitAnno();@ChangeDataSource(DataSourceFlag.DATA_SOURCE_FLAG_USER)String userNoAnno();@ChangeDataSource(DataSourceFlag.DATA_SOURCE_FLAG_USER)String userExitAnno();}

然后通过切面DataSourceAspect更换ThreadLocal中key实现数据源切换

@Aspect
@Component
public class DataSourceAspect implements Ordered {protected Logger logger = LoggerFactory.getLogger(getClass());/*** 切点: 所有配置 ChangeDataSource 注解的方法*/@Pointcut("@annotation(com.luu.druid.druid_demo.common.ChangeDataSource)")public void dataSourcePointCut() {}@Around("dataSourcePointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();ChangeDataSource ds = method.getAnnotation(ChangeDataSource.class);// 通过判断 @ChangeDataSource注解 中的值来判断当前方法应用哪个数据源DynamicDataSourceHolder.setRouteKey(ds.value());System.out.println("当前数据源: " + ds.value());logger.debug("set datasource is " + ds.value());try {return point.proceed();} finally {DynamicDataSourceHolder.removeRouteKey();logger.debug("clean datasource");}}@Overridepublic int getOrder() {return 1;}
}

单元测试

@SpringBootTest
class DruidDemoApplicationTests {@AutowiredTestMapper testMapper;@Testvoid contextLoads() {int i = testMapper.test();String id = testMapper.mallNoAnno();String id2 = testMapper.mallExitAnno();String name = testMapper.userNoAnno();String name2 = testMapper.userExitAnno();}
}

到这里呢,代码基本写完来。这里就是多数据源的配置,然后还有读写分离怎么实现呢。

而上面我们说到事物上不起效果的,因为事物上要拿到数据源的连接对象,而这里我们在mapper层有更换数据源,所以是不行的,所以数据源无法切换成果,然后执行的时候会报错的。但是如果我们整个是在Service上使用这个注解,整个方法上同一个数据源就可以的。

实现读写分离

其实读写分离的实现通过上面的方式稍微修改下就可以来,就是在切面中,不在通过注解,根据方法名的前缀来判断是走主库,还是走从库。比如find、select这样读数据的就走从库,而insert这样的就走主库。具体的代码的话,摸一摸我发量不多的头,算了,偷一偷就不贴来,反正思路就是这样的。

通过Mybatis 配置不同的 Mapper 使用不同的 SqlSessionTemplate

根据不同操作类(就是mapper),然后创建不同的SqlSessionTemplate ,这样每个SqlSessionTemplate 就可以设置不同的数据源和扫描不同的mapper咯。听起来是不是很简单呢。不用管什么切面不切面的,不像上面那么麻烦咯。但是多数据源的通病还是在滴,那就是多数据源事物用起来不方便啦。

多数据源

还是用刚才的springboot项目吧,改动一下咯。pom文件啥的就不说咯,跟上面一样的。然后我们看下配置文件,数据源还是一样,两个数据源一样的配置,只不过这里没有mybatis的配置咯。

application

server:port: 8080spring:application:name: dynamicdatasource:mall:url: jdbc:mysql://rm-wz9yy0528x91z1iqdco.mysql.rds.aliyuncs.com/luu_mall?useSSL=false&useUnicode=true&characterEncoding=UTF-8driver-class-name: com.mysql.cj.jdbc.Driverusername: root # 数据库账号password: root0319@ # 数据库密码type: com.alibaba.druid.pool.DruidDataSource # 设置类型为 DruidDataSourcemin-idle: 0 # 池中维护的最小空闲连接数,默认为 0 个。max-active: 20 # 池中最大连接数,包括闲置和使用中的连接,默认为 8 个。# 用户数据源配置users:url: jdbc:mysql://rm-wz9yy0528x91z1iqdco.mysql.rds.aliyuncs.com/luu_user_center?useSSL=false&useUnicode=true&characterEncoding=UTF-8driver-class-name: com.mysql.cj.jdbc.Driverusername: root # 数据库账号password: root0319@ # 数据库密码type: com.alibaba.druid.pool.DruidDataSource # 设置类型为 DruidDataSource# Druid 自定义配置,对应 DruidDataSource 中的 setting 方法的属性min-idle: 0 # 池中维护的最小空闲连接数,默认为 0 个。max-active: 20 # 池中最大连接数,包括闲置和使用中的连接,默认为 8 个。# Druid 自定义配置,对应 DruidDataSource 中的 setting 方法的属性druid: # 设置 Druid 连接池的自定义配置。然后 DruidDataSourceAutoConfigure 会自动化配置 Druid 连接池。filter:stat: # 配置 StatFilter ,对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatFilterlog-slow-sql: true # 开启慢查询记录slow-sql-millis: 5000 # 慢 SQL 的标准,单位:毫秒merge-sql: true # SQL合并配置stat-view-servlet: # 配置 StatViewServlet ,对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AEenabled: true # 是否开启 StatViewServletlogin-username: root # 账号login-password: root # 密码

DBConstants常量类

public class DBConstants {public static final String TX_MANAGER_MALL = "malltransactionManager";public static final String TX_MANAGER_SER = "usertransactionManager";
}

然后就是创建不同的SqlSessionTemplate和数据源了。

DataSourceMallConfig配置mall的数据源,并且mallSqlSessionTemplate设置了扫面mapper包位置

@Configuration
@MapperScan(basePackages = "com.luu.druid.druid_demo.mapper.mall", sqlSessionTemplateRef = "mallSqlSessionTemplate")
public class DataSourceMallConfig {/*** 创建 mall 数据源*/@Bean(name = "mallDataSource")@ConfigurationProperties(prefix = "spring.datasource.mall")public DataSource mallDataSource() {return DruidDataSourceBuilder.create().build();}/*** 创建 MyBatis SqlSessionFactory*/@Bean(name = "mallSqlSessionFactory")public SqlSessionFactory sqlSessionFactory(DataSource mallDataSource) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();// <2.1> 设置 orders 数据源bean.setDataSource(mallDataSource);// <2.2> 设置 entity 所在包bean.setTypeAliasesPackage("com.luu.druid.druid_demo.entity.*");// <2.3> 设置 config 路径
// bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml"));// <2.4> 设置 mapper 路径bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/mall/*.xml"));return bean.getObject();}/*** 创建 MyBatis SqlSessionTemplate*/@Bean(name = "mallSqlSessionTemplate")public SqlSessionTemplate sqlSessionTemplate(DataSource mallDataSource) throws Exception {return new SqlSessionTemplate(this.sqlSessionFactory(mallDataSource));}/*** 创建 mall 数据源的 TransactionManager 事务管理器*/@Bean(name = DBConstants.TX_MANAGER_MALL)public PlatformTransactionManager transactionManager(DataSource mallDataSource) {return new DataSourceTransactionManager(mallDataSource);}}

DataSourceUserConfig配置user的数据源,并且userSqlSessionTemplate设置了扫面mapper包位置

@Configuration
@MapperScan(basePackages = "com.luu.druid.druid_demo.mapper.user", sqlSessionTemplateRef = "userSqlSessionTemplate")
public class DataSourceUserConfig {/*** 创建 user 数据源*/@Bean(name = "userDataSource")@ConfigurationProperties(prefix = "spring.datasource.users")public DataSource userDataSource() {return DruidDataSourceBuilder.create().build();}/*** 创建 MyBatis SqlSessionFactory*/@Bean(name = "userSqlSessionFactory")public SqlSessionFactory sqlSessionFactory(DataSource userDataSource) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();// <2.1> 设置 orders 数据源bean.setDataSource(userDataSource);// <2.2> 设置 entity 所在包bean.setTypeAliasesPackage("com.luu.druid.druid_demo.entity.*");// <2.3> 设置 config 路径
// bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml"));// <2.4> 设置 mapper 路径bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/user/*.xml"));return bean.getObject();}/*** 创建 MyBatis SqlSessionTemplate*/@Bean(name = "userSqlSessionTemplate")public SqlSessionTemplate sqlSessionTemplate(DataSource userDataSource) throws Exception {return new SqlSessionTemplate(this.sqlSessionFactory(userDataSource));}/*** 创建 user 数据源的 TransactionManager 事务管理器*/@Bean(name = DBConstants.TX_MANAGER_SER)public PlatformTransactionManager transactionManager(DataSource userDataSource) {return new DataSourceTransactionManager(userDataSource);}}

到这多数据源配置就差不多了,不同的mapper对应不同的数据源。这里mapper,entity啥的就不贴出来了,秉承着能偷懒就偷懒的一贯风格。直接把单元测试贴一下看下。

@SpringBootTest
class DruidDemoApplicationTests {@AutowiredUserMapper userMapper;@AutowiredTestMapper testMapper;@Testvoid contextLoads() {String id = testMapper.mallNoAnno();String id2 = testMapper.mallExitAnno();String name = userMapper.userNoAnno();String name2 = userMapper.userExitAnno();}
}

当然,上面说到说,依然是多数据源,所以呢对于事物的支持依然是有问题的。

读写分离

这种方式实现读写分离,就不用多说咯吧,我这个专业bug制造者都想的明白,各位大佬也能想明白的。

分库分表中间件,比如Sharding-JDBC 、Mycat等

对于分库分表的中间件,会解析我们编写的 SQL ,路由操作到对应的数据源。那么,它们天然就支持多数据源。如此,我们仅需配置好每个表对应的数据源,中间件就可以透明的实现多数据源或者读写分离。Sharding-JDBC 、Mycat是比较常用的中间件,这里使用的话就不写了,后面会专门写如何去使用它们的,Sharding-JDBC并且支持分布式事物的。

如果需要可以下载代码试试的dynamic-datasource-spring、dynamic-datasource-mybatis,到此完美收工:

https://github.com/servef-toto/luu_yinchuishiting.git​github.com

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

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

相关文章

docker安装gitlab_docker 安装部署gitlab

下载镜像并且启动//下载镜像(镜像较大,如果网速不行可以切换阿里云镜像仓库) docker pull gitlab/gitlab-ce&#xff1a;latest //启动镜像 docker run --name gitlab -d -p 18080:80 -p 1443:443 -p 2222:22 -v /data/gitlab/config:/etc/gitlab -v /data/gitlab/logs:/var/lo…

easyexcel导出百万级数据_百万级别数据Excel导出优化

这篇文章不是标题党&#xff0c;下文会通过一个仿真例子分析如何优化百万级别数据Excel导出。笔者负责维护的一个数据查询和数据导出服务是一个相对远古的单点应用&#xff0c;在上一次云迁移之后扩展为双节点部署&#xff0c;但是发现了服务经常因为大数据量的数据导出频繁Ful…

运行catia_CATIA清除错误目录及防串链接

清除错误目录大家好今天我来教大家如何清除CATIA错误目录&#xff0c;由于我们在日常工作中经常会互相发送数据&#xff0c;而CATIA的缓存功能通常会记录这些文件的路径&#xff0c;所以我们打开别人发的数据时在桌面的右下角通常会有警告的提醒&#xff0c;那我们如何来清除缓…

python 键盘输入数字_九宫格键盘输入

九宫格键盘输入 Letter Combinations of a Phone Number 给定一个数字字符串&#xff0c;返回数字可能代表的所有可能的字母组合。 数字到字母的映射&#xff08;就像九宫格电话按钮一样&#xff09;如下图。 Given a digit string, return all possible letter combinations t…

java2组随机数的共通数_java随机数产生-指数分布 正态分布 等

1 指数分布指数分布的概率密度函数&#xff1a;ylamda*exp(-lamda*x)x>0由此可以计算概率分布函数&#xff1a;y1-exp(-lamda*x)x>0y是 X首先&#xff0c;把y当作是在(0&#xff0c;1)区间的均匀分布的随机变量。然后&#xff0c;求y1-exp(-lamda*x)的逆函数&#xff0c…

c# combobox集合数据不显示_excel打开数据时显示乱码/问号amp;看起来一样却v不出来怎么办...

1、乱码问题&#xff1a;今天正好碰到这种情况&#xff0c;想起来写一写。有时从客户那里拿到的CSV等文件&#xff0c;直接用excel打开是这样的&#xff1a;其实观察一下会发现&#xff1f;的地方一般就是中文&#xff0c;实质上是中文字符显示不出来。有小伙伴一直是单独下个W…

if嵌套while循环语句_Python学习笔记015--while循环嵌套

while循环嵌套前面学习过if的嵌套了&#xff0c;想一想if嵌套是什么样子的&#xff1f;类似if的嵌套&#xff0c;while嵌套就是&#xff1a;while里面还有while<1>while嵌套的格式while 条件1:条件1满足时&#xff0c;做的事情1条件1满足时&#xff0c;做的事情2条件1满足…

long类型怎么转换成string_Python知识点-Python变量类型有哪些?

Python的内置变量类型是非常基础的知识点&#xff0c;善用变量类型转换在日常的工作学习中会给我们带来非常大的帮助。相对于其他语言&#xff0c;Python的变量类型既有自己的特色方法&#xff0c;也有借鉴前辈的优秀之处。今天酷仔整理总结了关于Python内置变量类型一文&#…

mysql连接服务密码_Hydra爆破常见服务密码

Hydra是一款专业的服务密码探测工具&#xff0c;支持FTP、LDAP、MYSQL、Oracle、POP3、SMB、SSH等网络服务&#xff0c;多用于信息安全检查工作中弱口令登陆测试。实验环境操作系统&#xff1a;Kali 2019.4目标网络&#xff1a;192.168.168.0/24爆破教程第一步 升级系统、软件版…

矩阵求逆c语言实现_[V-SLAM] Bundle Adjustment 实现

SLAM问题的后端有主要有滤波和优化两种方案。目前&#xff0c;普遍认为优化的方法在精度上要超过滤波方法&#xff0c;因为它可以进行多次的线性化。近年来出现的SLAM算法也大都是基于优化的算法&#xff08;如ORB-SLAM、DSO等&#xff09;。优化问题的核心便是Bundle Adjustme…

centos安装 mysql_Linux centos 安装 mysql 5.6

一、mysql下载1、方式一(简单粗暴)直接在linux 目录下wget https://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.43-linux-glibc2.12-x86_64.tar.gz2、方式二(官方下载)浏览器打开网址&#xff1a;https://www.mysql.com如下图依次点击1、2、3、4步如下图设置对应版本点击…

tracepro杂散光分析例子_光刻机的蜕变过程及专利分析

来源&#xff1a;芯通社近两年&#xff0c;中国芯片产业受到了严重打击&#xff0c;痛定思痛之余也让国人意识到芯片自主研发的重要性。从2008年以来&#xff0c;十年间&#xff0c;芯片都是我国第一大宗进口商品&#xff0c;进口额远超于排名第二的石油。2018年我国进口集成电…

docker mysql日志_面试官问:了解Mysql主从复制原理么?我呵呵一笑

搭建Mysql主从同步之前&#xff0c;我们先来说他们之间同步的过程与原理&#xff1a;同步复制过程献上一张图&#xff0c;这张图诠释了整个同步过程主从复制过程&#xff1a;slave节点与主节点进行连接&#xff0c;建立主从关系&#xff0c;并把从哪开始同步&#xff0c;及哪个…

查看socket缓冲区数据_什么是socket缓冲区?

Socket 就是发送和接收网络数据&#xff0c;Socket 有发送缓冲也有接收缓冲&#xff0c;这些缓冲区有什么作用&#xff1f;1、什么是Socket缓冲区&#xff1f;熟悉 Socket 的读者都知道&#xff0c;Socket 的发送和接收&#xff0c;就是调用 send 和 recv 函数。实际操作中&…

哈工大大数据实验_科研常用 | 实验大数据分析方法

对于每个科研工作者而言&#xff0c;对实验数据进行处理是在开始论文写作之前十分常见的工作之一。但是&#xff0c;常见的数据分析方法有哪些呢&#xff1f;常用的数据分析方法有&#xff1a;聚类分析、因子分析、相关分析、对应分析、回归分析、方差分析。1、聚类分析(Cluste…

搭建github服务器_搭建一个属于自己的公网博客

相信每一位程序员都喜欢拥有一个属于自己的博客。当然&#xff0c;在我认为&#xff0c;内容以及模块都要自己进行可扩展定义才是真正属于自己的。那么想要一个博客就必须要有一个服务器和一个域名&#xff0c;这样的话才能让自己的博文内容发扬光大&#xff0c;但是服务器的性…

mysql修改级联表数据_MySQL数据库 外键,级联, 修改表的操作

1.外键: 用来建立两张表之间的关系- 一对多- 多对多- 一对一研究表与表之间的关系:1.定义一张 员工部门表id, name, gender, dep_name, dep_desc- 将所有数据存放在一张表中的弊端:1.结构不清晰 ---> 不致命2.浪费空间 ---> 不致命3.可扩展性极差 ---> 不可忽视的弊端…

OpenCV学习笔记 - 使用密集光流检测运动的简单方法

一、简述 使用光流进行运动检测的方法与帧间差分方法类似。主要区别在于第一步,我们将从光流而不是帧差分中获取初始运动信息(一些神经网络模型也是基于光流和原始图像进行运动识别训练的)。 该算法概述如下: 1、计算密集光流 2、获得运动掩模的阈值光流 3、在运动蒙版中查…

mysql 5.6.27安装图解_Linux下MySQL 5.6.27 安装教程

本文实例为大家分享了Linux下MySQL 5.6.27 安装教程&#xff0c;供大家参考&#xff0c;具体内容如下1、下载地址2、将压缩包上传到服务器3、解压tar -zxf mysql-5.6.27-linux-glibc2.5-x86_64.tar.gz4、移动压缩包至mysql文件夹下mp mysql-5.6.27-linux-glibc2.5-x86_64 /usr…

linux部署tomcat项目404_Tomcat部署项目的几种常见方式

点击蓝字“程序员考拉”欢迎关注&#xff01;1 /直接将web项目文件件拷贝到webapps目录中这是最常用的方式&#xff0c;Tomcat的Webapps目录是Tomcat默认的应用目录&#xff0c;当服务器启动时&#xff0c;会加载所有这个目录下的应用。如果你想要修改这个默认目录&#xff0c;…