当前位置: 七九推 > IT编程>数据库>Redis > RedisTemplate访问Redis的更好方法

RedisTemplate访问Redis的更好方法

2023年01月21日 Redis 我要评论
开始准备开始之前我们需要有redis安装,我们采用本机docker运行redis, 主要命令如下docker pull redisdocker run --name my_redis -d -p 63

开始准备

开始之前我们需要有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)
打赏 微信扫一扫 微信扫一扫

相关文章:

  • 使用Redis缓存时高效的批量删除的几种方案

    使用Redis缓存时高效的批量删除的几种方案

    前因后果之前我们的服务,在上线的时候发现有一些大key的使用不是很规范,特别是没有设置过期时间,因此导致redis中内存的数据越来越多,目前redis节点的内存... [阅读全文]
  • Redis+Hbase+RocketMQ 实际使用问题案例讲解

    Redis+Hbase+RocketMQ 实际使用问题案例讲解

    需求将hbase数据,解析后推送到rocketmq。redis使用list数据类型,存储了需要推送的数据的rowkey及表名。简单画个流程图就是:分析及确定方案... [阅读全文]
  • Redis脑裂导致数据丢失的解决

    Redis脑裂导致数据丢失的解决

    1 案例主从集群有1个主库、5个从库和3个哨兵实例,突然发现客户端发送的一些数据丢了,直接影响业务层数据可靠性。最终排查发现是主从集群中的脑裂问题导致:主从集群... [阅读全文]
  • 浅谈Redis在秒杀场景的作用

    浅谈Redis在秒杀场景的作用

    秒杀业务特点:限时限量,业务系统要处理瞬时高并发请求,redis是必需品。秒杀可分成秒杀前、秒杀中和秒杀后三阶段,每个阶段的请求处理需求不同,redis具体在秒... [阅读全文]
  • Redis使用Bitmap的方法实现

    1. bitmap 是什么bitmap(也称为位数组或者位向量等)是一种实现对位的操作的'数据结构',在数据结构加引号主要因为:bitmap 本身不是一种数据结构,…

    2023年01月27日 数据库
  • Redisson如何解决redis分布式锁过期时间到了业务没执行完问题

    面试问题redis锁的过期时间小于业务的执行时间该如何续期?问题分析首先如果你之前用redis的分布式锁的姿势正确,并且看过相应的官方文档的话,这个问题so easy.我们来看很多…

    2023年01月14日 数据库

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2023  七九推 保留所有权利. 粤ICP备17035492号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com