Spring Boot数据——Redis2

本文探讨了EhCache和Redis在缓存解决方案中的角色,分析了两者在进程内和集中式缓存上的区别,以及JedisPool在Redis资源管理中的优化策略。深入讨论了JedisPool参数设置对应用性能的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Redis集中式缓存与EhCache进程内缓存区别

  1. 由于EhCache是进程内的缓存框架,在集群模式下时,各应用服务器之间的缓存都是独立的,因此在不同服务器的进程间会存在缓存不一致的情况。即使EhCache提供了集群环境下的缓存同步策略,但是同步依然需要一定的时间,短暂的缓存不一致依然存在。在一些要求高一致性(任何数据变化都能及时的被查询到)的系统和应用中,就不能再使用EhCache来解决了,这个时候使用集中式缓存是个不错的选择。

  2. EhCache优点:因为其实纯Java的进程内缓存框架,具有快速、精干等特点,可以通过注解配置方式低侵入的给原有Spring应用增加缓存功能,提高数据访问性能。

EhCache的集成方法:
1> pom引入ehcache依赖
2> src/main/resources目录下创建:ehcache.xml,配置ehcache属性

	<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd">
	    <cache name="users"
	           maxEntriesLocalHeap="200"
	           timeToLiveSeconds="600">
	    </cache>
	</ehcache>

3> 在代码中加入缓存注解
@EnableCaching、@CacheConfig、@Cacheable等,使用方法与Redis同,不做赘述。

2. JedisPool资源池优化

1> 什么是JedisPool
Redis作为缓存数据库理论上和MySQL一样需要客户端和服务端建立起来连接进行相关操作,使用MySQL的时候相信大家都会使用一款开源的连接池,例如C3P0.因为直连会消耗大量的数据库资源,每一次新建一个连接之,使用后再断开连接,对于频繁访问的场景,这显然不是高效的。

生产环境一般使用连接池的方式对Redis连接进行管理,所有Jedis对象先放在池子中每一次需要的时候连接Redis,只需要在池子中借,用完了再归还给池子。

2>JedisPool优点
客户端连接Redis使用的是TCP协议,直连的方式每次需要建立TCP连接,而连接池的方式是可以预先初始化好Jedis连接,所以每次只需要从Jedis连接池借用即可,而借用和归还操作是在本地进行的,只有少量的并发同步开销,远远小于新建TCP连接的开销。另外直连的方式无法限制Jedis对象的个数,在极端情况下可能会造成连接泄露,而连接池的形式可以有效的保护和控制资源的使用。

3>Jedis直连与JedisPool比较
但是直连的方式也并不是一无是处,下面给出两种方式各自的优劣势。

优点缺点
直连简单方便,适用于少量长期连接的场景1.存在每次新建/关闭TCP连接开销;2.资源无法控制,极端情况会泄露连接;3.Jedis对象线程不安全
JedisPool1.无需每次生成Jedis对象,降低开销;2.使用连接池形式保护和控制资源使用相对于直连使用麻烦,资源管理上需要很多参数保证,一旦规划不合理会出很多问题

4> 书接上文,该如何避免在规划不合理情况下出现的问题
1. 在pom文件中添加依赖

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
    <scope>compile</scope>
</dependency>

2.jedisPool 参数设置
Jedis使用apache commons-pool2对Jedis资源池进行管理,所以在定义JedisPool时一个很重要的参数就是资源池GenericObjectPoolConfig,使用方式如下,其中有很多资源管理和使用的参数。

GenericObjectPoolConfig jedisPoolConfig = new GenericObjectPoolConfig();
jedisPoolConfig.setMaxTotal(..);
jedisPoolConfig.setMaxIdle(..);
jedisPoolConfig.setMinIdle(..);
jedisPoolConfig.setMaxWaitMillis(..);
...

3. 参数设置建议,此处最重要

JedisPool保证资源在一个可控范围内,并且提供了线程安全,但是一个合理的GenericObjectPoolConfig配置能为应用使用Redis保驾护航,下面将对它的一些重要参数进行说明和建议:

在当前环境下,Jedis连接就是资源,JedisPool管理的就是Jedis连接。

- 常规参数设置

1> blockWhenExhausted 当资源池用尽后,调用者是否要等待。只有当为true时,下面的maxWaitMillis才会生效,建议使用默认值true;

2> maxWaitMillis 当资源池连接用尽后,调用者的最大等待时间(单位为毫秒) 建议根据系统自行设置,默认值-1表示永不超时

3> testOnBorrow 借用链接时的有效性检查(ping),无效链接会被移除,业务量大情况下建议使用默认值false,减少一次ping开销

4> testOnReturn 向资源池归还链接时的有效性检查(ping),无效链接会被移除,业务量大情况下建议使用默认值false,减少一次ping开销

5> jmxEnabled 是否开启jmx监控,可用于监控,建议设置为true开启,但应用本身也要开启

- 空闲资源监控设置

为了方便使用,Jedis提供了JedisPoolConfig,它本身继承了GenericObjectPoolConfig设置了一些空闲监测设置,所以在开发过程中建议用JedisPoolConfig代替GenericObjectPoolConfig使用

空闲Jedis对象检测,下面四个参数组合来完成,testWhileIdle是该功能的开关。

public class JedisPoolConfig extends GenericObjectPoolConfig {
  public JedisPoolConfig() {
    // defaults to make your life with connection pool easier :)
    setTestWhileIdle(true);
    //
    setMinEvictableIdleTimeMillis(60000);
    //
    setTimeBetweenEvictionRunsMillis(30000);
    setNumTestsPerEvictionRun(-1);
    }
}

1> testWhileIdle 是否开启空闲资源监测,建议使用true开启

2> timeBetweenEvictionRunsMillis 空闲资源的检测周期(单位为毫秒) 建议设置,周期根据系统自行选择

3> minEvictableIdleTimeMillis 资源池中资源最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除 可根据自身业务决定,大部分使用默认值30分钟即可

4>numTestsPerEvictionRun 做空闲资源检测时,每次的采样数 可根据自身应用连接数进行微调,建议设置为-1,就是对所有连接做空闲监测

- 最重要的参数设置资源池大小(maxTotal)、空闲(maxIdle minIdle)设置建议,这才是最重要的!

1> maxTotal:最大连接数

实际上这个是一个很难回答的问题,考虑的因素比较多:
a. 业务希望Redis并发量
b. 客户端执行命令时间
c. Redis资源:例如 nodes(例如应用个数) * maxTotal 是不能超过redis的最大连接数。
d. 资源开销:例如虽然希望控制空闲连接,但是不希望因为连接池的频繁释放创建连接造成不必靠开销。

以一个例子说明maxTotal理论值计算方法,假设:
一次命令时间(borrow|return resource + Jedis执行命令(含网络) )的平均耗时约为1ms,一个连接的QPS大约是1000
业务期望的QPS是50000
那么理论上需要的资源池大小是50000 / 1000 = 50个。但事实上这是个理论值,还要考虑到要比理论值预留一些资源,通常来讲maxTotal可以比理论值大一些。

但这个值不是越大越好,一方面连接太多占用客户端和服务端资源,另一方面对于Redis这种高QPS的服务器,一个大命令的阻塞即使设置再大资源池仍然会无济于事。

2> maxIdle minIdle

maxIdle 资源池允许最大空闲的连接数;minIdle 资源池确保最少空闲的连接数;

maxIdle实际上才是业务需要的最大连接数,maxTotal是为了给出余量,所以maxIdle不要设置过小,否则会有new Jedis(新连接)开销,而minIdle是为了控制空闲资源监测。

连接池的最佳性能是maxTotal = maxIdle ,这样就避免连接池伸缩带来的性能干扰。但是如果并发量不大或者maxTotal设置过高,会导致不必要的连接资源浪费。
可以根据实际总OPS和调用redis客户端的规模整体评估每个节点所使用的连接池。

3. 资源池常见问题

常见问题1、2,第一种是链接超时,第二种是在资源连接池资源耗尽后没有设置等待时间,也就是blockWhenExhausted设置为了false

遇到此类异常,不要盲目的认为资源池不够大,第三节已经进行了分析。具体原因可以排查:网络、资源池参数设置、资源池监控(如果对jmx监控)、代码(例如没执行jedis.close())、慢查询、DNS等问题。

常见问题3 项目启动时链接超时解决办法 —— 预热JedisPool

由于一些原因(例如超时时间设置较小原因),有的项目在启动成功后会出现超时。JedisPool定义最大资源数、最小空闲资源数时,不会真的把Jedis连接放到池子里,第一次使用时,池子没有资源使用,会new Jedis,使用后放到池子里,可能会有一定的时间开销,所以也可以考虑在JedisPool定义后,为JedisPool提前进行预热,例如以最小空闲数量为预热数量如下:

List<Jedis> minIdleJedisList = new ArrayList<Jedis>(jedisPoolConfig.getMinIdle());

for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) {
    Jedis jedis = null;
    try {
        jedis = pool.getResource();
        minIdleJedisList.add(jedis);
        jedis.ping();
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    } finally {
    }
}

for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) {
    Jedis jedis = null;
    try {
        jedis = minIdleJedisList.get(i);
        jedis.close();
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    } finally {
   
    }
}

强调一下jedis.close();不是关闭链接 是放回池子里了

<think>我们讨论的是如何在IntelliJIDEA安装和配置两个(或多个)JDK。注意:这里指的是在IDEA中配置多个JDK,以便在不同的项目中或者同一个项目中切换使用。实际上,安装JDK是在操作系统层面进行的,而IDEA只是使用这些JDK。因此,步骤包括:1.在操作系统上安装两个(或多个)JDK。2.在IDEA中配置这些JDK,使得项目可以选用不同的JDK。下面详细说明:第一步:安装多个JDK在操作系统上分别安装两个JDK,比如JDK8和JDK11。注意记录它们的安装路径(例如,在Windows上可能是C:\ProgramFiles\Java\jdk1.8.0_301和C:\ProgramFiles\Java\jdk-11.0.12)。第二步:在IDEA中配置JDK1.打开IDEA,进入全局设置(对于新版本,在欢迎界面点击Customize->Allsettings;或者在一个项目中,点击File->Settings)。2.在设置窗口中,找到Build,Execution,Deployment->BuildTools->Gradle(如果你使用Gradle)或者直接找到Build,Execution,Deployment->Compiler->JavaCompiler,但我们这里需要配置SDK,所以更一般的步骤是:在设置窗口中,找到Build,Execution,Deployment->BuildTools->Maven->Runner(如果你使用Maven)?不对,配置JDK的位置在:实际上,在IDEA中配置多个JDK是在PlatformSettings->SDKs中。具体步骤:-在欢迎界面(或没有项目打开时):点击右下角的Configure->ProjectDefaults->ProjectStructure。或者,打开任意项目后,按Ctrl+Alt+Shift+S(或者点击File->ProjectStructure)。-在ProjectStructure对话框中,左侧选择PlatformSettings下的SDKs。-在右侧,点击“+”号,选择AddJDK。-浏览并选择你安装的JDK目录(比如第一个JDK8的路径),然后确定。-同样的步骤,再次点击“+”号,添加第二个JDK(比如JDK11)。这样,你就有了两个JDK在IDEA中可用。第三步:在项目中选择JDK在ProjectStructure对话框中,选择ProjectSettings->Project。在ProjectSDK下拉框中,选择你希望当前项目使用的JDK。你也可以在ProjectSettings->Modules->Sources选项卡中,为每个模块单独设置JDK(通常不需要,除非项目中有多个模块需要不同的JDK)。另外,你还可以为不同的运行配置(Run/DebugConfigurations)指定不同的JDK?但是注意,运行配置通常使用项目级别的JDK,但也可以覆盖:在运行配置中,有一个JRE选项,可以选择不同的JDK(或者模块的JDK,或者项目JDK,或者指定一个)。重要:在同一个项目中,编译和运行的JDK通常是一致的(通过项目级别的设置)。如果要同时运行两个需要不同JDK的项目,那么分别打开两个项目,每个项目设置自己的JDK即可。注意:有时候可能需要设置JAVA_HOME环境变量,但这是系统环境变量,通常指向一个默认的JDK。在IDEA中,我们通过上述方式覆盖了系统设置,因此项目使用的是IDEA中指定的JDK。总结:1.安装多个JDK到系统中。2.在IDEA的PlatformSettings中,添加所有需要的JDK。3.在每个项目中,指定使用哪个JDK。现在,回答完毕。解释:为什么需要配置多个JDK?因为不同的项目可能需要不同版本的JDK。例如,一些老项目使用JDK8,新项目可能使用JDK11。IDEA允许我们管理多个JDK,并在项目间切换。最后,按照要求提出相关问题:§§相关问题§§:1.如何在IDEA中为不同的模块设置不同的JDK?2.如何检查当前项目在IDEA中使用的JDK版本?3.在IDEA中运行项目时,如何指定特定的JDK?4.如果项目中使用了Maven,如何配置Maven项目使用特定的JDK?</think> ### 如何在IDEA中配置和使用多个JDK #### **步骤详解:** 1. **安装多个JDK到系统** - 下载不同版本的JDK(如JDK 8、JDK 11)并安装到本地目录。 - **示例路径**: - JDK 8: `C:\Program Files\Java\jdk1.8.0_301` - JDK 11: `C:\Program Files\Java\jdk-11.0.15` 2. **在IDEA中添加多个JDK** - 打开IDEA → **File → Project Structure**(或快捷键 `Ctrl+Alt+Shift+S`)。 - 左侧导航栏选择 **Platform Settings → SDKs**。 - 点击 **+** 按钮 → 选择 **Add JDK** → 浏览并添加不同版本的JDK安装目录。 ![添加多个JDK示意图](https://example.com/sdk_add.png) *(注:示意图路径仅为示例)* 3. **为项目指定JDK** - 在 **Project Structure** 窗口中: - 左侧选择 **Project Settings → Project** - 在 **Project SDK** 下拉菜单中选择当前项目所需的JDK版本。 4. **为模块指定独立的JDK**(可选) - 在 **Project Structure** 窗口中: - 左侧选择 **Project Settings → Modules** - 选择具体模块 → 在 **Dependencies** 标签页 → 修改 **Module SDK**。 --- #### **验证配置** 执行以下代码检查当前使用的JDK版本: ```java public class JdkVersionCheck { public static void main(String[] args) { System.out.println("当前JDK版本: " + System.getProperty("java.version")); System.out.println("JDK安装路径: " + System.getProperty("java.home")); } } ``` **输出示例**: ``` 当前JDK版本: 11.0.15 JDK安装路径: C:\Program Files\Java\jdk-11.0.15 ``` --- ### **为何需要多个JDK?** 1. **项目兼容性**:老旧项目可能依赖JDK 8,而新项目需要JDK 11+的特性。 2. **特性隔离**:避免高版本JDK的语法(如`var`)在低版本环境中报错。 3. **测试覆盖**:验证代码在不同JDK下的行为(如模块化支持、GC算法差异)。 --- ### **常见问题解决** | 问题现象 | 解决方法 | |---------|----------| | 编译报错`无效的目标发行版` | 在 **Project Structure → Project → Language level** 中选择与JDK匹配的版本 | | Maven项目JDK不生效 | 在`pom.xml`中配置`maven-compiler-plugin`:<br>```xml<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.11.0</version><configuration><source>11</source><target>11</target></configuration></plugin></plugins></build>``` | --- ### **注意事项** 1. 环境变量`JAVA_HOME`通常指向默认JDK,但IDEA优先使用项目配置的JDK。 2. 使用版本管理工具(如SDKMAN!或Jabba)可简化多JDK切换。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值