SpringCache学习之操作redis

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 一、redis快速入门   1、redis简介   在java领域,常见的四大缓存分别是ehcache,memcached,redis,guava-cache,其中redis与其他类型缓存相比,有着得天独厚的优势: 它是基于内存的数据库,什么意思呢?由于受磁盘IO影响,它所有操作都在内存当中,用以提高性能,同时采用异步的方式将数据保存在硬盘当中。

一、redis快速入门

  1、redis简介

  在java领域,常见的四大缓存分别是ehcache,memcached,redis,guava-cache,其中redis与其他类型缓存相比,有着得天独厚的优势:

  1. 它是基于内存的数据库,什么意思呢?由于受磁盘IO影响,它所有操作都在内存当中,用以提高性能,同时采用异步的方式将数据保存在硬盘当中。
  2. 与memcached相比,redis支持多种数据类型,string,list,set,sorted set,hash。让我们使用起来更加灵活
  3. 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行  
  4. 丰富的特性:可用于缓存,消息,可以设置key的过期时间,过期后将会自动删除对应的记录 
  5. redis是单线程单进程的,它利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销。

 

 2、redis常见的数据类型

   2.1   redis的key:

     我们可以使用任何二进制序列当我们的键,过长过短的键都不建议使用,在这里建议大家使用尝试使用一个模式。例如,“object-type:id”是一个比较推荐的方式,举个例子,我们可以这样来定义键:“user:1000”。点或破折号通常用于多字字段。

    2.2  strings: 该类型是最基本的数据类型,也是最常见的数据类型。我们可以使用set 或get 来设置和获取对应的值:

> set mykey somevalue
OK
> get mykey
"somevalue"

    在这里面set 如果已经存在的key 会替换已有的值。注意value的值不要大于512M

    另外,我们可以使用mset 与 mget来设置和获取多个值,如下:

> mset a 10 b 20 c 30
OK
> mget a b c
1) "10"
2) "20"
3) "30"

    我们可以使用exists判断key对应的值是否存在,del则是删除key对应的值

> set mykey hello
OK
> exists mykey
(integer) 1
> del mykey
(integer) 1
> exists mykey
(integer) 0

     我们也可以指定key值对应的过期时间:(单位为秒)

> set key some-value
OK
> expire key 5
(integer) 1
> get key (immediately)
"some-value"
> get key (after some time)
(nil)

 

  2.3 lists类型:这个类似于我们的集合类型,可以存放多个值,我们可以使用lpush(在头部添加)与rpush(在尾部添加)来添加对应的元素:

  

 rpush mylist A
(integer) 1
> rpush mylist B
(integer) 2
> lpush mylist first
(integer) 3
> lrange mylist 0 -1
1) "first"
2) "A"
3) "B"

  其中lrange 是取一定范围的元素

 

  2.4 Hashes 这个类似于key->key ,value的形式,我们可以使用hmset与hget来取值

> hmset user:1000 username antirez birthyear 1977 verified 1
OK
> hget user:1000 username
"antirez"
> hget user:1000 birthyear
"1977"
> hgetall user:1000
1) "username"
2) "antirez"
3) "birthyear"
4) "1977"
5) "verified"
6) "1"

 

  2.5、SETS:存放了一系列无序的字符串集合,我们可以通过sadd与smembers来取值:

> sadd myset 1 2 3
(integer) 3
> smembers myset
1. 3
2. 1
3. 2

 

  3、redis中的事务

    MULTI 命令用于开启一个事务,它总是返回 OK 。 MULTI执行之后, 客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中, 当 EXEC命令被调用时, 所有队列中的命令才会被执行。redis中事务不存在回滚,我们这么操作试试:

localhost:0>multi
"OK"

localhost:0>set test abc
"QUEUED"

localhost:0>incr test
"QUEUED"

localhost:0>exec
 1)  "OK"
 2)  "ERR value is not an integer or out of range"
localhost:0>get test
"abc"

如果事务当中入队不成功,我们可以看一下例子:

localhost:0>multi
"OK"

localhost:0>set test abcd
"QUEUED"

localhost:0>test abc
"ERR unknown command 'test'"

localhost:0>exec
"EXECABORT Transaction discarded because of previous errors."

localhost:0>get test
"abc"

此时就会终止我们提交事务

另外我们调用 DISCARD , 客户端可以清空事务队列并放弃执行事务。例子:

localhost:0>multi
"OK"

localhost:0>set test abcd
"QUEUED"

localhost:0>discard
"OK"

localhost:0>get test
"abc"

 

4、远程连接redis注意事项

   redis现在的版本开启redis-server后,redis-cli只能访问到127.0.0.1,因为在配置文件中固定了ip,因此需要修改redis.conf(有的版本不是这个文件名,只要找到相对应的conf后缀的文件即可)文件以下几个地方。1、bind 127.0.0.1改为 #bind 127.0.0.1   2、protected-mode yes 改为 protected-mode no

     指定配置文件运行:

./redis-server ../redis.conf

 远程连接的命令:

 

./redis-cli -p 6379 -h xxx.xxx.xxx

 

二、使用SpringCache集成redis

1、关于SpringCache

  从SpringFramework3.1版本开始,Spring给我们提供了一系列注解和接口规范来简化我们操作缓存代码量,几个核心接口如下:

  CacheManager: 该接口主要作用是获取缓存和获取缓存名称

/*
 * Copyright 2002-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cache;

import java.util.Collection;

import org.springframework.lang.Nullable;

/**
 * Spring's central cache manager SPI.
 * Allows for retrieving named {@link Cache} regions.
 *
 * @author Costin Leau
 * @since 3.1
 */
public interface CacheManager {

    /**
     * Return the cache associated with the given name.
     * @param name the cache identifier (must not be {@code null})
     * @return the associated cache, or {@code null} if none found
     */
    @Nullable
    Cache getCache(String name);

    /**
     * Return a collection of the cache names known by this manager.
     * @return the names of all caches known by the cache manager
     */
    Collection<String> getCacheNames();

}
View Code

  在这里面最核心的接口当然是Cache:

/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cache;

import java.util.concurrent.Callable;

import org.springframework.lang.Nullable;

/**
 * Interface that defines common cache operations.
 *
 * <b>Note:</b> Due to the generic use of caching, it is recommended that
 * implementations allow storage of <tt>null</tt> values (for example to
 * cache methods that return {@code null}).
 *
 * @author Costin Leau
 * @author Juergen Hoeller
 * @author Stephane Nicoll
 * @since 3.1
 */
public interface Cache {

    /**
     * Return the cache name.
     */
    String getName();

    /**
     * Return the underlying native cache provider.
     */
    Object getNativeCache();

    /**
     * Return the value to which this cache maps the specified key.
     * <p>Returns {@code null} if the cache contains no mapping for this key;
     * otherwise, the cached value (which may be {@code null} itself) will
     * be returned in a {@link ValueWrapper}.
     * @param key the key whose associated value is to be returned
     * @return the value to which this cache maps the specified key,
     * contained within a {@link ValueWrapper} which may also hold
     * a cached {@code null} value. A straight {@code null} being
     * returned means that the cache contains no mapping for this key.
     * @see #get(Object, Class)
     */
    @Nullable
    ValueWrapper get(Object key);

    /**
     * Return the value to which this cache maps the specified key,
     * generically specifying a type that return value will be cast to.
     * <p>Note: This variant of {@code get} does not allow for differentiating
     * between a cached {@code null} value and no cache entry found at all.
     * Use the standard {@link #get(Object)} variant for that purpose instead.
     * @param key the key whose associated value is to be returned
     * @param type the required type of the returned value (may be
     * {@code null} to bypass a type check; in case of a {@code null}
     * value found in the cache, the specified type is irrelevant)
     * @return the value to which this cache maps the specified key
     * (which may be {@code null} itself), or also {@code null} if
     * the cache contains no mapping for this key
     * @throws IllegalStateException if a cache entry has been found
     * but failed to match the specified type
     * @since 4.0
     * @see #get(Object)
     */
    @Nullable
    <T> T get(Object key, @Nullable Class<T> type);

    /**
     * Return the value to which this cache maps the specified key, obtaining
     * that value from {@code valueLoader} if necessary. This method provides
     * a simple substitute for the conventional "if cached, return; otherwise
     * create, cache and return" pattern.
     * <p>If possible, implementations should ensure that the loading operation
     * is synchronized so that the specified {@code valueLoader} is only called
     * once in case of concurrent access on the same key.
     * <p>If the {@code valueLoader} throws an exception, it is wrapped in
     * a {@link ValueRetrievalException}
     * @param key the key whose associated value is to be returned
     * @return the value to which this cache maps the specified key
     * @throws ValueRetrievalException if the {@code valueLoader} throws an exception
     * @since 4.3
     */
    @Nullable
    <T> T get(Object key, Callable<T> valueLoader);

    /**
     * Associate the specified value with the specified key in this cache.
     * <p>If the cache previously contained a mapping for this key, the old
     * value is replaced by the specified value.
     * @param key the key with which the specified value is to be associated
     * @param value the value to be associated with the specified key
     */
    void put(Object key, @Nullable Object value);

    /**
     * Atomically associate the specified value with the specified key in this cache
     * if it is not set already.
     * <p>This is equivalent to:
     * <pre><code>
     * Object existingValue = cache.get(key);
     * if (existingValue == null) {
     *     cache.put(key, value);
     *     return null;
     * } else {
     *     return existingValue;
     * }
     * </code></pre>
     * except that the action is performed atomically. While all out-of-the-box
     * {@link CacheManager} implementations are able to perform the put atomically,
     * the operation may also be implemented in two steps, e.g. with a check for
     * presence and a subsequent put, in a non-atomic way. Check the documentation
     * of the native cache implementation that you are using for more details.
     * @param key the key with which the specified value is to be associated
     * @param value the value to be associated with the specified key
     * @return the value to which this cache maps the specified key (which may be
     * {@code null} itself), or also {@code null} if the cache did not contain any
     * mapping for that key prior to this call. Returning {@code null} is therefore
     * an indicator that the given {@code value} has been associated with the key.
     * @since 4.1
     */
    @Nullable
    ValueWrapper putIfAbsent(Object key, @Nullable Object value);

    /**
     * Evict the mapping for this key from this cache if it is present.
     * @param key the key whose mapping is to be removed from the cache
     */
    void evict(Object key);

    /**
     * Remove all mappings from the cache.
     */
    void clear();


    /**
     * A (wrapper) object representing a cache value.
     */
    @FunctionalInterface
    interface ValueWrapper {

        /**
         * Return the actual value in the cache.
         */
        @Nullable
        Object get();
    }


    /**
     * Wrapper exception to be thrown from {@link #get(Object, Callable)}
     * in case of the value loader callback failing with an exception.
     * @since 4.3
     */
    @SuppressWarnings("serial")
    class ValueRetrievalException extends RuntimeException {

        @Nullable
        private final Object key;

        public ValueRetrievalException(@Nullable Object key, Callable<?> loader, Throwable ex) {
            super(String.format("Value for key '%s' could not be loaded using '%s'", key, loader), ex);
            this.key = key;
        }

        @Nullable
        public Object getKey() {
            return this.key;
        }
    }

}
View Code

  该接口定义了操作缓存的最基本行为

  同时Spring给我们提供了一些默认的缓存实现 比如说:JDK java.util.concurrent.ConcurrentMap,ehcache2.x , Gemfire cache, guava等

2、几个重要的注解

   @EnableCaching:用于开启缓存注解,通常需要和@Configuration使用

   @Cacheable 该注解的意思为:读缓存中的值,如果没有该值,那么将执行注解对应的方法并将方法返回结果存入缓存

   使用示例:

@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
View Code

   有些时候我们希望根据条件将对应数据存入缓存,可以这么写:

@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback")
public Book findBook(String name)
View Code

   示例中:condition条件为true时,才存入缓存,unless属性是针对本次查询结果筛选。注意当表达式为true时的结果都不会存入缓存,  #result 代表了方法的返回结果

 

 

     @CacheEvict 触发Cache接口中的evict方法,相当于移除key对应的值

@CacheEvict(cacheNames="books", allEntries=true,beforeInvocation=true)
public void loadBooks(InputStream batch)
View Code

  beforeInvocation属性表明是否在执行方法之前清除缓存,allEntries属性会删除缓存下的所有数据,另外如果该值为true时就不能和key同时使用了

 

    @CachePut 执行方法并更新缓存

@CachePut(cacheNames="book", key="#isbn")
public Book updateBook(ISBN isbn, BookDescriptor descriptor)
View Code

   这些缓存注解的幕后英雄当然是我们的AOP啦

3、缓存注解中的SpringEL表达式

  springEL可以运用在我们的cache注解当中,比如说其中key属性 condition属性等,其中#result只能在unless属性中使用这点比较特殊,在这里我贴出官网的列表:

Description Example

The name of the method being invoked

#root.methodName

The method being invoked

#root.method.name

The target object being invoked

#root.target

The class of the target being invoked

#root.targetClass

The arguments (as array) used for invoking the target

#root.args[0]

Collection of caches against which the current method is executed

#root.caches[0].name

Name of any of the method arguments. If for some reason the names are not available (e.g. no debug information), the argument names are also available under the #a<#arg> where #arg stands for the argument index (starting from 0).

#iban or #a0 (one can also use #p0 or #p<#arg>notation as an alias).

The result of the method call (the value to be cached). Only available in unlessexpressions, cache putexpressions (to compute the key), or cache evictexpressions (when beforeInvocation is false). For supported wrappers such as Optional#resultrefers to the actual object, not the wrapper.

#result

4、代码集成示例

该代码是基于spring与hibernate集成,请参考:这里

gradle配置:

compile 'org.springframework.data:spring-data-redis:2.0.6.RELEASE'

    // https://mvnrepository.com/artifact/redis.clients/jedis
    compile group: 'redis.clients', name: 'jedis', version: '2.9.0'

// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
    compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.5'

// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
    compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.5'
View Code

 

1.创建RedisConfig:

package com.bdqn.lyrk.ssh.study.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

@Configuration
@EnableCaching
public class RedisConfig {

    /**
     * 创建redisConnectionFactory
     * @return
     */
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        /*
            设置相关配置
         */
        RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
        configuration.setPort(6379);
        configuration.setHostName("localhost");
        configuration.setDatabase(0);
        JedisConnectionFactory jedisConnectionFactory =
                new JedisConnectionFactory(configuration);
        return jedisConnectionFactory;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate() {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
        return stringRedisTemplate;
    }


    @Bean
    public RedisCacheManager cacheManager() {
        /*
            配置json序列化方式
         */
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().
                serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
                        new GenericJackson2JsonRedisSerializer()));
        return RedisCacheManager.builder(redisConnectionFactory()).
                transactionAware().cacheDefaults(cacheConfiguration).build();
    }
}
View Code

2.创建StudentService

package com.bdqn.lyrk.ssh.study.service;

import com.bdqn.lyrk.ssh.study.entity.StudentEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class StudentService {

    @Autowired
    private HibernateTemplate hibernateTemplate;

    @Transactional
    public int save(StudentEntity studentEntity) {
        hibernateTemplate.save(studentEntity);
        return studentEntity.getId();
    }

    @Cacheable(key = "#id", cacheNames = "student", cacheManager = "cacheManager", condition = "#id==1")
    public StudentEntity listStudents(Integer id) {
        return hibernateTemplate.get(StudentEntity.class, id);
    }

    @Transactional
    @CachePut(key = "#id", cacheNames = "student", cacheManager = "cacheManager")
    public StudentEntity updateStudent(Integer id) {
        StudentEntity studentEntity = new StudentEntity();
        studentEntity.setId(id);
        studentEntity.setStuName("tests");
        studentEntity.setPassword("password");
        hibernateTemplate.update(studentEntity);
        return studentEntity;
    }

    @CacheEvict(key = "#id", cacheNames = "student")
    @Transactional
    public StudentEntity deleteStudent(Integer id) {
        StudentEntity studentEntity = hibernateTemplate.load(StudentEntity.class, id);
        hibernateTemplate.delete(studentEntity);
        return studentEntity;
    }
}
View Code

3.Main方法:

package com.bdqn.lyrk.ssh.study;

import com.bdqn.lyrk.ssh.study.config.AppConfig;
import com.bdqn.lyrk.ssh.study.service.StudentService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.io.IOException;

public class Main {

    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext applicationContext =
                new AnnotationConfigApplicationContext(AppConfig.class);
        StudentService studentService =
                applicationContext.getBean(StudentService.class);
        studentService.updateStudent(1);
        System.out.println(studentService.listStudents(1).getStuName());
        studentService.deleteStudent(1);
    }
}
View Code

运行结果:

 

我们可以看到:第二次查询时并没有输出Hibernate的执行的sql,说明这个是从redis缓存中读取的。 

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
NoSQL 数据可视化 Linux
redis学习四、可视化操作工具链接 centos redis,付费Redis Desktop Manager和免费Another Redis DeskTop Manager下载、安装
本文介绍了Redis的两个可视化管理工具:付费的Redis Desktop Manager和免费的Another Redis DeskTop Manager,包括它们的下载、安装和使用方法,以及在使用Another Redis DeskTop Manager连接Redis时可能遇到的问题和解决方案。
116 1
redis学习四、可视化操作工具链接 centos redis,付费Redis Desktop Manager和免费Another Redis DeskTop Manager下载、安装
|
1月前
|
NoSQL Linux Redis
Docker学习二(Centos):Docker安装并运行redis(成功运行)
这篇文章介绍了在CentOS系统上使用Docker安装并运行Redis数据库的详细步骤,包括拉取Redis镜像、创建挂载目录、下载配置文件、修改配置以及使用Docker命令运行Redis容器,并检查运行状态和使用Navicat连接Redis。
225 3
|
1月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
26 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
1月前
|
存储 Prometheus NoSQL
大数据-44 Redis 慢查询日志 监视器 慢查询测试学习
大数据-44 Redis 慢查询日志 监视器 慢查询测试学习
24 3
|
5月前
|
NoSQL Java Redis
Redis系列学习文章分享---第十八篇(Redis原理篇--网络模型,通讯协议,内存回收)
Redis系列学习文章分享---第十八篇(Redis原理篇--网络模型,通讯协议,内存回收)
83 0
|
5月前
|
存储 消息中间件 缓存
Redis系列学习文章分享---第十七篇(Redis原理篇--数据结构,网络模型)
Redis系列学习文章分享---第十七篇(Redis原理篇--数据结构,网络模型)
97 0
|
1月前
|
NoSQL 关系型数据库 MySQL
Redis 事务特性、原理、具体命令操作全方位诠释 —— 零基础可学习
本文全面阐述了Redis事务的特性、原理、具体命令操作,指出Redis事务具有原子性但不保证一致性、持久性和隔离性,并解释了Redis事务的适用场景和WATCH命令的乐观锁机制。
200 0
Redis 事务特性、原理、具体命令操作全方位诠释 —— 零基础可学习
|
1月前
|
NoSQL Redis
redis学习五、错误总结,redis正常运行时后会出现一些bug 总结。
本文介绍了Redis在正常运行时可能遇到的一个错误,即无法进行磁盘持久化的问题,并提供了通过设置`stop-writes-on-bgsave-error`为`no`来解决这一问题的方案。
94 0
|
3月前
|
缓存 NoSQL Java
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
Spring Cache 是 Spring 提供的简易缓存方案,支持本地与 Redis 缓存。通过添加 `spring-boot-starter-data-redis` 和 `spring-boot-starter-cache` 依赖,并使用 `@EnableCaching` 开启缓存功能。JetCache 由阿里开源,功能更丰富,支持多级缓存和异步 API,通过引入 `jetcache-starter-redis` 依赖并配置 YAML 文件启用。Layering Cache 则提供分层缓存机制,需引入 `layering-cache-starter` 依赖并使用特定注解实现缓存逻辑。
947 1
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
|
3月前
|
缓存 NoSQL 关系型数据库
Redis学习总结
Redis学习总结
37 1