RedisTemplate访问Redis的更好方法

目录
  • 开始准备
  • RedisTemplate
  • JPA Repository
  • Cache
  • 总结

开始准备

开始之前我们需要有Redis安装,我们采用本机Docker运行Redis, 主要命令如下

docker pull redis
docker run --name my_redis -d -p 6379:6379 redis
docker exec -it my_redis bash
redis-cli

前面两个命令是启动redis docker, 后两个是连接到docker, 在使用redis-cli 去查看redis里面的内容,主要查看我们存在redis里面的数据。

RedisTemplate

我们先从RedisTemplate开始,这个是最好理解的一种方式,我之前在工作中也使用过这种方式,先看代码示例 我们先定义一个POJO类

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Book implements Serializable {
    private Long id;
    private String name;
    private String author;
}

一个很简单的BOOK类,三个字段: id,name和author. 再来一个RedisTemplate的Bean

    @Bean
    public RedisTemplate<String, Book> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Book> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

再定义一个使用这个RedisTemplate的Service类

    public Optional<Book> findOneBook(String name) {
        HashOperations<String, String, Book> hashOperations = redisTemplate.opsForHash();
        if (redisTemplate.hasKey(CACHE) && hashOperations.hasKey(CACHE, name)) {
            log.info("Get book {} from Redis.", name);
            return Optional.of(hashOperations.get(CACHE, name));
        }
        Optional<Book> book = bookRepository.getBook(name);
        log.info("Book Found: {}", book);
        if (book.isPresent()) {
            log.info("Put book {} to Redis.", name);
            hashOperations.put(CACHE, name, book.get());
            redisTemplate.expire(CACHE, 10, TimeUnit.MINUTES);
        }
        return book;
    }

我们使用Hash来存储这个Book信息,在上面的方法中查找书名存不存在Redis中,如果存在就直接返回,如果不存在就去持久化存储中找,找到就再通过Template写入到Redis中, 这是缓存的通用做法。 使用起来感觉很方便。 我们这里为了简单没有使用持久化存储,就硬编码了几条数据, 代码如下

@Repository
public class BookRepository {
    Map<String, Book> bookMap = new HashMap<>();
    public BookRepository(){
        bookMap.put("apache kafka", Book.builder()
                .name("apache kafka").id(1L).author("zhangsan")
                .build());
        bookMap.put("python", Book.builder()
                .name("python").id(2L).author("lisi")
                .build());
    }
    public Optional<Book> getBook(String name){
        if(bookMap.containsKey(name)){
            return Optional.of(bookMap.get(name));
        }
        else{
            return Optional.empty();
        }
    }
}

我们调用 bookService.findOneBook("python")和bookService.findOneBook("apache kafka"); 来把数据写入到换存中

我们来看下存储在Redis的数据长什么样子。

127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04book"
127.0.0.1:6379> type "\xac\xed\x00\x05t\x00\x04book"
hash
127.0.0.1:6379> hgetall "\xac\xed\x00\x05t\x00\x04book"
1) "\xac\xed\x00\x05t\x00\x06python"
2) "\xac\xed\x00\x05sr\x00&com.ken.redistemplatesample.model.Book=\x19\x96\xfb\x7f\x7f\xda\xbe\x02\x00\x03L\x00\x06authort\x00\x12Ljava/lang/String;L\x00\x02idt\x00\x10Ljava/lang/Long;L\x00\x04nameq\x00~\x00\x01xpt\x00\x04lisisr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x00\x00\x00\x00\x02t\x00\x06python"
3) "\xac\xed\x00\x05t\x00\x0capache kafka"
4) "\xac\xed\x00\x05sr\x00&com.ken.redistemplatesample.model.Book=\x19\x96\xfb\x7f\x7f\xda\xbe\x02\x00\x03L\x00\x06authort\x00\x12Ljava/lang/String;L\x00\x02idt\x00\x10Ljava/lang/Long;L\x00\x04nameq\x00~\x00\x01xpt\x00\bzhangsansr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x00\x00\x00\x00\x01t\x00\x0capache kafka"

我们可以看到数据被存在了key是“\xac\xed\x00\x05t\x00\x04book”的一个Hash表中, Hash里面有两条记录。 大家发现一个问题没有? 就是这个key不是我们想象的用“book”做key,而是多了一串16进制的码, 这是因为RedisTemplate使用了默认的JdkSerializationRedisSerializer 去序列化我们的key和value, 如果大家都用Java语言那没有问题, 如果有人用Java语言写,有人用别的语言读,那就有问题,就像我开始的时候用hgetall "book"始终拿不到数据那样。 RedisTemplate也提供了StringRedisTemplate来方便大家需要使用String来序列化redis里面的数据。简单看下代码

    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
    {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
    public Optional<String> getBookString(String name){
        HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
        if (stringRedisTemplate.hasKey(STRINGCACHE) && hashOperations.hasKey(STRINGCACHE, name)) {
            log.info("Get book {} from Redis.", name);
            return Optional.of(hashOperations.get(STRINGCACHE, name));
        }
        Optional<Book> book = bookRepository.getBook(name);
        log.info("Book Found: {}", book);
        if (book.isPresent()) {
            log.info("Put book {} to Redis.", name);
            hashOperations.put(STRINGCACHE, name, book.get().getAuthor());
            stringRedisTemplate.expire(STRINGCACHE, 10, TimeUnit.MINUTES);
            return Optional.of(book.get().getAuthor());
        }
        return Optional.empty();
    }

使用上就没有那么方便,你就得自己写需要存的是哪个字段,读出来是哪个字段。

127.0.0.1:6379> keys *
1) "string_book"
127.0.0.1:6379> hgetall string_book
1) "python"
2) "lisi"
3) "apache kafka"
4) "zhangsan"

如上图所示,使用客户端读出来看起来就比较清爽一些。也可以看到占用的Size会小很多,我们这个例子相差7倍,如果是数据量大,这个还是比较大的浪费。

127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04book"
2) "string_book"
127.0.0.1:6379> memory usage "string_book"
(integer) 104
127.0.0.1:6379> memory usage "\xac\xed\x00\x05t\x00\x04book"
(integer) 712

JPA Repository

我们知道使用JPA Repository来访问DataBase的时候,增删改查那样的操作能够很方便的实现,基本就是定义个接口,代码都不用写,Spring就帮我们完成了大部分的工作,那么访问Redis是不是也可以这样呢? 答案是肯定的,我们来看代码 首先我们还是定义一个POJO

@RedisHash(value = "cache-book", timeToLive = 600)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CacheBook implements Serializable {
    @Id
    private Long userId;
    @Indexed
    private String name;
    private String author;
}

这个类与我们上面template上面的类的区别就是我们加了两个注解, 在类开头加了 @RedisHash(value = "cache-book", timeToLive = 600) 在字段上面加了@Id和@Indexed 定义一个Repository的接口

public interface CacheBookRepository extends CrudRepository<CacheBook, Long> {
    Optional<CacheBook> findOneByName(String name);
}

再定义一个service和上面那个例子template一样,缓存中有就到缓存中拿,没有就到持久化存储中找,并写入缓存

@Slf4j
@Service
public class BookService {
    private static final String CACHE = "repository-book";
    @Autowired
    private CacheBookRepository cacheRepository;
    @Autowired
    private BookRepository bookRepository;
    public Optional<CacheBook> findOneBook(String name) {
        Optional<CacheBook> optionalCacheBook = cacheRepository.findOneByName(name);
        if(!optionalCacheBook.isPresent())
        {
            Optional<CacheBook> book = bookRepository.getBook(name);
            log.info("Book Found: {}", book);
            if (book.isPresent()) {
                log.info("Put book {} to Redis.", name);
                cacheRepository.save(book.get());
            }
            return book;
        }
        return optionalCacheBook;
    }
}

代码很简单,简单到不敢相信是真的。 还是一样,调用这个方法,我们来看存在Redis里面的数据

127.0.0.1:6379> keys *
1) "repository-book:2"
2) "repository-book:2:idx"
3) "repository-book"
4) "repository-book:name:apache kafka"
5) "repository-book:name:python"
6) "repository-book:1:idx"
7) "repository-book:1"

哇,感觉存的内容有些多, 不用怕我们来看下各自存什么数据 首先看最短的一个

127.0.0.1:6379> smembers repository-book
1) "1"
2) "2"

它里面存的是我们的id所有的value, 可以用来判断id是否存在 再来看

127.0.0.1:6379> hgetall repository-book:2
1) "_class"
2) "com.ken.redisrepositorysample.model.CacheBook"
3) "author"
4) "lisi"
5) "name"
6) "python"
7) "userId"
8) "2"

这个是我们数据存放的地方

127.0.0.1:6379> smembers repository-book:1:idx
1) "repository-book:name:apache kafka"
127.0.0.1:6379> smembers "repository-book:name:apache kafka"
1) "1"

另外两个都是set, 存在在它们里面的数据是索引信息。 由此可以看出通过JPA Repository 的方式,代码很少,而且存储的数据也很通用,个人觉得是比较理想的访问方法。

Cache

我们已经看了两种方式,在访问的时候遵循这样的模式:缓存中有就从缓存中返回,没有就从持久化存储中找,然后写入缓存,这部分代码我也不想自己写,那么Cache就是你的救星。 我们先看代码 我们这次使用内存数据库H2作为持久化存储, 放一个schema.sql在resouces下面

drop table t_book if exists;
create table t_book (
    id bigint auto_increment,
    create_time timestamp,
    update_time timestamp,
    name varchar(255),
    author varchar(200),
    primary key (id)
);
insert into t_book (name, author, create_time, update_time) values ('python', 'zhangsan', now(), now());
insert into t_book (name, author, create_time, update_time) values ('hadoop', 'lisi', now(), now());
insert into t_book (name, author, create_time, update_time) values ('java', 'wangwu', now(), now());

然后定义POJO

@Entity
@Table(name = "T_BOOK")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CacheBook implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String author;
    @Column(updatable = false)
    @CreationTimestamp
    private Date createTime;
    @UpdateTimestamp
    private Date updateTime;
}

完全是和数据库绑定的代码,和缓存没有任何关系 一个Repository来访问数据库

public interface BookRepository extends JpaRepository<CacheBook, Long> {
}

定义一个service来调用它

@Slf4j
@Service
@CacheConfig(cacheNames = "cache-book")
public class BookService {
    @Autowired
    private BookRepository bookRepository;
    @Cacheable
    public List<CacheBook> findAllCoffee() {
        return bookRepository.findAll();
    }
    @CacheEvict
    public void reloadCoffee() {
    }
}

这里就比较关键了,在类上加上了注解 @CacheConfig(cacheNames = "cache-book") 在方法上面加上了Cacheable和CacheEvict, Cacheable这个方法就是用来实现逻辑,有就从缓存中拿,没有就从数据库拿的,CacheEvict是调用这个方法的时候清除缓存。 然后再启动入口程序的地方加上注解 @EnableJpaRepositories @EnableCaching(proxyTargetClass = true) 在配置文件application.properties中加上

spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
management.endpoints.web.exposure.include=*
spring.cache.type=redis
spring.cache.cache-names=cache-book
spring.cache.redis.time-to-live=600000
spring.cache.redis.cache-null-values=false
spring.redis.host=localhost

这样就可以了, 感觉就是通过配置下就把缓存给完成了,非常的简单 我们来看Redis中是怎么存的

127.0.0.1:6379> keys *
1) "cache-book::SimpleKey []"
127.0.0.1:6379> get "cache-book::SimpleKey []"
"\xac\xed\x00\x05sr\x00\x13java.util.ArrayListx\x81\xd2\x1d\x99\xc7a\x9d\x03\x00\x01I\x00\x04sizexp\x00\x00\x00\x03w\x04\x00\x00\x00\x03sr\x00(com.ken.rediscachesample.model.CacheBook\xec\xcbR=\xe1U\x9b\xf7\x02\x00\x05L\x00\x06authort\x00\x12Ljava/lang/String;L\x00\ncreateTimet\x00\x10Ljava/util/Date;L\x00\x02idt\x00\x10Ljava/lang/Long;L\x00\x04nameq\x00~\x00\x03L\x00\nupdateTimeq\x00~\x00\x04xpt\x00\bzhangsansr\x00\x12java.sql.Timestamp&\x18\xd5\xc8\x01S\xbfe\x02\x00\x01I\x00\x05nanosxr\x00\x0ejava.util.Datehj\x81\x01KYt\x19\x03\x00\x00xpw\b\x00\x00\x01\x84\xff]\x85\xb0x\b-\x81\x80sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x00\x00\x00\x00\x01t\x00\x06pythonsq\x00~\x00\bw\b\x00\x00\x01\x84\xff]\x85\xb0x\b-\x81\x80sq\x00~\x00\x02t\x00\x04lisisq\x00~\x00\bw\b\x00\x00\x01\x84\xff]\x85\xb0x\b<\xbf\xd8sq\x00~\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x02t\x00\x06hadoopsq\x00~\x00\bw\b\x00\x00\x01\x84\xff]\x85\xb0x\b<\xbf\xd8sq\x00~\x00\x02t\x00\x06wangwusq\x00~\x00\bw\b\x00\x00\x01\x84\xff]\x85\xb0x\b<\xbf\xd8sq\x00~\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x03t\x00\x04javasq\x00~\x00\bw\b\x00\x00\x01\x84\xff]\x85\xb0x\b<\xbf\xd8x"

看到没有,就是当成Redis里面的String来存的, 如果数据量比较小,那是非常的方便,如果数据量大,这种方式就有些问题了。

总结

我们看了这三种方式,这里仅仅是做了个入门,每个里面都有很多细节的地方需要去研究和使用,整体的感觉是要想使用的简单,那么存储在Redis中的数据就要量少,量大后,就需要自己来定制了,那基本上要用RedisTemplate来做一些工作。

以上就是RedisTemplate访问Redis的更好方法的详细内容,更多关于RedisTemplate访问Redis的资料请关注我们其它相关文章!

(0)

相关推荐

  • spring使用RedisTemplate的操作类访问Redis

    事务需要开启enableTransactionSupport,然后使用@transactional注解,里面直接通过回调的connection,就不需要自己进行multi和exec的事务开启提交了.但是通过回调去获取connection,完全没有达到一个模版类的功能.所以这篇我们会讲下几种Operations接口提供的方法. private ValueOperations<K, V> valueOps; private ListOperations<K, V> listOps; p

  • Redis官方ORM框架比RedisTemplate更优雅

    目录 RedisOM简介 JDK 11安装 使用 总结 RedisOM简介 之前在SpringBoot项目中,我一直使用RedisTemplate来操作Redis中的数据,这也是Spring官方支持的方式.对比Spring Data对MongoDB和ES的支持,这种使用Template的方式确实不够优雅!最近发现Redis官方新推出了Redis的专属ORM框架RedisOM,用起来够优雅,推荐给大家! SpringBoot实战电商项目mall(50k+star)地址:github.com/mac

  • 使用redisTemplate从redis获取所有数据

    目录 redisTemplate从redis获取所有数据 idea后端效果 swagger测试效果 说说RedisTemplate的常用方法 1.RedisTemplate常用方法 2.String类型 4.List类型 5.Set类型 6.zSet类型 redisTemplate从redis获取所有数据 @Autowired private RedisTemplate<String,String> redisTemplate; @ApiOperation("StationCharg

  • 使用@Autowired 注入RedisTemplate报错的问题及解决

    目录 @Autowired 注入RedisTemplate报错 下面是Redis配置类 解决方法一 解决方法二 下面咱们来看一下原因 @Autowired 注入RedisTemplate为null 解决 @Autowired 注入RedisTemplate报错 先看报错信息 Field redisTemplate in xxx.xxx required a bean of type 'org.springframework.data.redis.core. RedisTemplate' that

  • Spring RedisTemplate 批量获取值的2种方式小结

    目录 Spring RedisTemplate 批量获取值 1.利用mGet 2.利用PipeLine Java对Redis的批量操作RedisTemplate 1.背景 2.操作 3.说明 Spring RedisTemplate 批量获取值 1.利用mGet List<String> keys = new ArrayList<>(); //初始keys List<YourObject> list = this.redisTemplate.opsForValue().

  • 浅谈RedisTemplate和StringRedisTemplate的区别

    目录 一.区别 二.问题总结 三.案例:springboot整合redis五种数据结构API 1.string(字符串)类型 2.hash(哈希)类型 3.list(列表)类型 4.set(无序集合)类型 5.zset(有序集合)类型 6.删除key 四.总结: 一.区别 区别点1:两者的关系是StringRedisTemplate继承RedisTemplate.RedisTemplate是一个泛型类,而StringRedisTemplate则不是. 区别点2:两者序列化策略不同, String

  • Java使用RedisTemplate如何根据前缀获取key列表

    目录 使用RedisTemplate根据前缀获取key列表 批量获取 key 批量获取 value RedisTemplate如何获取符合要求的key 如图:reds中存了三条数据 我写的test如下 使用RedisTemplate根据前缀获取key列表 我们在使用 Redis 的时候,会需要获取以某个字符串开头的所有 key 批量获取 key 根据前缀获取 key 代码如下: /**  * 根据前缀获取所有的key  * 例如:pro_*  */ public Set<String> get

  • docker django无法访问redis容器的解决方法

    docker-compose.yal文件中: redis: image: redis container_name: xdemo.redis ports: - 6379:6379 restart: always django setting.py中配置redis: CACHES = { 'default': { 'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379', "OPTIONS&qu

  • redis与ssm整合方法(mybatis二级缓存)

    SSM+redis整合 ssm框架之前已经搭建过了,这里不再做代码复制工作. 这里主要是利用redis去做mybatis的二级缓存,mybaits映射文件中所有的select都会刷新已有缓存,如果不存在就会新建缓存,所有的insert,update操作都会更新缓存. redis的好处也显而易见,可以使系统的数据访问性能更高.本节只是展示了整合方法和效果,后面会补齐redis集群.负载均衡和session共享的文章. 下面就开始整合工作: 后台首先启动redis-server(后台启动与远程连接l

  • Spring Boot 与 Kotlin 使用Redis数据库的配置方法

    Spring Boot中除了对常用的关系型数据库提供了优秀的自动化支持之外,对于很多NoSQL数据库一样提供了自动化配置的支持,包括:Redis, MongoDB, Elasticsearch, Solr和Cassandra. 使用Redis Redis是一个开源的使用 ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型. Key-Value 数据库. Redis官网 Redis中文社区 引入依赖 Spring Boot提供的数据访问框架Spring Data Redis基于Jedi

  • SpringBoot+Redis实现数据字典的方法

    前言 我们在日常的开发过程中针对一些字段采用整型的方式去代替某些具体的含义,比如性别0代表男,1代表女.如果只是一些不会变更的转译我们可以采用常量或者枚举类的方式来实现,但是事实上我们也会遇到那种可能需要变更的,显然这种场景下使用枚举类这种方式是不合理的,那么如何动态地去进行转译呢? 正文 数据字典 数据字典(Data dictionary)是一种用户可以访问的记录数据库和应用程序元数据的目录.主动数据字典是指在对数据库或应用程序结构进行修改时,其内容可以由DBMS自动更新的数据字典.被动数据字

  • Docker 部署单机版 Pulsar 和集群架构 Redis(开发神器)的方法

    一.前言: 现在互联网的技术架构中,不断出现各种各样的中间件,例如 MQ.Redis.Zookeeper,这些中间件在部署的时候一般都是以主从架构或者集群的架构来部署,公司一般都会在开发环境.测试环境和生产环境各部署一套. 当我们开发的时候,一般就会连着开发环境.但是呢,一般公司的开发环境都只能在内网使用,当我们回家了,除非公司提供有 VPN,不然就没办法使用了.有时候我们是有VPN了,但是开发起来还是很不方便.例如我们现在的 MQ 中间件使用的是 Pulsar,但是 Pulsar 的 tena

  • 在K8s上部署Redis集群的方法步骤

    一.前言 架构原理:每个Master都可以拥有多个Slave.当Master下线后,Redis集群会从多个Slave中选举出一个新的Master作为替代,而旧Master重新上线后变成新Master的Slave. 二.准备操作 本次部署主要基于该项目:https://github.com/zuxqoj/kubernetes-redis-cluster 其包含了两种部署Redis集群的方式: StatefulSet Service&Deployment 两种方式各有优劣,对于像Redis.Mong

  • Django使用redis配置缓存的方法

    对于非经常更新的服务器数据,若每次都从硬盘读取一次,会浪费服务器资源.拖慢响应速度,而且数据更新频率较高,服务器负担比较大.若保存到数据库,还需要额外建立一张对应的表存储数据.一个更好的方法是在Django中使用Redis进行缓存,下面通过本文给大家介绍Django使用redis配置缓存的方法. 前言   动态网站的基本权衡是,它们是动态的.每次用户请求页面时,Web服务器都会进行各种计算 - 从数据库查询到模板呈现再到业务逻辑 - 以创建站点访问者看到的页面.从处理开销的角度来看,这比标准的文

  • SpringBoot使用Redis缓存MySql的方法步骤

    目录 1项目组成 2运行springboot 2.1官网download最基本的restful应用 2.2运行应用 3访问mysql 4设置redis缓存 1 项目组成 应用:springboot rest api 数据库:mysql jdbc框架:jpa 缓存中间件:redis 2 运行springboot 2.1 官网download最基本的restful应用 教程地址:https://spring.io/guides/gs/rest-service/ 直接download成品,找到git命

  • 详解SpringBoot使用RedisTemplate操作Redis的5种数据类型

    目录 1.字符串(String) 1.1 void set(K key, V value):V get(Object key) 1.2 void set(K key, V value, long timeout, TimeUnit unit) 1.3 V getAndSet(K key, V value) 1.4 Integer append(K key, V value) 1.5 Long size(K key) 2.列表(List) 2.1 Long leftPushAll(K key, V

  • 使用Docker配置redis sentinel哨兵的方法步骤

    目录 1.配置主从 2. 配置哨兵 3.SpringBoot连接 本文演示一主二从. 先说一下遇到的问题.我看网上说想配置哨兵,必须让启动redis的docker网络模式为host,否则无法访问到从还是什么的.我指定--network host后无法外网访问,redis desktop manager连不上redis了,后来发现我这个新克隆的机器没关防火墙,关上就好了. 1.配置主从 docker pull一下redis master mkdir -p /mydata/redis/6379/co

随机推荐

其他