广告位联系
返回顶部
分享到

RedisTemplate访问Redis的更好方法

Redis 来源:互联网 作者:佚名 发布时间:2023-01-21 16:51:10 人浏览
摘要

开始准备 开始之前我们需要有Redis安装,我们采用本机Docker运行Redis, 主要命令如下 1 2 3 4 docker pull redis docker run --name my_redis -d -p 6379:6379 redis docker exec -it my_redis bash redis-cli 前面两个命令

开始准备

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

1

2

3

4

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类

1

2

3

4

5

6

7

8

9

@Data

@Builder

@NoArgsConstructor

@AllArgsConstructor

public class Book implements Serializable {

    private Long id;

    private String name;

    private String author;

}

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

1

2

3

4

5

6

@Bean

public RedisTemplate<String, Book> redisTemplate(RedisConnectionFactory redisConnectionFactory) {

    RedisTemplate<String, Book> template = new RedisTemplate<>();

    template.setConnectionFactory(redisConnectionFactory);

    return template;

}

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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中, 这是缓存的通用做法。 使用起来感觉很方便。 我们这里为了简单没有使用持久化存储,就硬编码了几条数据, 代码如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

@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的数据长什么样子。

1

2

3

4

5

6

7

8

9

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里面的数据。简单看下代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

@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();

}

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

1

2

3

4

5

6

7

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倍,如果是数据量大,这个还是比较大的浪费。

1

2

3

4

5

6

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

1

2

3

4

5

6

7

8

9

10

11

12

@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的接口

1

2

3

public interface CacheBookRepository extends CrudRepository<CacheBook, Long> {

    Optional<CacheBook> findOneByName(String name);

}

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

@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里面的数据

1

2

3

4

5

6

7

8

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"

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

1

2

3

127.0.0.1:6379> smembers repository-book

1) "1"

2) "2"

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

1

2

3

4

5

6

7

8

9

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"

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

1

2

3

4

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下面

1

2

3

4

5

6

7

8

9

10

11

12

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@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来访问数据库

1

2

public interface BookRepository extends JpaRepository<CacheBook, Long> {

}

定义一个service来调用它

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@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中加上

1

2

3

4

5

6

7

8

9

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中是怎么存的

1

2

3

4

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来做一些工作。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://juejin.cn/post/7180290294481420348
相关文章
  • 实现在线 + 离线模式进行迁移Redis数据实战指南

    实现在线 + 离线模式进行迁移Redis数据实战指南
    redis-full-check的使用背景 在经历了之前的文章内容章节内容,已完成Redis迁移后,可能会存在以下问题需要进行数据迁移之后的对比。例如,
  • RedisTemplate访问Redis的更好方法
    开始准备 开始之前我们需要有Redis安装,我们采用本机Docker运行Redis, 主要命令如下 1 2 3 4 docker pull redis docker run --name my_redis -d -p 6379:6379 re
  • redis保存session信息的代码

    redis保存session信息的代码
    1、新建一个maven web 工程名为 session-redis 如下: 目录结构如下: 2、修改配置文件内容 pom.xml 中依赖如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1
  • 用redis setNX命令来加锁的教程

    用redis setNX命令来加锁的教程
    用redis setNX命令来加锁 加锁和释放锁,都需要原子操作,不然在中途中断就会发生异常, 删锁的时候也只能删除自己对应线程的锁。 下面是
  • spring boot集成redis基础入门实例介绍
    redis 支持持久化数据,不仅支持key-value类型的数据,还拥有list,set,zset,hash等数据结构的存储。 可以进行master-slave模式的数据备份 更多
  • redis批量操作pipeline管道操作方法

    redis批量操作pipeline管道操作方法
    redis | pipeline(管道) 背景 Redis是一种基于客户端-服务端模型以及请求/响应的TCP服务。这意味着通常情况下一个请求会遵循以下步骤: 客户
  • springboot整合使用云服务器上的Redis方法

    springboot整合使用云服务器上的Redis方法
    一、前提条件 修改redis.conf配置文件 1、protected-mode yes(默认的) 修改成 protected-mode no,解除保护模式 2、注释掉绑定ip ,绑定ip的话,使得
  • 阿里云服务器部署Redis并整合Spring Boot的介绍

    阿里云服务器部署Redis并整合Spring Boot的介绍
    一、什么是Redis redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zs
  • 生产redisson延时队列不消费问题排查解决

    生产redisson延时队列不消费问题排查解决
    问题描述 项目使用redisson延时队列功能,实现直播的开播提醒,突然有一天业务爆出问题,未触发开播提醒。 初步排查 首先通过查询生产日
  • Redis主从复制分步讲解使用

    Redis主从复制分步讲解使用
    主服务器(master)启用二进制日志 选择一个唯一的server-id 创建具有复制权限的用户 从服务器(slave)启用中继日志, 选择一个唯一的serv
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计