高并发缓存问题分析以及分布式锁的实现

这篇具有很好参考价值的文章主要介绍了高并发缓存问题分析以及分布式锁的实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一,场景概述:

在高并发的环境下,比如淘宝,京东不定时的促销活动,大量的用户访问会导致数据库的性能下降,进而有可能数据库宕机从而不能产生正常的服务,一般一个系统最大的性能瓶颈,就是数据库的io操作,如果发生大量的io那么他的问题也会随之而来。从数据库入手也是调优性价比最高的切入点。因此需要对我们的程序进行优化.一般采取两种方案:

①从数据库自身出发:优化sql,通过分析sql給sql建立索引,优化查询效率

②尽量避免直接查询数据库:使用缓存来实现

此文主要以缓存来解决高并发的效率问题来阐述,工欲善其事必先利其器,我们从原理和实战两方面入手来完成优化

二,原理

1.缓存的基本使用流程

高并发缓存问题分析以及分布式锁的实现,分布式缓存,缓存,分布式

如图所示:用户发起请求到达服务器,处理业务的同时,如果涉及到持久层,会经历以下几个步骤:

①首先到达缓存查询,查看缓存是否存在

②如果存在,则直接返回

③如果不存在,则前往数据库查询

④将DB查询到的结果返回给客户端,同时将查询到的结果放入缓存一份

⑤再次查询,直接从缓存里面即可拿到对应的数据.

但是在高并发的情况下,大量的请求过来又会导致一系列问题的产生,因此针对不同的问题,我们又有不同的解决方案,详情如下:

2.缓存产生的问题以及解决方案

在高并发条件下,无数的请求并发会产生以下问题:

①缓存雪崩

②缓存穿透

③缓存击穿

............

2.1缓存雪崩

高并发缓存问题分析以及分布式锁的实现,分布式缓存,缓存,分布式

我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。

解决方案:

①加锁

②对于缓存的key随机的设置过期时间,保证让缓存不在同一时间失效

③采用队列方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上

2.2缓存击穿

高并发缓存问题分析以及分布式锁的实现,分布式缓存,缓存,分布式

如图所示:缓存穿透指的是对于某个热点key,在高峰期大量的并发请求过来的时候,该热点key正好过期,导致所有的请求一瞬间打在了DB上,从而导致DB宕机或性能下降.

解决方案:

①加锁

②队列

2.3缓存穿透

高并发缓存问题分析以及分布式锁的实现,分布式缓存,缓存,分布式

如图所示:缓存穿透指的是,查询一个不存在的key,缓存为空,同时数据库也为空,相当于做了无用功,意义不大,如果被别人利用,大量的请求null,将会对数据库的性能产生巨大的影响

解决办法:

①缓存空值,并给空值key设置小于5分的过期时间

②采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

2.4缓存预热

缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

解决办法

①直接写个缓存刷新页面,上线时手工操作下;

②数据量不大,可以在项目启动的时候自动进行加载;

③定时刷新缓存;

2.5缓存更新

除了缓存服务器自带的缓存失效策略之外(Redis默认的有6种策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:

①定时去清理过期的缓存;

②当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,可以根据自己的应用场景来权衡。

2.6、缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

总结:针对于上面提到的问题不同的场景有不同的解决方案,一般情况下,我们选择加锁的方式来完成对缓存产生问题的优化,锁的选择有多种:

①本地锁(不能解决集群下产生的问题)

②分布式锁(redis,zookeper,数据库锁)

以下将采用加锁的方式分别阐述解决缓存带来的问题

三,准备工作

3.1创建springboot工程,导入redis的依赖

springboot版本选择2.3.6.RELEASE

<!-- redis -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-redis</artifactId>        </dependency>
        <!-- spring2.X集成redis所需common-pool2-->        <dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-pool2</artifactId>            <version>2.6.0</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>

3.2添加配置文件并创建启动类(略)

redis:    host: 192.168.17.166    port: 6379    database: 0    timeout: 1800000    password:    lettuce:      pool:        max-active: 20 #最大连接数        max-wait: -1    #最大阻塞等待时间(负数表示没限制)        max-idle: 5    #最大空闲        min-idle: 0     #最小空闲

3.3配置类  

@Configuration
@EnableCaching
public class RedisConfig {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 序列号key value
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

四,实战模拟

4.1不加锁带来的问题

num的值为0,提前在redis客户端里面存储

创建controller和service

 

启动测试:

高并发缓存问题分析以及分布式锁的实现,分布式缓存,缓存,分布式

单独的测试,每次发请求都没有问题,但是如果面对大量的请求呢,又会发生什么问题呢?

使用ab工具进行压测

在linux里面安装yum -y install httpd-tools

测试5000个请求,每次请求100个会是什么情况呢?

预想值:5000

高并发缓存问题分析以及分布式锁的实现,分布式缓存,缓存,分布式

实际值:148

为什么呢?

原因是多个线程抢占同一资源,会有不同的线程在同一时刻抢占到同一个值,来回的切换修改,导致了,数据不一致的问题

如何解决:上锁,加入synchronized关键字,再次进行测试

4.2本地锁

synchonized又叫同步锁,它通过持有同步监视器对象来判断是否自动上锁还是解锁.

①实例方法:同步监视器是this

②静态方法:同步监视器是类.class

③同步代码块:同步监视器是括号里面的内容

  @Override    public synchronized  void testRedisRefresh() {        //查询缓存是否存在(预先客户端设置num的值为0)        String value = redisTemplate.opsForValue().get("num");        //校验        if(StringUtils.isEmpty(value)){            //不存在(查询数据库,并且将数据存入redis)            return;        }        //存在将num转换为int        int num = Integer.parseInt(value);        //存入缓存        redisTemplate.opsForValue().set("num",String.valueOf(++num));    }

高并发缓存问题分析以及分布式锁的实现,分布式缓存,缓存,分布式

可以看到,此时的结果正确,这是因为我们使用了synchronized给方法上了同步锁,那么我的方法每次只处理一个线程的请求,其余方法只能在外面等着,当我的一个线程处理完,自动释放锁之后,其他方法才会依次进入,这样就可以保证原子性.

但是,为了减少服务器的性能,会将服务器搭建集群,那么本地锁还能够适用吗,我们拭目以待.

搭建同样的8206,8216,8226三个微服务,使用gatway网关统一访问测试:

高并发缓存问题分析以及分布式锁的实现,分布式缓存,缓存,分布式

可以看到数据再次出现了不一致,导致出现这种情况的原因在于我们的三个微服务有三把锁,三把锁同时工作,

每个线程占到的锁不一样,因此会导致数据不一致的情况.

本地锁只能锁住同一工程内的资源,在分布式系统里面都存在局限性。

解决这个问题的办法在于给集群的程序设置一把锁,此时需要分布式锁。

4.3分布式锁

4.3.1分布式锁的解决方案

随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

分布式锁主流的实现方案:

  1. 基于数据库实现分布式锁

  2. 基于缓存(Redis等)

  3. 基于Zookeeper

每一种分布式锁解决方案都有各自的优缺点:

  1. 性能:redis最高

  2. 可靠性:zookeeper最高

这里,我们基于redis实现分布式锁。

4.3.2redis实现分布式锁

在redis中可以通过setnx来对一个key上锁,通过删除这个key来解锁,因此基于redis的这个特性我们可以将它做成全局分布式锁

高并发缓存问题分析以及分布式锁的实现,分布式缓存,缓存,分布式

(1)版本一

修改service代码如下所示

 @Override    public   void testRedisRefresh() {        //锁定key        Boolean absent = redisTemplate.opsForValue().setIfAbsent("lock", "111");        if (absent){            //处理业务            //查询缓存是否存在(预先客户端设置num的值为0)            String value = redisTemplate.opsForValue().get("num");            //校验            if(StringUtils.isEmpty(value)){                //不存在(查询数据库,并且将数据存入redis)                return;            }            //存在将num转换为int            int num = Integer.parseInt(value);            //存入缓存            redisTemplate.opsForValue().set("num",String.valueOf(++num));        }else{            //未拿到锁等待            try {                Thread.sleep(100);            } catch (InterruptedException e) {                e.printStackTrace();            }            //自旋            this.testRedisRefresh();        }
    }

再次启动三个微服务测试

通过测试发现结果为5000,与预想的结果值一样

再次分析:

高并发缓存问题分析以及分布式锁的实现,分布式缓存,缓存,分布式

(2)版本二,优化

基于2.6.1版本之后的特性我们在使用java执行setnx的时候可以给key设置一个过期时间

 @Override    public   void testRedisRefresh() {        //锁定key        Boolean absent = redisTemplate.opsForValue().setIfAbsent("lock", "111",3L,TimeUnit.SECONDS);        if (absent){            //处理业务            //查询缓存是否存在(预先客户端设置num的值为0)            String value = redisTemplate.opsForValue().get("num");            //校验            if(StringUtils.isEmpty(value)){                //不存在(查询数据库,并且将数据存入redis)                return;            }            //存在将num转换为int            int num = Integer.parseInt(value);            //存入缓存            redisTemplate.opsForValue().set("num",String.valueOf(++num));
            //释放锁           redisTemplate.delete("lock");        }else{            //未拿到锁等待            try {                Thread.sleep(100);                //自旋                this.testRedisRefresh();            } catch (InterruptedException e) {                e.printStackTrace();            }
        }

    }

测试发现依然没问题,再次分析

问题:可能会释放其他服务器的锁。

场景:如果业务逻辑的执行时间是7s。执行流程如下

  1. index1业务逻辑没执行完,3秒后锁被自动释放。

  2. index2获取到锁,执行业务逻辑,3秒后锁被自动释放。

  3. index3获取到锁,执行业务逻辑

  4. index1业务逻辑执行完成,开始调用del释放锁,这时释放的是index3的锁, 导致index3的业务只执行1s就被别人释放。

最终等于没锁的情况。

解决:setnx获取锁时,设置一个指定的唯一值(例如:uuid);释放前获取这个值,判断是否自己的锁

(3)版本三优化(uuid防止误删)

 @Override    public   void testRedisRefresh() {        //设置唯一表示,防止误删除        String uuid = UUID.randomUUID().toString().replace("-", "");        //锁定key        Boolean absent = redisTemplate.opsForValue().setIfAbsent("lock", uuid,3L,TimeUnit.SECONDS);        if (absent){            //处理业务            //查询缓存是否存在(预先客户端设置num的值为0)            String value = redisTemplate.opsForValue().get("num");            //校验            if(StringUtils.isEmpty(value)){                //不存在(查询数据库,并且将数据存入redis)                return;            }            //存在将num转换为int            int num = Integer.parseInt(value);            //存入缓存            redisTemplate.opsForValue().set("num",String.valueOf(++num));
                        //判断是否是自己的锁,防误删           if (uuid.equals(redisTemplate.opsForValue().get("lock"))){               //释放锁               redisTemplate.delete("lock");           }        }else{            //未拿到锁等待            try {                Thread.sleep(100);                //自旋                this.testRedisRefresh();            } catch (InterruptedException e) {                e.printStackTrace();            }
        }

    }

由于删除操作不具有原子性因此需要再次优化

(4)终极版本(使用lua脚本)

高并发缓存问题分析以及分布式锁的实现,分布式缓存,缓存,分布式

代码实现:

@Override    public   void testRedisRefresh() {        //①上锁        //设置唯一表示,防止误删除        String uuid = UUID.randomUUID().toString().replace("-", "");        //锁定key        Boolean absent = redisTemplate.opsForValue().setIfAbsent("lock", uuid,3L,TimeUnit.SECONDS);        //②业务
        if (absent){            //处理业务            //查询缓存是否存在(预先客户端设置num的值为0)            String value = redisTemplate.opsForValue().get("num");            //校验            if(StringUtils.isEmpty(value)){                //不存在(查询数据库,并且将数据存入redis)                return;            }            //存在将num转换为int            int num = Integer.parseInt(value);            //存入缓存            redisTemplate.opsForValue().set("num",String.valueOf(++num));

        //③解锁                //lua脚本            String script="if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +                    "then\n" +                    "    return redis.call(\"del\",KEYS[1])\n" +                    "else\n" +                    "    return 0\n" +                    "end";                //创建对象            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();            redisScript.setScriptText(script);            redisScript.setResultType(Long.class);            //执行删除            redisTemplate.execute(redisScript,Arrays.asList("lock",uuid));            //④自旋        }else{            //未拿到锁等待            try {                Thread.sleep(100);                //自旋                this.testRedisRefresh();            } catch (InterruptedException e) {                e.printStackTrace();            }
        }

4.3.3redision实现分布式锁

(1)概述:Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

高并发缓存问题分析以及分布式锁的实现,分布式缓存,缓存,分布式

(2)基本使用

导入依赖

1.导入依赖 service-util
<!-- redisson --><dependency>   <groupId>org.redisson</groupId>   <artifactId>redisson</artifactId>   <version>3.15.3</version></dependency>

配置redission,加载redis连接数据

@Data@Configuration@ConfigurationProperties("spring.redis")public class RedissonConfig {
    private String host;
    private String password;
    private String port;
    private int timeout = 3000;    private static String ADDRESS_PREFIX = "redis://";
    /**     * 自动装配     */    @Bean    RedissonClient redissonSingle() {        Config config = new Config();
        if(StringUtils.isEmpty(host)){            throw new RuntimeException("host is  empty");        }        SingleServerConfig serverConfig = config.useSingleServer()                .setAddress(ADDRESS_PREFIX + this.host + ":" + port)                .setTimeout(this.timeout);        if(!StringUtils.isEmpty(this.password)) {            serverConfig.setPassword(this.password);        }        return Redisson.create(config);    }}

(3)实现

@Override    public   void testRedisRefresh() {        //上锁        String skuId="31";        String lockKey="lock:"+skuId;        //获取锁        RLock lock = redissonClient.getLock(lockKey);        lock.lock();        //处理业务        //查询缓存是否存在(预先客户端设置num的值为0)        String value = redisTemplate.opsForValue().get("num");        //校验        if(StringUtils.isEmpty(value)){            //不存在(查询数据库,并且将数据存入redis)            return;        }        //存在将num转换为int        int num = Integer.parseInt(value);        //            //存入缓存        redisTemplate.opsForValue().set("num",String.valueOf(++num));        //解锁        lock.unlock();    }

五,总结

经过分析发现分布式锁的实现在一定程度上还是会存在一系列的问题,通过这些问题的解决可以在很大程度的避免数据不一致的情况,关于分布式锁的实现可以抽取为模板,有业务场景需要的时候二次修改复用即可.

5.1redis实现分布式锁四部曲

①上锁

②判断true处理业务

③解锁(lua脚本)

④自旋

@Override    public   void testRedisRefresh() {        //①上锁        //设置唯一表示,防止误删除        String uuid = UUID.randomUUID().toString().replace("-", "");        //锁定key        Boolean absent = redisTemplate.opsForValue().setIfAbsent("lock", uuid,3L,TimeUnit.SECONDS);        //②业务
        if (absent){            //处理业务            //查询缓存是否存在(预先客户端设置num的值为0)            String value = redisTemplate.opsForValue().get("num");            //校验            if(StringUtils.isEmpty(value)){                //不存在(查询数据库,并且将数据存入redis)                return;            }            //存在将num转换为int            int num = Integer.parseInt(value);            //存入缓存            redisTemplate.opsForValue().set("num",String.valueOf(++num));

        //③解锁                //lua脚本            String script="if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +                    "then\n" +                    "    return redis.call(\"del\",KEYS[1])\n" +                    "else\n" +                    "    return 0\n" +                    "end";                //创建对象            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();            redisScript.setScriptText(script);            redisScript.setResultType(Long.class);            //执行删除            redisTemplate.execute(redisScript,Arrays.asList("lock",uuid));            //④自旋        }else{            //未拿到锁等待            try {                Thread.sleep(100);                //自旋                this.testRedisRefresh();            } catch (InterruptedException e) {                e.printStackTrace();            }        }

5.redission实现分布式锁三部曲

①上锁

②业务逻辑

③解锁文章来源地址https://www.toymoban.com/news/detail-806108.html

 @Override    public   void testRedisRefresh() {        //上锁        String skuId="31";        String lockKey="lock:"+skuId;        //获取锁        RLock lock = redissonClient.getLock(lockKey);        lock.lock();        //处理业务        //查询缓存是否存在(预先客户端设置num的值为0)        String value = redisTemplate.opsForValue().get("num");        //校验        if(StringUtils.isEmpty(value)){            //不存在(查询数据库,并且将数据存入redis)            return;        }        //存在将num转换为int        int num = Integer.parseInt(value);        //            //存入缓存        redisTemplate.opsForValue().set("num",String.valueOf(++num));        //解锁        lock.unlock();    }

到了这里,关于高并发缓存问题分析以及分布式锁的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【go项目-geecache】动手写分布式缓存 day2 - 单机并发缓存

    [ Github- geecache ](luckly-0/geecache (github.com)) 收获总结: 了解接口的使用场景,它和函数之间的差别和优略势 测试文件要以_test结尾 系统设计要严谨,要考虑后期的拓展性和维护 ,比如load函数考虑到了分布式场景 数据结构之间的封装 sync.Mutex 互斥锁 如果我们要是实现并发缓存

    2023年04月18日
    浏览(85)
  • Redis实战案例14-分布式锁的基本原理、不同实现方法对比以及基于Redis进行实现思路

    基于数据库的分布式锁:这种方式使用数据库的特性来实现分布式锁。具体流程如下: 获取锁:当一个节点需要获得锁时,它尝试在数据库中插入一个特定的唯一键值(如唯一约束的主键),如果插入成功,则表示获得了锁。 释放锁:当节点完成任务后,通过删除该唯一键

    2024年02月13日
    浏览(49)
  • 微服务 - Redis缓存 · 数据结构 · 持久化 · 分布式 · 高并发

    系列目录 微服务 - 概念 · 应用 · 架构 · 通讯 · 授权 · 跨域 · 限流 微服务 - Consul集群化 · 服务注册 · 健康检测 · 服务发现 · 负载均衡 微服务 - Redis缓存 · 数据结构 · 持久化 · 分布式 · 高并发 微服务 - Nginx网关 · 进程机制 · 限流熔断 · 性能优化 · 动态负载 · 高可用

    2023年04月18日
    浏览(46)
  • SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】

    上一篇实现了单体应用下如何上锁,这一篇主要说明如何在分布式场景下上锁 上一篇地址:加锁 需要注意的点是: 在上锁和释放锁的过程中要保证 原子性操作 核心是上锁和解锁的过程 关于解锁使用脚本参考:SET key value [EX seconds] [PX milliseconds] [NX|XX] 3.1 一个服务按照多个端口同时

    2023年04月10日
    浏览(47)
  • 一文拿捏分布式、分布式缓存及其问题解决

    1.集中式 传统的计算模型通常是集中式的,所有的计算任务和数据处理都由 单一的计算机或服务器 完成。然而,随着数据量和计算需求的增加,集中式系统可能会面临性能瓶颈和可靠性问题。 故而引出了分布式↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    2024年02月07日
    浏览(43)
  • 论文-分布式-并发控制-并发控制问题的解决方案

    目录 参考文献 问题 解法与证明 易读版本 参考文献 Dijkstra于1965年发表文章Solution of a Problem in Concurrent Programming Control,引出并发系统下的互斥(mutual exclusion)问题,自此开辟了分布式计算领域 Dijkstra在文中给出了基于共享存储原子性访问的解决方案只有十多行代码,但阅读起来

    2024年02月08日
    浏览(44)
  • 分布式缓存:什么是它以及为什么需要它?

      随着网络的快速发展,分布式应用变得越来越普遍。这种类型的应用程序需要访问多个组件和服务,而这些组件可能分散在不同的物理位置上。在这种情况下,由于网络通信的高延迟和低带宽,性能问题变得尤为明显。为解决这一问题,分布式缓存应运而生。   简单的

    2024年02月05日
    浏览(50)
  • Redis集群(分布式缓存):详解持久化、主从同步原理、哨兵机制、Cluster分片集群,实现高并发高可用

            单机式Redis存在以下问题,因此需要Redis集群化来解决这些问题        Redis数据快照,简单来说就是 把内存中的所有数据都记录到磁盘中 。当Redis实例故障重启后,从 磁盘读取快照文件,恢复数据 。快照文件称为RDB文件,默认是保存在当前运行目录。     (1)

    2024年02月08日
    浏览(55)
  • 分布式锁原理与实战三:ZooKeeper分布式锁的原理

             目录 ZooKeeper分布式锁的原理 ZooKeeper的每一个节点,都是一个天然的顺序发号器。 ZooKeeper节点的递增有序性,可以确保锁的公平 ZooKeeper的节点监听机制,可以保障占有锁的传递有序而且高效 ZooKeeper的节点监听机制,能避免羊群效应 分布式锁的抢占过程 客户端

    2024年02月08日
    浏览(43)
  • 基于Mongodb分布式锁简单实现,解决定时任务并发执行问题

    我们日常开发过程,会有一些定时任务的代码来统计一些系统运行数据,但是我们应用有需要部署多个实例,传统的通过配置文件来控制定时任务是否启动又太过繁琐,而且还经常出错,导致一些异常数据的产生 网上有很多分布式锁的实现方案,基于redis、zk、等有很多,但

    2023年04月18日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包