前言
Redis的各种思想跟机组Cache和操作系统对进程的管理非常类似!
一:看到你的简历上写了你的项目里面用到了redis,为啥用redis?
因为传统的关系型数据库如Mysql,已经不能适用所有的场景,比如秒杀的库存扣减,APP首页的访问流量高峰等,都很容易把数据库打崩,所以引入了缓存中间件,redis。
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库;跟机组内存中的cache很相似。
集群:复制模式,每个机器做同一件事
分布式:每台机器分工合作,做的不是同一件事。
二、Redis数据类型
字符串类型String
哈希类型hash
列表类型
集合类型
有序集合类型
三:Redis的使用
1:下载软件
2:Spring-data-redis:提供了通过简单配置访问redis服务,对redis底层开发包jedis进行了高度封装,RedisTemplate类提供了redis各种操作。
redisTemplate.opsForValue().set("name", "gao1");
redisTemplate.opsForList().rightPush("nameList1", "刘备");
redisTemplate.opsForHash().put("nameHash", "c", "八戒");
四、Redis常见问题
1:缓存穿透:恶意请求或者频繁查询一个不存在的数据,导致每次请求都要访问数据库。
解决:缓存空对象,并设置合适的过期时间。
2:缓存击穿:在高并发的情况下,一个热点数据的缓存过期或被删除,恰好有大量的请求同时访问该数据,导致无法从缓存中获取数据,从而直接访问数据库。
解决:互斥锁;单机环境用synchronized,集群环境则使用分布式锁(redis的setnx,Redisson)
3:缓存雪崩:在同一时段大量的缓存Key同时失效或者Redis服务宕机,导致大量请求到达数据库。(与缓存击穿的区别,雪崩是很多key,击穿是某一个key缓存)
解决:合理设置缓存过期时间,给不同的key的·TTL添加随机值;建立redis集群
缓存预热:缓存预热是指在系统启动或高峰期之前,提前将一些热点数据加载到缓存中,以减少后续请求对数据库的访问压力,提高系统的性能和响应速度
五、缓存击穿问题解决
分布式环境下解决:使用 Redisson 会更加合适。
1:引入依赖;配置文件
//1、获取一把锁,只要锁的名字一样,就是同一把锁RLock lock = redissonClient.getLock("my-lock");//2、加锁 阻塞式等待(没有获得锁的线程都要等待)。默认加的锁都是30s,lockWatchdogTimeout = 30 * 1000 【看门狗默认时间】lock.lock();//1)、锁的自动续期,如果业务超长,运行期间自动续期,不用担心业务时间长,锁自动过期被删掉//只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔10秒都会自动的再次续期,续成30秒// internalLockLeaseTime 【看门狗时间】 / 3, 10s//2)、加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认会在30s内自动过期,不会产生死锁问题try {System.out.println("加锁成功,执行业务..." + Thread.currentThread().getId());try { TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }} finally {//3、解锁 假设解锁代码没有运行,Redisson会不会出现死锁System.out.println("释放锁..." + Thread.currentThread().getId());lock.unlock();}return "hello"; }
六、redis与数据库不一致问题
失效模式:修改数据库后,删除缓存后面再重新建立缓存。
双写模式:修改数据库后,修改缓存。
七、SpringCache简化缓存开发
springcache提供了一些核心概念和注解来管理缓存。
Cache Manager :是一个缓存的抽象接口。
Cache:基本操作单元,定义了常见的缓存操作,例如get,put,evict(删除)
缓存注解:
@Cacheable:触发将数据放入缓存的操作。
@CacheEvict:触发将数据从缓存删除的操作。失效模式
@CachePut:不影响方法执行更新缓存。双写模式。
// value:分组,区分不同模块的缓存 //@Cacheable(value = "product", key = "'selectById:' + #id") @Cacheable(value = "product", key = "#root.methodName + ':' + #id") @Override public Product selectById(Long id) {return productMapper.selectById(id); }
//加锁解决缓存击穿问题:sync = true //@Cacheable(value = "product", key = "'selectById'") //多个参数这种方式用“,”分割就不用字符串拼接了, key = "{#root.methodName, #id, #name}" @Cacheable(value = "product", key = "#root.methodName + ':' + #id", sync = true) @Override public Product selectById(Long id) {return productMapper.selectById(id); }//@CacheEvict(value = "product", allEntries = true) //删除分区中所有数据 @CacheEvict(value = "product", key = "'selectById:' + #product.id") @Override public void update(Product product) {productMapper.updateById(product); }
八、Redis持久化机制
1:RDB:Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。
简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。
2:AOF:Append Only File(追加文件)
Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件(不记录读命令)。
AOF默认是关闭的,需要修改redis.conf配置文件来开启AOF
AOF重写
比如我们有个计数的服务,有很多自增的操作,比如有一个key自增到1个亿,对AOF文件来说就是一亿次incr。AOF重写就只用记1条记录。
九、Redis过期数据删除策略
惰性删除
只会在取出key的时候才对数据进行过期检查,如果过期,我们就删掉它,反之返回该key。
优点 :对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查
缺点 :对内存不友好,如果一个key已经过期,但是一直没有使用,那么该key就会一直存在内存中,内存永远不会释放
定期删除
每隔一段时间抽取一批 key进行检查,删除里面过期的key。
优点:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。也能有效释放过期键占用的内存。
缺点:难以确定删除操作执行的时长和频率。
Redis的过期删除策略:惰性删除 + 定期删除两种策略进行配合使用
十、Redis数据淘汰策略(和操作系统的进程管理类似)
当Redis中的内存不够用时,此时在向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉,这种数据的删除规则被称之为内存的淘汰策略。
Redis支持8种不同策略来选择要删除的key:
noeviction:当内存不足时,Redis 不会淘汰任何数据,而是直接返回错误信息。这是默认的淘汰策略。
allkeys-lru: 对全体key,基于LRU算法进行淘汰
allkeys-lfu: 对全体key,基于LFU算法进行淘汰
allkeys-random:对全体key ,随机进行淘汰。
volatile-lru: 对设置了TTL的key,基于LRU算法进行淘汰
volatile-lfu: 对设置了TTL的key,基于LFU算法进行淘汰
volatile-random:对设置了TTL的key ,随机进行淘汰。
volatile-ttl: 对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰