返回顶部
首页 > 资讯 > 数据库 >Redis和本地缓存使用的技巧有哪些
  • 127
分享到

Redis和本地缓存使用的技巧有哪些

redis 2022-11-30 23:11:41 127人浏览 泡泡鱼
摘要

这篇文章主要介绍“Redis和本地缓存使用的技巧有哪些”,在日常操作中,相信很多人在Redis和本地缓存使用的技巧有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Redi

这篇文章主要介绍“Redis和本地缓存使用的技巧有哪些”,在日常操作中,相信很多人在Redis和本地缓存使用的技巧有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Redis和本地缓存使用的技巧有哪些”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

三种缓存的使用场景

这部分会介绍redis,比如guava的LoadinGCache和快手开源的ReloadableCache的使用场景和局限,通过这一部分的介绍就能知道在怎样的业务场景下应该使用哪种缓存,以及为什么。

Redis的使用场景和局限性

如果宽泛的说redis何时使用,那么自然就是用户访问量过高的地方使用,从而加速访问,并且缓解数据库压力。如果细分的话,还得分为单节点问题和非单节点问题。

如果一个页面用户访问量比较高,但是访问的不是同一个资源。比如用户详情页,访问量比较高,但是每个用户的数据都是不一样的,这种情况显然只能用分布式缓存了,如果使用redis,key为用户唯一键,value则是用户信息。

redis导致的缓存击穿

但是需要注意一点,一定要设置过期时间,而且不能设置到同一时间点过期。举个例子,比如用户又个活动页,活动页能看到用户活动期间获奖数据,粗心的人可能会设置用户数据的过期时间点为活动结束,这样会

单(热)点问题

单节点问题说的是redis的单个节点的并发问题,因为对于相同的key会落到redis集群的同一个节点上,那么如果对这个key的访问量过高,那么这个redis节点就存在并发隐患,这个key就称为热key。

如果所有用户访问的都是同一个资源,比如小爱同学app首页对所有用户展示的内容都一样(初期),服务端给h6返回的是同一个大JSON,显然得使用到缓存。首先我们考虑下用redis是否可行,由于redis存在单点问题,如果流量过大的话,那么所有用户的请求到达redis的同一个节点,需要评估该节点能否抗住这么大流量。我们的规则是,如果单节点qps达到了千级别就要解决单点问题了(即使redis号称能抗住十万级别的qps),最常见的做法就是使用本地缓存。显然小爱app首页流量不过百,使用redis是没问题的。

LoadingCache的使用场景和局限性

对于这上面说的热key问题,我们最直接的做法就是使用本地缓存,比如你最熟悉的guava的LoadingCache,但是使用本地缓存要求能够接受一定的脏数据,因为如果你更新了首页,本地缓存是不会更新的,它只会根据一定的过期策略来重新加载缓存,不过在我们这个场景是完全没问题的,因为一旦在后台推送了首页后就不会再去改变了。即使改变了也没问题,可以设置写过期为半小时,超过半小时重新加载缓存,这种短时间内的脏数据我们是可以接受的。

LoadingCache导致的缓存击穿

虽然说本地缓存和机器上强相关的,虽然代码层面写的是半小时过期,但由于每台机器的启动时间不同,导致缓存的加载时间不同,过期时间也就不同,也就不会所有机器上的请求在同一时间缓存失效后都去请求数据库。但是对于单一一台机器也是会导致缓存穿透的,假如有10台机器,每台1000的qps,只要有一台缓存过期就可能导致这1000个请求同时打到了数据库。这种问题其实比较好解决,但是容易被忽略,也就是在设置LoadingCache的时候使用LoadingCache的load-miss方法,而不是直接判断cache.getIfPresent()== null然后去请求db;前者会加虚拟机层面的,保证只有一个请求打到数据库去,从而完美的解决了这个问题。

但是,如果对于实时性要求较高的情况,比如有段时间要经常做活动,我要保证活动页面能近实时更新,也就是运营在后台配置好了活动信息后,需要在C端近实时展示这次配置的活动信息,此时使用LoadingCache肯定就不能满足了。

ReloadableCache的使用场景和局限性

对于上面说的LoadingCache不能解决的实时问题,可以考虑使用ReloadableCache,这是快手开源的一个本地缓存框架,最大的特点是支持多机器同时更新缓存,假设我们修改了首页信息,然后请求打到的是A机器,这个时候重新加载ReloadableCache,然后它会发出通知,监听了同一zk节点的其他机器收到通知后重新更新缓存。使用这个缓存一般的要求是将全量数据加载到本地缓存,所以如果数据量过大肯定会对gc造成压力,这种情况就不能使用了。由于小爱同学首页这个首页是带有状态的,一般online状态的就那么两个,所以完全可以使用ReloadableCache来只装载online状态的首页。

小结

到这里三种缓存基本都介绍完了,做个小结:

  • 对于非热点的数据访问,比如用户维度的数据,直接使用redis即可;

  • 对于热点数据的访问,如果流量不是很高,无脑使用redis即可;

  • 对于热点数据,如果允许一定时间内的脏数据,使用LoadingCache即可;

  • 对于热点数据,如果一致性要求较高,同时数据量不大的情况,使用ReloadableCache即可;

小技巧

不管哪种本地缓存虽然都带有虚拟机层面的加锁来解决击穿问题,但是意外总有可能以你意想不到的方式发生,保险起见你可以使用两级缓存的方式即本地缓存+redis+db。

缓存使用的简单介绍

这里redis的使用就不再多说了,相信很多人对api的使用比我还熟悉

LoadingCache的使用

这个是guava提供的网上一抓一大把,但是给两点注意事项

  • 要使用load-miss的话, 要么使用V get(K key, Callable<? extends V> loader);要么使用build的时候使用的是build(CacheLoader<? super K1, V1> loader)这个时候可以直接使用get()了。此外建议使用load-miss,而不是getIfPresent==null的时候再去查数据库,这可能导致缓存击穿;

  • 使用load-miss是因为这是线程安全的,如果缓存失效的话,多个线程调用get的时候只会有一个线程去db查询,其他线程需要等待,也就是说这是线程安全的。

LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(1000L)
                .expireAfterAccess(Duration.ofHours(1L)) // 多久不访问就过期
                .expireAfterWrite(Duration.ofHours(1L))  // 多久这个key没修改就过期
                .build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String key) throws Exception {
                        // 数据装载方式,一般就是loadDB
                        return key + " world";
                    }
                });
String value = cache.get("hello"); // 返回hello world

reloadableCache的使用

导入三方依赖

<dependency>
  <groupId>com.GitHub.phantomthief</groupId>
  <artifactId>zknotify-cache</artifactId>
  <version>0.1.22</version>
</dependency>

需要看文档,不然无法使用,有兴趣自己写一个也行的。

public interface ReloadableCache<T> extends Supplier<T> {

    
    @Override
    T get();

    
    void reload();

    
    void reloadLocal();
}

老生常谈的缓存击穿/穿透/雪崩问题

这三个真的是亘古不变的问题,如果流量大确实需要考虑。

缓存击穿

简单说就是缓存失效,导致大量请求同一时间打到了数据库。对于缓存击穿问题上面已经给出了很多解决方案了。

  • 比如使用本地缓存

  • 本地缓存使用load-miss方法

  • 使用第三方服务来加载缓存

1.2和都说过,主要来看3。假如业务愿意只能使用redis而无法使用本地缓存,比如数据量过大,实时性要求比较高。那么当缓存失效的时候就得想办法保证只有少量的请求打到数据库。很自然的就想到了使用分布式锁,理论上说是可行的,但实际上存在隐患。我们的分布式锁相信很多人都是使用redis+lua的方式实现的,并且在while中进行了轮训,这样请求量大,数据多的话会导致无形中让redis成了隐患,并且占了太多业务线程,其实仅仅是引入了分布式锁就加大了复杂度,我们的原则就是能不用就不用。

那么我们是不是可以设计一个类似分布式锁,但是更可靠的rpc服务呢?当调用get方法的时候这个rpc服务保证相同的key打到同一个节点,并且使用synchronized来进行加锁,之后完成数据的加载。在快手提供了一个叫cacheSetter的框架。下面提供一个简易版,自己写也很容易实现。

import com.Google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;


public abstract class AbstractCacheSetterService implements CacheSetterService {

    private final ConcurrentMap<String, CountDownLatch> loadCache = new ConcurrentHashMap<>();

    private final Object lock = new Object();

    @Override
    public void load(Collection<String> needLoadIds) {
        if (CollectionUtils.isEmpty(needLoadIds)) {
            return;
        }
        CountDownLatch latch;
        Collection<CountDownLatch> loadingLatchList;
        synchronized (lock) {
            loadingLatchList = excludeLoadingIds(needLoadIds);

            needLoadIds = Collections.unmodifiableCollection(needLoadIds);

            latch = saveLatch(needLoadIds);
        }
        System.out.println("needLoadIds:" + needLoadIds);
        try {
            if (CollectionUtils.isNotEmpty(needLoadIds)) {
                loadCache(needLoadIds);
            }
        } finally {
            release(needLoadIds, latch);
            block(loadingLatchList);
        }

    }

    
    protected void block(Collection<CountDownLatch> loadingLatchList) {
        if (CollectionUtils.isEmpty(loadingLatchList)) {
            return;
        }
        System.out.println("block:" + loadingLatchList);
        loadingLatchList.forEach(l -> {
            try {
                l.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }

    
    private void release(Collection<String> needLoadIds, CountDownLatch latch) {
        if (CollectionUtils.isEmpty(needLoadIds)) {
            return;
        }
        synchronized (lock) {
            needLoadIds.forEach(id -> loadCache.remove(id));
        }
        if (latch != null) {
            latch.countDown();
        }
    }

    
    protected abstract void loadCache(Collection<String> needLoadIds);

    
    protected CountDownLatch saveLatch(Collection<String> needLoadIds) {
        if (CollectionUtils.isEmpty(needLoadIds)) {
            return null;
        }
        CountDownLatch latch = new CountDownLatch(1);
        needLoadIds.forEach(loadId -> loadCache.put(loadId, latch));
        System.out.println("loadCache:" + loadCache);
        return latch;
    }

    
    private Collection<CountDownLatch> excludeLoadingIds(Collection<String> ids) {
        List<CountDownLatch> loadingLatchList = Lists.newArrayList();
        Iterator<String> iterator = ids.iterator();
        while (iterator.hasNext()) {
            String id = iterator.next();
            CountDownLatch latch = loadCache.get(id);
            if (latch != null) {
                loadingLatchList.add(latch);
                iterator.remove();
            }
        }
        System.out.println("loadingLatchList:" + loadingLatchList);
        return loadingLatchList;
    }
}

业务实现

import java.util.Collection;
public class BizCacheSetterRpcService extends AbstractCacheSetterService {
    @Override
    protected void loadCache(Collection<String> needLoadIds) {
        // 读取db进行处理
    // 设置缓存
    }
}

缓存穿透

简单来说就是请求的数据在数据库不存在,导致无效请求打穿数据库。

解法也很简单,从db获取数据的方法(getByKey(K key))一定要给个默认值。

比如我有个奖池,金额上限是1W,用户完成任务的时候给他发笔钱,并且使用redis记录下来,并且落表,用户在任务页面能实时看到奖池剩余金额,在任务开始的时候显然奖池金额是不变的,redis和db里面都没有发放金额的记录,这就导致每次必然都去查db,对于这种情况,从db没查出来数据应该缓存个值0到缓存。

缓存雪崩

就是大量缓存集中失效打到了db,当然肯定都是一类的业务缓存,归根到底是代码写的有问题。可以将缓存失效的过期时间打散,别让其集中失效就可以了。

到此,关于“Redis和本地缓存使用的技巧有哪些”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

您可能感兴趣的文档:

--结束END--

本文标题: Redis和本地缓存使用的技巧有哪些

本文链接: https://lsjlt.com/news/173704.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Redis和本地缓存使用的技巧有哪些
    这篇文章主要介绍“Redis和本地缓存使用的技巧有哪些”,在日常操作中,相信很多人在Redis和本地缓存使用的技巧有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Redi...
    99+
    2022-11-30
    redis
  • 高并发技巧之Redis和本地缓存使用技巧分享
    目录三种缓存的使用场景Redis的使用场景和局限性LoadingCache的使用场景和局限性ReloadableCache的使用场景和局限性小结小技巧缓存使用的简单介绍LoadingCache的使用reloadableC...
    99+
    2024-04-02
  • Redis和本地缓存如何使用
    今天小编给大家分享一下Redis和本地缓存如何使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。众所周知,缓存最主要的目的就...
    99+
    2023-07-04
  • Redis在内存分配和使用统计的技巧有哪些
    本篇内容主要讲解“Redis在内存分配和使用统计的技巧有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Redis在内存分配和使用统计的技巧有哪些”吧!  具...
    99+
    2024-04-02
  • ASP.NET缓存数据技巧有哪些
    这篇文章主要讲解了“ASP.NET缓存数据技巧有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ASP.NET缓存数据技巧有哪些”吧!ASP.NET缓存数据技巧:访问缓存的值由于缓存中所存...
    99+
    2023-06-18
  • Python 中的同步缓存存储技巧有哪些?
    随着数据量的不断增加,对数据的读写效率也变得越来越重要。为了提高应用程序的性能,我们通常会使用缓存技术来避免频繁地读写磁盘或网络。 在 Python 中,我们可以通过一些同步缓存存储技巧来实现缓存功能。这些技巧包括: 使用内置的缓存模块...
    99+
    2023-10-18
    存储 同步 缓存
  • 使用Redis做缓存的原因有哪些
    这篇文章给大家分享的是有关使用Redis做缓存的原因有哪些的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。对Redis,百度百科给出的的解释是“Redis(Remote Dicti...
    99+
    2024-04-02
  • 处理git本地误删的方法和技巧有哪些
    今天小编给大家分享一下处理git本地误删的方法和技巧有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。使用git命令恢复文...
    99+
    2023-07-05
  • ASP 和 numpy 都有哪些存储缓存技术可以使用?
    ASP 和 numpy 都是常用的编程语言,它们都有存储缓存技术来提高程序的运行效率。本文将介绍 ASP 和 numpy 分别采用的存储缓存技术,并给出相应的代码演示。 一、ASP 的存储缓存技术 ASP 的存储缓存技术主要是基于 ASP ...
    99+
    2023-10-10
    存储 缓存 numpy
  • redis缓存的应用场景有哪些
    redis缓存的应用场景有哪些?可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。大规模读写数据与数据库读写能力之间的矛盾    &n...
    99+
    2024-04-02
  • redis缓存用到的场景有哪些
    Redis缓存可以应用于以下场景:1. 页面缓存:将经常访问的页面内容存储在Redis缓存中,减少数据库的访问压力,提高页面加载速度...
    99+
    2023-09-04
    redis
  • Python 打包中的 numpy 缓存有哪些技巧和注意事项?
    Python 是一个强大的编程语言,它的开源生态系统提供了各种各样的工具和库,其中包括 numpy 库。Numpy 是一个基于 Python 的科学计算库,它提供了高效的多维数组操作和数学函数库。在 Python 打包的过程中,numpy...
    99+
    2023-10-26
    打包 numpy 缓存
  • mysql存储过程有哪些使用技巧
    mysql存储过程有哪些使用技巧?这个问题可能是我们日常学习或工作经常见到的。希望通过这个问题能让你收获颇深。下面是小编给大家带来的参考内容,让我们一起来看看吧!mysql存储过程使用技巧有:1、创建带in...
    99+
    2024-04-02
  • redis缓存数据库的作用有哪些
    1. 提高访问速度:Redis缓存数据库可以将热门数据存储在内存中,从而加快数据的访问速度,提高系统的响应性能。2. 减轻数据库负载...
    99+
    2023-09-04
    redis
  • php常用缓存技术有哪些
    在PHP中,常用的缓存技术有以下几种:1. 文件缓存:将数据以文件的形式存储在服务器的文件系统中。可以使用PHP的文件操作函数(如f...
    99+
    2023-08-24
    php
  • HTML的使用技巧有哪些
    本篇内容主要讲解“HTML的使用技巧有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“HTML的使用技巧有哪些”吧!1、html---position/rel...
    99+
    2024-04-02
  • 使用Vue.js的技巧有哪些
    小编给大家分享一下使用Vue.js的技巧有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!第一招:化繁为简的Watchers场...
    99+
    2024-04-02
  • iFrame的使用技巧有哪些
    这篇文章主要介绍“iFrame的使用技巧有哪些”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“iFrame的使用技巧有哪些”文章能帮助大家解决问题。   1.作为弹...
    99+
    2024-04-02
  • PS的使用技巧有哪些
    这篇文章主要介绍了PS的使用技巧有哪些的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇PS的使用技巧有哪些文章都会有所收获,下面我们一起来看看吧。   1.Ctrl+滚轮缩放。...
    99+
    2024-04-02
  • Git的使用技巧有哪些
    本篇内容主要讲解“Git的使用技巧有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Git的使用技巧有哪些”吧!1、你的 ~/.gitconfig 文件在**...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作