阅读完需:约 27 分钟
Spring @RequestHeader用法
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
//..
@Controller
public class HelloController {
@RequestMapping(value = "/hello.htm")
public String hello(@RequestHeader(value="User-Agent") String userAgent)
//..
}
}
在上面的代码片段中,定义了一个映射到/hello.htm的hello控制器方法。同时用 @RequestHeader注解将请求头中”User-Agent“的变量与”userAgent“变量绑定。当此某个请求映射到了此控制器方法,Spring会检查请求头中的”User-Agent“变量,并将其与”userAgent“变量绑定。
如果 @RequestHeader绑定的变量,在请求头中不存在,Spring会将控制器中的参数初始化为null。如果想给控制器参数提供一个默认值,在 @RequestHeader的defaultParameter属性。
@RequestMapping(value = "/hello.htm")
public String hello(@RequestHeader(value="User-Agent", defaultValue="foo") String userAgent)
//..
}
Springboot 3.2.6 修改 tomcat连接池
package org.config.init;
import org.apache.catalina.Context;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.tomcat.util.scan.StandardJarScanner;
import org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executors;
/**
* @Description: TomcatFactoryConfig
*/
@Configuration
public class TomcatFactoryConfig {
/**
* tomcat-embed-jasper引用后提示jar找不到的问题
*/
@Bean
public TomcatServletWebServerFactory tomcatFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
}
};
factory.addConnectorCustomizers(connector -> {
// connector.setProperty("relaxedPathChars", "[]{}");
// connector.setProperty("relaxedQueryChars", "[]{}");
connector.setProperty("relaxedPathChars", "\"#<>[\\]^`{|}/");
connector.setProperty("relaxedQueryChars", "\"#<>[\\]^`{|}/");
});
//支持APR AIO
//factory.setProtocol("org.apache.coyote.http11.Http11AprProtocol");
//factory.addContextLifecycleListeners(new AprLifecycleListener());
//支持APR AIO
// 关键点在这里,写入自己的配置
Collection<TomcatProtocolHandlerCustomizer<?>> tomcatProtocolHandlerCustomizers = factory.getTomcatProtocolHandlerCustomizers();
tomcatProtocolHandlerCustomizers.add(protocolHandlerCustomizer());
factory.setTomcatProtocolHandlerCustomizers(tomcatProtocolHandlerCustomizers);
return factory;
}
public static TomcatProtocolHandlerCustomizer<?> protocolHandlerCustomizer(){
return protocolHandler -> {
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}
}
JKD17运行报错Unable to make field private final byte[] java.lang.String.value accessible
大致意思就是通过反射获取String类中的私有字段byte[] value 失败了。
那怎么会这样呢,原因是:
在Java9之后引入了module模块的概念,而不同module模块间是不允许使用反射来访问非public的字段/方法/构造函数(field/method/constructor),除非被访问的目标模块为反射设置open即允许自身被其他模块进行non-public反射访问。
解决办法:
Java 9之后引入了一个新的JVM选项 –illegal-access,该选项有四个可选值:
permit:Jdk9、Jdk11的默认值,允许不同模块间进行non-public反射访问,且仅在首次非法访问会给出警告,后续不再警告。在该模式下会自动将Jdk8(或更低版本)的代码进行opens设置,即允许Jdk8的代码被其他模块进行non-public反射访问,也允许Jdk8的代码对其他模块进行non-public反射访问,如此可保证高、低版本混合的Java程序如往常一样正常运行。
warn:类似permit,但是每次非法访问都会警告
debug:类似warn,但在warn的基础上加入了类似e.printStackTrace()的功能
deny:未来的默认值,禁止所有的不同模块间的non-public反射访问,出现非法反射则抛出异常,除了使用特别的命令行参数排除的模块,比如使用 –add-opens排除某些模块使其能够通过非法反射访问
线上使用了–illegal-access=deny,所以出现非法反射时会导致程序抛异常而启动失败,
而本地开发环境运行程序时,未明确设置–illegal-access则使用默认–illegal-access=premit,所以可以启动成功,但在第一次非法反射访问时给出了警告。
综上,在设置了–illegal-access=deny(推荐设置deny,兼容未来Java版本)时,需同时添加–add-opens以开启对应模块/包允许被其他模块进行非法(non-public)反射访问。
例如根据之前的日志:
Unable to make field private final byte[] java.lang.String.value accessible:
module java.base does not “opens java.lang” to unnamed module @1b70203f
将关键提示日志拆解后如下表:
注: ALL-UNNAMED表示所有的未命名模块
反复重启根据提示最终整理如下完整–add-opens选项:
--illegal-access=deny
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.lang.invoke=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens java.base/java.text=ALL-UNNAMED
--add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED
maven-failsafe-plugin与maven-surefire-plugin配置
一些好用的maven插件介绍
https://www.cnblogs.com/wanghengbin/p/17926868.html
Maven Surefire Plugin是Maven生态系统中的一个重要组成部分,主要用于自动化测试和单元测试。它能够方便地集成到Maven项目中,并提供了丰富的功能来支持各种测试框架和工具。
Maven Surefire Plugin的主要作用是在Maven项目中执行测试用例。它支持多种测试框架,如JUnit、TestNG等,并能够生成详细的测试报告,帮助开发人员快速定位和修复问题。
Maven Surefire Plugin通过在项目的构建过程中运行测试用例来工作。当您执行Maven的test命令时,Surefire Plugin会自动扫描项目中的测试源代码,并执行相应的测试。测试结果将显示在控制台或生成的测试报告中。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<!-- 配置测试类文件所在的目录 -->
<testSourceDirectory>src/test/java</testSourceDirectory>
<!-- 配置测试类的匹配规则 -->
<includes>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
Maven Failsafe插件是Maven提供的一个测试插件,用于运行集成测试。 与Maven Surefire插件不同,Maven Failsafe插件用于运行需要依赖外部环境、例如数据库或网络等的测试。 与Surefire插件类似,Failsafe插件会自动查找和执行符合命名规则的测试类和测试方法。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M5</version>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
<execution>
<id>verify</id>
<goals>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- 配置测试类文件所在的目录 -->
<testSourceDirectory>src/test/java</testSourceDirectory>
<!-- 配置测试类的匹配规则 -->
<includes>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
但是如果用的是JDK17,那么也需要在Maven配置上加上刚刚的忽略配置,例如:
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
<argLine>--add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED @{argLine}</argLine>
</configuration>
</plugin>
Spring Boot 升级 3.2 报错 Invalid value type for attribute ‘factoryBeanObjectType‘: java.lang.String
java.lang.IllegalArgumentException: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
原因分析
mybatis-spring 官方 ISSUE: https://github.com/mybatis/spring/issues/855
项目中使用 mybatis-plus-boot-starter 当前最新版本 3.5.4.1 ,其中依赖的 mybatis-spring 版本为 2.1.1
在 mybatis-spring 2.1.1 版本的 ClassPathMapperScanner#processBeanDefinitions 方法里将 BeanClassName
赋值给 String 变量
并将 beanClassName
赋值给 factoryBeanObjectType
但是在 Spring Boot 3.2 版本中FactoryBeanRegistrySupport#getTypeForFactoryBeanFromAttributes方法已变更,如果 factoryBeanObjectType 不是 ResolvableType 或 Class 类型会抛出 IllegalArgumentException 异常。
此时因为 factoryBeanObjectType 是 String 类型,不符合条件而抛出异常。
解决方案(新)
Mybatis-Plus 于 2023年12月24日发布 mybatis-plus v3.5.5 版本,发布日志声明 升级spring-boot3版本mybatis-spring至3.0.3
。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.7</version>
</dependency>
SpringBoot 3.X 自动配置详解
创建自定义的自动配置涉及到几个关键步骤:定义配置类、使用条件注解确保配置仅在合适的条件下生效、声明所需的bean,以及在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中注册自定义自动配置。
mica-auto 自动生成配置
<dependency>
<groupId>net.dreamlu</groupId>
<artifactId>mica-auto</artifactId>
<version>2.3.0</version>
<scope>provided</scope>
</dependency>
在 Kotlin + Spring Boot 中启用虚拟线程
- 从Java 19开始, 的概念已作为预览功能
Virtual Thread
新增,并计划从Java 21 LTS开始将其纳入官方功能。虽然传统的平台线程直接映射到操作系统 (OS)的线程,但虚拟线程作为Java 虚拟机 (JVM)抽象的轻量级虚拟线程运行,消耗的内存显著减少。虚拟线程由 JVM 的调度程序自动管理,使开发人员能够将更多精力放在业务逻辑上,同时享受性能优势。 - Spring Boot 3和Spring Framework 6正式支持Virtual Thread。本文总结了如何将基于 Spring Boot 的项目中处理Spring Web MVC请求、@Async、协程执行的平台线程替换为虚拟线程。
虚拟线程功能
-
Java 19/20开始
Virtual Thread
作为预览功能提供,并在Java 21 ( LTS版本)中作为完整功能提供。 - 传统的平台线程直接包装操作系统线程,无法在由IO或计算(例如网络和数据库操作)引起的阻塞期间使用。另一方面,虚拟线程作为JVM管理的逻辑单元要轻得多,与物理 OS 线程隔离。至关重要的是,当发生阻塞时,正在使用的OS线程可以由另一个虚拟线程使用,从而显著改善并发处理。与Reactive方法不同,这使开发人员无需进行重大更改即可在传统的顺序编程中实现显着的性能改进。(这代表了JVM社区多年来的一次重大创新。)
- Java社区在引入Virtual Thread时,一直努力保持向后兼容性。可用于轻松创建Executor对象。 将其与 servlet 容器和 Kotlin 协程集成,无需更改现有代码即可立即使用Virtual Thread 。
Executors.newVirtualThreadPerTaskExecutor()
-
虚拟线程有两种使用方式。Java 19/20
--release 19 --enable-preview
要求您在项目构建时和--enable-preview
运行时添加该选项。而Java 21则不需要添加此选项。
将 HTTP 请求处理切换到虚拟线程
- 在Java生态系统中,servlet 容器通过物理平台线程处理请求,这些线程是针对每个单元请求包装的 OS 线程。以下代码允许您指示Apache Tomcat ( Spring Boot中嵌入的 servlet 实现)使用虚拟线程而不是平台线程处理所有请求。
import org.apache.coyote.ProtocolHandler
import org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.util.concurrent.Executors
@Configuration
class TomcatConfig {
@Bean
fun protocolHandlerVirtualThreadExecutorCustomizer(): TomcatProtocolHandlerCustomizer<*>? {
return TomcatProtocolHandlerCustomizer<ProtocolHandler> { protocolHandler: ProtocolHandler ->
protocolHandler.executor = Executors.newVirtualThreadPerTaskExecutor()
}
}
}
将异步执行切换到虚拟线程
-
在Spring Boot生态系统中,执行异步逻辑最方便的方法之一,传统上在基于平台线程池的AsyncTaskExecutor
@Async
实现分配的线程上运行。可以按如下方式将其切换到虚拟线程。
/**
* 异步配置
*
* 传统上在基于平台线程池的AsyncTaskExecutor@Async实现分配的线程上运行。可以按如下方式将其切换到虚拟线程。
*
* @date 2024/06/17
*/
@Configuration
@EnableAsync
public class AsyncConfig {
/**
* 任务执行者
*
* @return {@link AsyncTaskExecutor}
*/
@Bean
public AsyncTaskExecutor asyncTaskExecutor() {
TaskExecutorAdapter taskExecutor = new TaskExecutorAdapter(Executors.newThreadPerTaskExecutor(Thread.ofVirtual().name("virtual-async", 1).factory()));
taskExecutor.setTaskDecorator(new LoggingTaskDecorator());
return taskExecutor;
}
}
将调度程序执行切换到虚拟线程
- 在Spring Boot生态系统中,
@Scheduled
任务是根据定义的规则在特定时间执行的,传统上是在由基于平台线程池的ThreadPoolTask Executor 实现分配的线程上运行。可以按如下方式将其切换到虚拟线程。
/**
* 调度配置
*
* @Scheduled 任务是根据定义的规则在特定时间执行的
* 传统上是在由基于平台线程池的ThreadPoolTask Executor 实现分配的线程上运行。这可以按如下方式切换到虚拟线程。
*
* @date 2024/06/17
*/
@Configuration
@EnableScheduling
public class SchedulingConfig {
/**
* 任务调度器
*
* @return {@link TaskScheduler}
*/
@Bean
public TaskScheduler taskScheduler() {
SimpleAsyncTaskScheduler simpleAsyncTaskScheduler = new SimpleAsyncTaskScheduler();
simpleAsyncTaskScheduler.setThreadNamePrefix("SchedulingVirtual-");
simpleAsyncTaskScheduler.setVirtualThreads(true);
simpleAsyncTaskScheduler.setTaskTerminationTimeout(10*1000);
return simpleAsyncTaskScheduler;
}
}
将 Kotlin 协程执行切换到虚拟线程
- 早在虚拟线程出现之前,Kotlin就早已通过协程为开发人员提供了类似的挂起功能。通过编写下面的代码并使用应用虚拟线程而不是传统使用的Dispatchers.IO 的Dispatchers.LOOM,协程可以在虚拟线程上执行。
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher
import java.util.concurrent.Executors
val Dispatchers.LOOM: CoroutineDispatcher
get() = Executors.newVirtualThreadPerTaskExecutor().asCoroutineDispatcher()
SpringBoot 3.2:虚线程支持
springboot3.2在上周发布了,可以直接通过springboot提供的配置spring.threads.virtual.enabled: true
为许多组件开启虚线程支持
spring:
# 启用虚拟线程
threads:
virtual:
enabled: true
生效点关键代码在下面
jdk21 发布了虚拟线程,tomcat 10 从传统线程池切换到虚拟线程使用的好处
JDK 21 支持了虚拟线程,这是一种轻量级的线程模型,可以简化高吞吐量并发 Java 应用程序的创建、管理和监控,让开发者能够高效地处理数百万个任务,并更好地利用系统资源。
虚拟线程的好处有:
- 虚拟线程不是绑定在特定的操作系统线程上,而是由 JDK 提供的协作式调度器来管理,这样可以减少上下文切换的开销,提高 CPU 利用率。
- 虚拟线程可以在一个操作系统线程上运行多个虚拟线程,这样可以节省内存空间,因为虚拟线程的栈空间和其他资源都比平台线程要小得多。
- 虚拟线程可以自动挂起和恢复,当虚拟线程执行阻塞 I/O 操作时,它会被挂起,直到 I/O 完成后再被恢复,这样可以避免浪费操作系统线程的时间,让它们去执行其他虚拟线程的任务。
- 虚拟线程是兼容平台线程的,它们都是 java.lang.Thread 的实例,可以使用现有的 Java 代码和 API 来操作它们,也可以在平台线程和虚拟线程之间进行无缝的迁移和互操作。
虚拟线程的运行方式和对 I/O 的控制如下:
- 虚拟线程是由 JDK 内置的 Fork/Join 框架来执行的,每个操作系统线程都对应一个 Fork/Join 池,每个池中有一个或多个 Fork/Join 工作线程来运行虚拟线程。
- 当一个虚拟线程被创建时,它会被放入一个 Fork/Join 池中的一个工作队列中,等待被调度。当一个工作线程空闲时,它会从自己或其他工作队列中取出一个虚拟线程来执行。
- 当一个虚拟线程执行到一个阻塞 I/O 操作时,它会被挂起,并且释放它所占用的工作线程。JDK 会使用 java.nio.channels 包中的异步 I/O API 来处理阻塞 I/O 操作,并且在 I/O 完成后通知对应的虚拟线程恢复执行。
- 当一个虚拟线程执行完毕或者被终止时,它会从 Fork/Join 池中移除,并且释放它所占用的资源。
Spring-cloud-gateway 接口转发无响应
问题复述:连接Nacos,转发配置与名称都是对的,服务可以正常的转发到后端,但是gateway无法正常接受后端返回的数据,gateway与后端业务服务没有任何的报错。
问题原因:经过线上的Debug,怀疑是环境变量的错误,最后找到问题response-timeout
,配置错误,没有写单位
response-timeout
是 Spring Cloud Gateway 中的一个配置项,用于设置 HTTP 客户端请求的响应超时时间。这个配置项确保网关在设定的时间内等待后端服务的响应,如果超时则会终止请求并返回错误。
下面是如何在 Spring Cloud Gateway 的配置文件中设置 response-timeout
的示例:
spring:
cloud:
gateway:
httpclient:
response-timeout: 5s # 设置响应超时时间为 5 秒
单位:超时时间可以使用 ms
(毫秒)、s
(秒)、m
(分钟)等单位。例如,5s
表示 5 秒,200ms
表示 200 毫秒。
默认值:如果不设置,默认的响应超时时间为 30 秒。
完整示例
spring:
cloud:
gateway:
routes:
- id: example_route
uri: http://example.com
predicates:
- Path=/example/**
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
httpclient:
connect-timeout: 2s # 设置连接超时时间为 2 秒
response-timeout: 5s # 设置响应超时时间为 5 秒
pool:
max-idle-time: 60s # 连接池中连接的最大空闲时间
max-life-time: 300s # 连接池中连接的最大生存时间
max-connections: 1000 # 连接池的最大连接数
acquire-timeout: 45s # 连接池获取连接的超时时间
- connect-timeout:设置与后端服务的连接超时时间。
- pool.max-idle-time:设置连接池中连接的最大空闲时间。
- pool.max-life-time:设置连接池中连接的最大生存时间。
- pool.max-connections:设置连接池的最大连接数。
- pool.acquire-timeout:设置从连接池中获取连接的超时时间。
在 Spring Cloud Gateway 的配置中,时间参数需要明确指定单位。如果你不指定单位,只写数字 60
,会导致配置无法正确解析,进而产生错误,运维在配置参数的时候,以为60就可以了,没有写入 s 单位
如果你只写 60
而不指定单位,配置会被认为是无效的,导致 Spring Cloud Gateway 启动失败或者配置项不起作用:
spring:
cloud:
gateway:
httpclient:
response-timeout: 60 # 错误:缺少单位
支持的单位
Spring Cloud Gateway 支持的时间单位包括:
-
ns
:纳秒 -
us
:微秒 -
ms
:毫秒 -
s
:秒 -
m
:分钟 -
h
:小时 -
d
:天
SpringBoot 项目能处理多少请求?最大连接数?
一个 SpringBoot 项目能同时处理多少请求?
一个 SpringBoot 项目,未进行任何特殊配置,全部采用默认设置,这个项目同一时刻,最多能同时处理多少请求?(SpringBoot 3.3.1)
测试接口
@Slf4j
@RestController
public class TestController {
@GetMapping("/getTest")
public void getTest(@RequestParam(defaultValue = "1") int num) throws Exception {
log.info("{} 接受到请求:num={}", Thread.currentThread().getName(), num);
TimeUnit.HOURS.sleep(1);
}
}
请求接口
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
int finalI = i;
new Thread(() -> {
HttpUtil.get("127.0.0.1:8080/getTest?num=" + finalI);
}).start();
}
//阻塞主线程
Thread.yield();
}
测试结果200次
因为Tomcat默认
server:
tomcat:
# 当所有可能的请求处理线程都在使用中时,传入连接请求的最大队列长度
accept-count: 100
# 服务器在任何给定时间接受和处理的最大连接数。一旦达到限制,操作系统仍然可以接受基于“acceptCount”属性的连接。
max-connections: 8192
threads:
# 工作线程的最小数量,初始化时创建的线程数
min-spare: 10
# 工作线程的最大数量 io密集型建议10倍的cpu数,cpu密集型建议cpu数+1,绝大部分应用都是io密集型
max: 200
# 连接器在接受连接后等待显示请求 URI 行的时间。
connection-timeout: 20000
# 在关闭连接之前等待另一个 HTTP 请求的时间。如果未设置,则使用 connectionTimeout。设置为 -1 时不会超时。
keep-alive-timeout: 20000
# 在连接关闭之前可以进行流水线处理的最大HTTP请求数量。当设置为0或1时,禁用keep-alive和流水线处理。当设置为-1时,允许无限数量的流水线处理或keep-alive请求。
max-keep-alive-requests: 100
JDK 的线程池,是先使用核心线程数配置,接着使用队列长度,最后再使用最大线程配置。
Tomcat 的线程池,就是先使用核心线程数配置,再使用最大线程配置,最后才使用队列长度。
JDK 线程池流程:minThreads –> queue –> maxThreads –> Exception
Tomcat 增强后: minThreads –> maxThreads –> queue –> Exception
JDK 线程池架构图
Tomcat 线程架构
然后如果将 server.tomcat.max-connections=10
最大连接数,默认是8192,如果改成10个,最大请求就只有10个了。
因为这个配置是控制连接数量的,好比 NIO 里Socket的连接数量上限大小,一般情况下在NIO模式下,连接上限都是比线程池上限大的
当连接数大于 maxConnections+acceptCount + 1 时,新来的请求不会收到服务器拒绝连接响应,而是不会和新的请求进行 3 次握手建立连接,一段时间后(客户端的超时时间或者 Tomcat 的 20s 后)会出现请求连接超时。
但是当升级到Tomcat 10 开启虚拟线程
spring.threads.virtual.enabled=true
此时请求会突破最大线程数量的限制,启用虚拟线程,但是最大连接数还是会被限制住
Spring Boot配置MySQL5和MySQL8
这里做一个配置的记录
MySQL5
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.17</version>
<scope>runtime</scope>
</dependency>
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/onlineTest2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
spring.datasource.username=root
spring.datasource.password=xudong123456
MySQL8
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
<scope>runtime</scope>
</dependency>
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/onlineTest2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=xudong123456
MySQL8.0导出SQL转换为5.7的SQL
将MySQL8.0生成的sql文件,导入MySQL5.7版本时,出现了版本不兼容问题
将MySQL8.0导出的sql文件中,所有的utf8mb4_0900_ai_ci
替换为utf8_general_ci
,以及所有的utf8mb4
替换为utf8
Flyway配置MySQL
Flyway 8.2.1及以后版本不再支持MySQL
MySQL代码被提取出来作为插件,需要另外增加依赖
https://mvnrepository.com/artifact/org.flywaydb/flyway-mysql
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
</dependency>
目前支持mysql5.7的社区版为7.15.0,支持mysql8.0的版本是8.2.0,8.2.1移除了mysql支持
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>7.15.0</version>
</dependency>
Flyway 兼容 Mysql 5.7 的新发现(因为用的SpringBoot版本是 3.3.1 和旧版本的Flyway不兼容,只能用新的)
Flyway 在 10.x.x 之前的版本,不兼容mysql 5.7
10.x.x 重新兼容了低版本的数据库
要使用 10.x.x版本的Flyway,需要引入依赖
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
<version>10.13.0</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>10.13.0</version>
</dependency>
docker启动mysql无法输入中文以及中文不显示或乱码问题
这里无论是 mysql5 还是 mysql 8 都是这样的问题无法输入中文或者乱码,但是PG数据库是正常的。
乱码一般都是因为编码引起的,所以我们来查一下数据库的编码,默认都是没问题的
最后找到的原因是容器的编码为设置好
docker exec -it mysql env LANG=C.UTF-8 mysql -uroot -p
当加上 env LANG=C.UTF-8
这个环境变量就可以正常使用中文
MySQL 8.0 自定义配置文件
root@hcss-ecs-8d74:~# cat my.cnf
[mysqld]
#限制server接受的数据包大小
max_allowed_packet=200M
#最大连接数,不要盲目提高此值
max_connections=1024
#指定大小的内存来缓冲数据和索引,最大可以把该值设置成物理内存的80%
innodb_buffer_pool_size = 10G
innodb_buffer_pool_instances = 10
#此参数确定数据日志文件的大小,以为单位,根据据更新频率调整
innodb_log_file_size = 50M
key_buffer_size = 16M
#设置 sql_mode
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
group_concat_max_len = 1024000
#关闭严格模式
innodb_strict_mode = 0
innodb_file_per_table = ON
注意事项:
1、在MySQL 8.0版本中,由于初始化后无法更改lower_case_table_names
参数导致数据库表名大小写问题。官方文档指出该参数必须在服务器初始化时配置。针对此问题,解决方案是重新安装MySQL,并在初始化时指定lower_case_table_names
=1。
2、mysql8 启动报错:Error while setting value ‘STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DI
8.0以上已经取消了NO_AUTO_CREATE_USER
这个关键字,在mysql.ini
中的sql_mode
中删掉这个关键字即可
MySql 连接失败
MySQL 连接出现 Authentication plugin 'caching_sha2_password' cannot be loaded
很多用户在使用Navicat Premium 12连接MySQL数据库时会出现Authentication plugin 'caching_sha2_password' cannot be loaded
的错误。
出现这个原因是mysql8 之前的版本中加密规则是mysql_native_password
,而在mysql8之后,加密规则是caching_sha2_password
, 解决问题方法有两种,一种是升级navicat驱动,一种是把mysql用户登录密码加密规则还原成mysql_native_password
.
查看默认的加密方式;本地用户的加密方式
show variables like 'default_authentication_plugin';
select host,user,plugin from mysql.user;
修改登录用户的加密规则
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '密码';
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';
MySql 创建新的用户并赋予权限
如果你想允许从任何主机连接,可以将 'localhost'
替换为 '%'
:
CREATE USER 'newuser'@'%' IDENTIFIED BY 'password';
使用 GRANT
语句授予用户对特定数据库的权限。假设数据库名为 sjhl
。
同样,如果你想允许从任何主机连接,可以将 'localhost'
替换为 '%'
:
GRANT ALL PRIVILEGES ON sjhl.* TO 'newuser'@'%';
使用 FLUSH PRIVILEGES
语句使权限更改立即生效。
FLUSH PRIVILEGES;
# 登录 MySQL
mysql -u root -p
# 创建用户
CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
# 授予权限
GRANT ALL PRIVILEGES ON sjhl.* TO 'newuser'@'localhost';
# 应用权限更改
FLUSH PRIVILEGES;
# 验证权限
EXIT;
# 使用新用户登录
mysql -u newuser -p
# 尝试访问 sjhl 数据库
USE sjhl;
# 尝试访问其他数据库
USE otherdb;