专栏首页每天学Java如何合理的在项目中运用Redis

如何合理的在项目中运用Redis

在昨天推送的文章中,我们能够明显的看到访问Redis存储的数据,比访问MySQL中存储的数据要快很多,但是我们也强调了Redis的一些缺点,那么在实际的项目中,我们如何合理的使用Redis呢?

这篇文章我们主要结合实际来看看如何合理的使用Redis。在文章开始之前有这么几个问题,什么数据放到Redis中比较合理?数据库更新数据,缓存数据怎么处理?对于访问缓存和数据库都不存在的数据,如何应对这种恶意的频繁请求?

01

如何使用缓存

一:缓存热点数据

我们首先来看一下第一个问题:什么数据放到Redis中比较合适?大家都明白Redis虽然也能将数据持久化,但是通常它最主要的作用就是放在内存中供我们快速查询数据,这里不禁就要想了,什么样的数据值得我们以更快的速度查询出来?几十天没人访问的数据我们放到Redis中除了占内存别无他用。所以值得我们放到Redis中的数据首先要是热门数据,我对热门数据是这样理解的:你打开一个应用,你不得不看的数据(比如一些版本提示),以及很多人都想去看的数据就属于热门数据。这里用我的小程序做个例子:

在我的小程序中,首页的提示栏就属于热点数据,不管你喜不喜欢,打开小程序你都会看到这些数据:

1.小程序更新的版本,

2.小程序更新的文章,

3.小程序更新的题库。

我们来看一下代码如何实现的:

  /**
     * Redis存放Hash
     *
     * @param key
     * @param map
     */
    public void setHash(String key, Map map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    public Object getHash(String hashKey, String mapKey) {
        Map map = redisTemplate.opsForHash().entries(hashKey);
        Object o = map.get(mapKey);
        return o;
    }

这里闲扯一句,我个人爱好使用Hash存储数据,因为String会拥有很多key,查询起来比较麻烦。下面是接口,在请求到达的时候我们会先从缓存中取数据,如果为null的话,就去数据库中取数据,同时在数据库取出数据之后,调用线程池的线程来缓存查询的数据。

    if(redisCache.getHash("user", "versionInfo") != null) {
            logger.info("user-versionInfo");
            System.out.println(redisCache.getHash("user", "versionInfo"));
            versionVO = redisCache.getHash("user", "versionInfo");
        } else {
            logger.info("赋值:user-versionInfo");
            versionVO = userService.versionInfo();
            XcxThread.singleton.getInstance().submit(new Runnable() {
                @Override
                public void run() {
                    Map map = new HashMap();
                    map.put("versionInfo", userService.versionInfo());
                    System.out.println(map.get("versionInfo"));
                    try {
                        redisCache.setHash("user", map);
                        System.out.println("=============");
                        System.out.println(redisCache.getHash("user", "versionInfo"));
                    } catch (Exception e) {
                        System.out.println(e);
                    }

                }
            });
        }

说明一下这个接口,这里不知道小伙伴是否发现setHash这个方法我用trycatch处理来一下,因为 run( )方法不允许thorw exception,所有的异常必须在run方法内进行处理,而如果这里不处理,那么在出错的情况下,不会有任何错误出来(比如说类型转换出现异常后,系统内却没有任何错误显示),但是缓存却没有存进去数据,这就导致每次查询其实走的都是数据库查询。此外:在map执行put操作的时候,我是又查询来一下数据库,原因有以下几点:

1.线程内部不能使用外部变量

2.将外部变量设置为final不合适,因为这个类并非不可变类。

3.我觉的硬要把外部变量弄到线程内部,还不如执行一次查询来的方便。

说完这一个点之后,我想大家明白Redis第一个合理缓存数据的建议:热点数据,缓存 才有价值

二:频繁修改的数据,看情况考虑使用缓存

在上面我们说到,热点数据使用缓存,但是存在一些热点数据是会频繁变动的,比如我的小程序中的最新题库每天都会变更,那么这种数据要放在缓存中吗?对于这样的数据,我们要考虑这些数据的存取对数据库压力大不大,如果不大的话,可以不使用缓存,因为每次更新数据同时更新缓存也是一笔开销,但是如果对数据库的压力比较大的话,还是建议使用缓存去减轻数据库的压力。

我想说到这里大家应该就明白来,缓存如何去用,通常情况下,我们只需要考虑上面两点就行,在一些特定的情况下我们需要根据实际的业务场景进行实际的分析。

这里我们也可以知道在开头说的第二个问题:数据库更新数据,缓存数据怎么处理。如果业务场景允许脏数据存在一定的时间,那么我们可以通过设置过期时间来完成更新,如果数据要保证一致性那么我们可以在更新数据库成功后,立刻更新缓存。

说到这里下面我们再来看一下Redis架构设计的一个缺点:缓存穿透。 同时也是对第三个问题的解答:对于访问缓存和数据库都不存在的数据,如何应对这种恶意的频繁请求?(Redis架构设计还有其他两个缺陷:瞬间并发,缓存雪崩这里就不做详细的介绍了,你可以通过今天小程序更新的题库来了解他们。)

02

缓存穿透

所谓的缓存穿透,简单来讲就是查询某些不存在的key时,缓存和数据库查询结果都为空,而空的结果又不被缓存起来,而导致每次查询都去请求数据库层的情况。这种情况在什么时候会发生呢?比如说爬虫,在它爬到你的商品ID后,又根据商品ID去请求一些参数,如果这个商品没有参数(假设),那么频繁的请求数据库,就会让服务器崩溃,尤其是执行开销很大的SQL时候,这个崩溃的会更加快速。那么如何解决这种问题呢?

1.缓存空数据

当第一次查询数据库时,若数据不存在,返回空数据时将其写入缓存,后续查询就不必再去查询数据库了。当然这样也是有问题的,因为会平白无故的存很多无用的key,不过我们可以设计key的过期时间,这样应该在接受范围内。另外一个问题是:如果key过期时间较长,出现恶意攻击时,容易出现内存不够的情况。另外,需要额外的业务逻辑处理数据库与缓存中数据一致性的问题。

2.布隆过滤器拦截

简单来讲就是使用多个hash函数将一个key映射到一个很长的二进制向量的多个比特位中,类似于hash set。

存在问题:维护复杂,建议只在海量数据的情况下使用。

关于运用Redis的问题,还有以下两点大家在使用中要注意下:

1.数据的不一致性:缓存设置失效时间,一旦超过失效时间,就要从数据库重新加载,因此应用要容忍一定时间的数据不一致。还有一种是在数据更新时立即更新缓存,不过这也会增加系统的开销和事务一致性问题

2.如果Redis服务发生故障,可能会导致数据库跟着一起发生雪崩问题:不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户

那么今天小程序更新的题库是什么呢?

今天小程序更新的题目是:

1.什么是缓存雪崩?

2.如何解决缓存雪崩?

3.redis为什么会有高并发问题?

4.redis高并发解决办法

本文分享自微信公众号 - 每天学Java(gh_fddfb9d03324),作者:每天学Java

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-09-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Redis入门总结(大神慎入)

    “ 从Redis的安装到项目集成的两篇文章中,我们已经简单的了解到何如去用Redis的,再然后通过Redis和Mysql的查询性能对比和项目中如何合理运用Red...

    每天学Java
  • Mysql和Redis查询速度的对比

    “ 在软件系统中,IO速度比内存速度慢,IO读写在很多情况下会是系统的瓶颈,我们也知道Redis的查询速度比直接查数据库要快,因为Redis将数据存在内存中,而...

    每天学Java
  • Java底层-HotSpot

    在前面几节我们聊到,Javac编译器将java文件编译为class文件后,由JVM将字节码转为与机器适配的机器码进行执行, 这里我们说的JVM实际上是JVM实例...

    每天学Java
  • 一篇文章了解Redis数据库

    redis是一个key-value存储系统。它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sor...

    逆月翎
  • 2019秋招:460道Java后端面试高频题答案版【模块十二:Redis】

    Redis 对于 2020 届后端开发秋招面试中已经是一个常规的模块了。毕竟现在是互联网时代,各大公司维护的系统的并发量都是很高的,对性能都是有很高的要求,所以...

    乔戈里
  • 缓存淘汰、缓存穿透、缓存击穿、缓存雪崩、数据库缓存双写一致性

    为什么需要缓存淘汰?你需要缓存30G的数据,但是Redis本身只能使用10G的内存,那你就得做个取舍了,毕竟鱼与熊掌不可兼得。为了利益最大化肯定要保留最重要的1...

    Java学习录
  • Redis 真得那么好用吗?

    Redis是一个开源的底层使用C语言编写的Key-Value存储数据库。可用于缓存、事件发布订阅、高速队列等场景。而且支持丰富的数据类型:string(字符串)...

    猿哥
  • Redis 缓存问题(13) 原

    因为这些数据是很少修改的,所以在绝大部分的情况下可以命中缓存。但是,一旦被缓存的数据发生变化的时候,我们既要操作数据库的数据,也要操作Redis的数据,所以问题...

    兜兜毛毛
  • 这几道 Redis 面试题都不懂,怎么拿 Offer?

    随着系统访问量的提高,复杂度的提升,响应性能成为一个重点的关注点。而缓存的使用成为一个重点。redis 作为缓存中间件的一个佼佼者,成为了面试必问项目。

    芋道源码
  • 这么简单的Redis面试题都不懂,怎么拿offer?

    随着系统访问量的提高,复杂度的提升,响应性能成为一个重点的关注点。而缓存的使用成为一个重点。redis 作为缓存中间件的一个佼佼者,成为了面试必问项目。本文分享...

    lyb-geek

扫码关注云+社区

领取腾讯云代金券