简单谈谈redis缓存问题

边路缓存

cache aside pattern 边路缓存的问题。其实是一种指导思想,思想中包含:

1.查询的时候应该先查询缓存,如果缓存不存在,再查询数据库。

2.修改缓存数据时,应该先修改数据库,再修改缓存。

缓存穿透

在实际开发中,添加缓存工具的目的,较少对数据库的访问次数,增加访问效率。

肯定会存在Redis中不存在的缓存数据。例如:访问id= -1的数据 。可能出现绕过redis依然频繁访问数据库的情况,称为缓存穿透,多出现在数据库查询为null时的情况不被缓存时。

解决办法:

如果查询出来为null的数据,把null数据依然放入到redis缓存中,同时设置这个key的有效时间比正常有效时间更短一些。

if(list==null){
    // key value 有效时间 时间单位
    redisTemplate.opsForValue().set(navKey,null,10, TimeUnit.MINUTES);
}else{
    redisTemplate.opsForValue().set(navKey,result,7,TimeUnit.DAYS);
}

缓存击穿


        实际开发中,考虑redis所在服务器中内存压力,都会设置key的有效时间。一定会出现键值对过期的情况。如果正好key过期了,此时出现大量并发访问,这些访问都会去访问数据库,这种情况成为缓存击穿。

解决办法:

        永久数据(设置热点数据永远不过期)

        加锁。防止出现数据库的并发访问

ReentrantLock(重入锁)

JDK 对于并发访问处理的内容都放入了java.until.concurrent中

ReentrantLock性能和synchronized没有区别的,但是API使用起来更加方便。

@SpringBootTest
public class MyTest {
 
    @Test
    public void test(){
        new Thread(){
            @Override
            public void run() {
                test2("第一个线程111111");
            }
        }.start();
 
        new Thread(){
            @Override
            public void run() {
                test2("第二个线程222222");
            }
        }.start();
 
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    ReentrantLock lock = new ReentrantLock();
    public void test2(String who){
        lock.lock();
        if(lock.isLocked()) {
            System.out.println("开始执行:" + who);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("执行完:" + who);
            lock.unlock();
        }
    }
}
2.2 解决缓存击穿实例代码

只有在第一次访问时和key过期时才会访问数据库。对于性能来说没有过大影响,因为平时都是直接访问redis

private ReentrantLock lock = new ReentrantLock();
@Override
public Item selectByid(Integer id) {
    String key = "item:"+id;
    if(redisTemplate.hasKey(key)){
        return (Item) redisTemplate.opsForValue().get(key);
    }

    // lock.lock()------  开始获取锁  lock.isLocked()----查询此锁定是否任意线程保持(加锁是否成功)
    lock.lock();
    if(lock.isLocked()) {
        Item item = itemDubboService.selectById(id);
        // 由于设置了有效时间,就可能出现缓存击穿问题
        redisTemplate.opsForValue().set(key, item, 7, TimeUnit.DAYS);

   // lock.unlock()------ 开锁
        lock.unlock();
        return item;
    }
    // 如果加锁失败,为了保护数据库,直接返回null
    return null;
}

 缓存雪崩


        在一段时间内容,出现大量缓存数据失效,这段时间内容数据库的访问频率骤增,这种情况称之为缓存雪崩,与缓存击穿不同的是,缓存击穿是指并发查同一条数据,缓存雪崩是不同的数据都过期了,很多数据都查不到从而查数据库。

解决办法:

int seconds = random.nextInt(10000);

redisTemplate.opsForValue().set(key, item, 100+ seconds, TimeUnit.SECONDS);

        缓存数据的过期时间设置成随机,防止同一时间大量数据过期的现象产生。
        如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中
        设置热点数据永远不过期

Redis脑裂


        Redis脑裂主要是指因为一些网络原因导致Redis Master和Redis Slave和Sentinel集群处于不同的网络分区。sentinel连接不上Master就会重新选择Master,此时就会出现两个不同的Master,好像大脑分裂成两个一样

        Redis集群中不同的节点存储不同的数据,脑裂会导致大量数据丢失。

        解决Redis脑裂只需要在Redis配置文件中配置两个参数

min-slaves-to-write 3 //连接到master的最小slave数量

min-slaves-max-lag 10 //slave连接到master的最大延迟时间


Redis 缓存淘汰策略/当内存不足时如何回收数据/保证Redis中数据不出现内存溢出情况


Redis中数据都放入到内存中。如果没有淘汰策略将会导致内存中数据越来越多,最终导致内存溢出。在Redis5内置了缓存淘汰策略。在配置文件中有如下配置

# maxmemory-policy noeviction 默认策略noevication

# maxmemory <bytes> 缓存最大阈值

# volatile-lru -> 在设置过期key集中选择使用数最小的。

# allkeys-lru -> 在所有key中选择使用最小的。

# volatile-lfu -> 在设置过期时间key集中采用lfu算法。

# allkeys-lfu -> 在所有key中采用lfu算法。

# volatile-random -> 在设置过期key集中随机删除。

# allkeys-random -> 在所有key中随机删除。

# volatile-ttl -> 在设置了过期时间key中删除最早过期时间的。

# noeviction ->  不删除key,超过时报错。


Lru和lfu算法


LRU 

LRU(least recently used) 最近最少使用,如果数据最近被访问过,那么将来被访问的几率也更高。LRU算法实现简单,运行时性能也良好,被广泛的使用在缓存/内存淘汰中。

新插入的数据到链表的头部
每当缓存命中(即缓存数据被访问),则将数据移到链表头部
当链表满的时候,将链表尾部的数据丢弃


 LFU 

        Least  Frequently Used(最近最不经常使用)如果一个数据在最近一段时间很少被访问到, 那么可以认为在将来它被访问的可能性也很小。因此,当空间满时,最小频率访问的数据最先被淘汰。

LRU 和LFU的区别

LRU淘汰时淘汰的是链表最尾部的数据。而LFU是一段时间内访问次数最少的

何时淘汰数据 


 1.消极方法(passive way):在读取数据时先判断是否过期,如果过期删除他,例如:get、hget、hmget等

2.积极算法(active way)周期性判断是否有失效内容,如果有就删除。

3.主动删除:当超过阈值时会删除。

在Redis中每次新增数据都会判断是否超过阈值。如果超过了,就会按照淘汰策略删除一些key。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 为了在Java中处理Redis缓存问题,您需要使用Redis Java客户端库。常用的Java Redis客户端有Jedis、Lettuce和Redisson。 Jedis是一个纯Java实现的Redis客户端,使用简单,支持同步和异步两种操作方式。 Lettuce是一个高性能的Redis客户端,支持同步、异步和响应式操作。 Redisson是一个基于Netty的Redis客户端,支持分布式和可扩展的特性。 以下是使用Jedis的一个简单示例: ``` import redis.clients.jedis.Jedis; public class JedisExample { public static void main(String[] args) { Jedis jedis = new Jedis("localhost"); jedis.set("foo", "bar"); String value = jedis.get("foo"); System.out.println(value); } } ``` 通过以上示例,您可以很容易地对Redis缓存进行读写操作。 ### 回答2: 在Java中处理Redis缓存问题的方法有以下几个步骤: 首先,需要使用Java的Redis客户端库来连接和操作Redis服务器。常用的Java Redis客户端库包括Jedis、Lettuce等。 其次,需要在Java代码中导入合适的Redis客户端库,并通过连接池或者单例模式创建Redis连接对象。 然后,可以通过Redis连接对象进行操作,比如设置缓存、获取缓存、删除缓存等。对于设置缓存,可以使用set(key, value)方法来缓存键值对;对于获取缓存,可以使用get(key)方法来获取对应的值;对于删除缓存,可以使用del(key)方法来删除某个键。 另外,为了实现缓存的过期时间和淘汰策略,Redis提供了expire(key, seconds)方法来设置过期时间,以及setex(key, seconds, value)方法来设置带有过期时间的缓存。 此外,为了提高缓存的效率,可以使用批量操作和管道技术。比如,可以使用mget(keys)方法一次性获取多个缓存值;可以使用pipeline()和exec()方法来批量执行一组操作,从而减少网络往返时间。 最后,为了保证程序的健壮性和可靠性,需要在处理Redis缓存的代码中做好异常处理,并考虑并发访问和数据一致性等问题。 总之,通过以上步骤,在Java中处理Redis缓存问题可以使用Redis客户端库提供的API来连接、操作和管理Redis服务器,从而实现高效的缓存操作和管理。 ### 回答3: 用Java处理Redis缓存问题可以通过使用Java客户端库来与Redis进行交互。以下是处理Redis缓存的一般步骤: 1. 引入Java客户端库:首先,需要在Java项目中引入适当的Redis客户端库,例如Jedis或Lettuce。 2. 创建Redis连接:通过客户端库提供的API,使用Redis的主机名、端口号和密码创建Redis连接。连接信息可以在应用程序的配置文件中配置,以便在需要的时候进行更改。 3. 设置和获取缓存数据:使用客户端库提供的方法,可以将数据储到Redis缓存中。例如,可以使用"SET"命令将键值对储在Redis中,并使用"GET"命令来获取储在Redis中的数据。 4. 设置缓存过期时间:如果需要给缓存数据设置过期时间,可以使用客户端库提供的方法来设置键的过期时间。例如,可以使用"EXPIRE"命令设置键的过期时间。 5. 处理缓存失效:在从缓存中获取数据之前,需要先检查缓存是否在。如果缓存在,可以从其他数据源中获取数据,并将数据储在缓存中以供下一次使用。 6. 使用缓存策略:根据应用程序的需求,可以采用不同的缓存策略来提高缓存的效率和性能。例如,可以使用LRU(最近最少使用)策略来淘汰最近最少使用的缓存数据。 7. 监控和管理Redis缓存:使用客户端库提供的方法,可以监控和管理Redis缓存。例如,可以使用"INFO"命令获取Redis服务器的详细信息,使用"KEYS"命令列出或删除缓存数据等。 总结:使用Java处理Redis缓存问题主要涉及创建Redis连接、设置和获取缓存数据、设置缓存过期时间、处理缓存失效、使用缓存策略以及监控和管理Redis缓存。通过Java客户端库提供的API,可以方便地与Redis进行交互,实现缓存功能,提高应用程序的性能和可扩展性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值