<一> redis-缓存中间件
-
什么是redis
redis是c语言开发的,一个高性能key-value键值对内存数据库,可以用来做数据库、缓存、消息中间件的一种非关系型数据库。 -
redis数据存储在哪里
内存和磁盘中,但是redis的读写都在内存中,这也是速度快的原因之一。
对于高频繁访问的数据,存储在内存中方便访问。
为了避免服务宕机或者重启数据丢失,也可以将数据存储在磁盘中持久化。 -
redis的数据类型
String、set、zset、list、hash -
redis为什么响应速度快
1) 基于内存存储,redis的读写都是基于内存的key-value操作。内存远大于磁盘响应速度。
2) 单线程操作,避免了多线程cpu来回切换和各种加锁和解锁的资源消耗。
redis的单线程操作是指一个模块的操作单线程,其他该多线程的还是使用多线程。
3)丰富的数据结构
redis是基于key-velue键值对存储的缓存数据库,对于key都是string类型,但是vlue有多种数据类型
4)高性能的多复用IO模型 -
为什么需要使用redis
redis的响应速度快读10w+,写8w,对于一些业务场景(可以将计算的结果-菜单等存储到缓存中,下次直接getfromredis) -
redis怎么配置、步骤
1) pom文件加上依赖
org.springframework.boot
spring-boot-starter-data-redis
io.lettuce
lettuce-core
2) 配置文件加上redis服务器的host port user psw等信息
3)使用springboot自动配置好的StringRedisTemplete工具来操作redis进行读写操作,
@Autowired
StringRedisTemplete stringRedisTemplete;
// 读
stringRedisTemplete.opsForValue.get(“keyName”)
// 写
stringRedisTemplate.opsForValue().set(“keyName”, value, 1, TimeUnit.DAYS); // 设置过期时间 防止缓存数据与数据库不一致
-
redis怎么进行get,set,读写数据
使用StringRedisTemplete .get(“keyname”) /.set(“keyname”,“value”,“time”,“时间单位”) -
redis有哪些参数
key-String
value-list,string、set、zset、hash
过期时间-过期删除,重新sql获取set到缓存中,与数据库数据保持一致
过期时间的单位 -
redis缓存容易出现的问题
1)缓存穿透
reason: 大量并发请求一个不存在的数据,到缓存中null,之后大量请求到了数据库中,数据库压力过大崩了。这种是缓存穿透。
way: 即使数据库中为null的数据,也存储一个标识到缓存中,并且设置一个过期时间。
2)缓存雪崩
r:大量不同业务模块的请求到缓存,但是对应的数据同时过期了,导致所有的请求到db-压力大。
w:设置不同的过期时间,可以在配置文件中统一配置,因为不同业务key存储的时间不同,过期点也不会相同。
3)缓存击穿
r:大量恶意请求到一个热点词,刚好这个key过期了。db压力过大
w:加锁-记得双重验证,每次只允许一个请求进入db-get->db,其他没有进去锁的去访问缓存即可。
-
本地锁(不是redis分布式锁,加关键字Synchronized等)
本地锁只能锁住同一个进程里面的线程,如果是分布式下,即使每个服务仅仅放一个线程进来,相互是隔离的,不能只锁住一个。这个时候需要分布式锁-redisson(使用redis分布式锁-没有看门狗机制) -
redis怎么保证与数据库数据一致(更新数据库)
1)双写模式
先更新数据库,再去更新缓存对应的key,这种缓存中可能会短暂出现脏数据,稳定后缓存过期-重新读取db即可保证最新数据。
2)失效模式
先写数据库,后删除缓存。这种也可能短暂出现缓存有脏数据,稳定后即可无论是更新缓存还是删除缓存都会有脏数据,解决如下
1)失效模式-使用双删模式
先删除缓存->更新db-隔几秒再去删除缓存
2)使用MQ消息队列,消费者监听queue消息去删除缓存
生产者(更新db)->MQ->queue有数据->customer监听到->删除缓存->告知queue是否删除成功,失败queue再次发送。(不需要写代码,mq本身会重复出队。解耦修改数据库和查询数据库解耦,不会造成缓存有脏数据)
11.缓存锁使用的原则
正常数据,缓存加上过期时间,就可以实现数据一致性。
对于实时性要求高的,最好是到数据库中读取。
对于频繁修改的数据,不建议加读写锁,性能会慢。
<二> redisson-分布式锁
-
redisson是什么
redisson是基于redis的分布式框架,提供了分布式锁等服务 -
为什么需要分布式锁
本地锁只能锁住当前进程-一个服务的线程,所以需要分布式锁。 -
redission配置步骤
1)pom文件加上依赖(前提是有redis依赖)
org.redisson
redisson
3.12.0
2)配置redis配置信息 -
redisson提供的分布式锁与redis自己的分布式锁区别
1)redisson提供的分布式锁
-分布式下也只能有一个线程占用锁,执行业务代码,直至解锁,下一个thread才能进来执行。
使用RedissonClient加锁—不会出现死锁----锁会自动过期
@Autowried
RedissonClient client;
// 1. 获取锁mylock 只要锁的名字相同 就是同一把锁
RLock myLock = redisson.getLock(“myLock”);
// 2. 加锁
myLock.lock(); // 阻塞锁
// 业务代码
try {
System.out.println(“加锁成功,执行业务…” + Thread.currentThread().getId());
Thread.sleep(3000);
} catch (Exception e) {
} finally {
// 3. 解锁
// 即使业务代码执行失败或者断电,没有解锁成功,也不会出现死锁,
// RedissonClient不会出现死锁
System.out.println(“释放锁” + Thread.currentThread().getId());
myLock.unlock();
}
2)redis提供的分布式锁
使用StringRedisTemplate加锁
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(
“lockValue”,
uuid,
5,
TimeUnit.SECONDS);
3) redis-StringRedisTemplate分布式锁与redisson-RedissonClient 分布式锁区别。
区别在于
1.–redisson分布式锁有看门狗机制,一个线程占用锁之后,如果锁没有释放之前,redisson会自动给锁续期(10s检查一次锁是否释放,续期time-30s),不会过期,直至线程结束锁释放。
2- -redis 的stringRedisTemplate分布式锁不会自动续期。
redisson加锁的业务代码只要完成,就不会自动给锁续期,即使不unlock,锁默认在30s后自动删除。即使服务宕机-业务结束,也不会出现死锁,锁会自动过期。
-
读写锁相关
// 6.1 读写锁(可以获read-write)
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(“catalogJson-lock”);
// 6.2 写锁
RLock wLock = readWriteLock.writeLock();
// 6.3 读锁
RLock rLock = lock.readLock();
注意
1)一个读锁可以被多个thread线程占用不互斥。
2)写锁与其他锁都互斥,一次只能有一个线程占用锁,其他写或者读必须等待。 -
分布式锁的几个参数
创建锁的过期时间,时间单位,redisson分布式锁默认是10s查看一次锁是否还在,如果在自动延长30s(前提是没有手动设置过期时间) -
redisson分布式锁的特点
1)一次只能有一个进程的一个线程进来
2)有看门狗机制,redisson–自动续期30s,业务未执行完,其他锁进来造成数据紊乱
3)自动过期-避免死锁(什么是死锁,加锁->执行业务->执行完解锁,没有解锁业务代码出异常或者服务挂了,锁就一直存在redis缓存数据库中-死锁) -
redisson看门狗机制(给线程看门,线程进去执行业务,redisson看门)
redisson-redissonClient提供的分布锁,一旦某个线程进入到占了锁,在业务代码没有执行完成,释放锁之前,redisson没10s会自动检测锁是否还在,并续期30s。直至业务完成,解锁,不再自动续期。这种情况只针对没有手动配置过期时间的前提,如果配置了则失效。 -
redission的可重入锁机制
Redisson 可重入锁是一种分布式锁,它基于 Redis 实现。可重入指的是同一个线程在持有锁的情况下,可以多次获取该锁而不会造成死锁。
<三>SpringCache-分布式缓存使用
-
springcache是什么
spring提供的一个缓存框架,使用一些注解解决缓存的问题,不需要关心底层代码逻辑如何实现 -
使用redis作为缓存工具,cache使用步骤
1)pom文件加上依赖
引入依赖 spring-boot-starter-cache
spring-boot-starter-data-redis
2)手动在配置文件加上缓存类型(其他配置后面使用场景再说)
spring.cache.type=redis
3)开启缓存@EnableCaching // 开启缓存,加在对应Configuration类上面
3.springcache 主要的注解
1)@EnableCaching-开启缓存
2)@Cacheable 加上方法上面,触发方法更新缓存(如果缓存不是null则不存储db数据到缓存中)
3)@CacheEvict,更新数据库触发删除缓存对应的数据-失效模式
4)@CachePut,更新数据库,在不影响更新数据库的情况下,更新缓存数据-双写模式。
-
springcache有哪些不足之处?该缓存框架默认不加锁
1)读模式(@Cacheable-读db->set->redis)
只要涉及读db数据到redis缓存,且不加锁的情况,大并发就会暴露redis存储缺陷。
a. 缓存穿透-开启db空值也存到redis中
配置文件加
spring.cache.redis.cache-null-values=trueb. 缓存雪崩-配置设置缓存的过期时间
spring.cache.redis.time-to-live=600000c. 缓存击穿-加锁
springCache默认是不加分布式锁的,可以对应方法上
@Cacheable注解上面加上sync = “true”,线程加锁-一次只允许一个线程占用锁访问db-避免热点击穿db。
@Cacheable(value = “category”, key = “#root.method.name”, sync = true)2)写模式
数据写模式对应的是更新数据,势必涉及更新db与更新缓存,两者存在数据一致性问题。
解决方法:对于读多写少的数据,直接使用springcache缓存即可,key设置过期时间,查询触发再次更新db数据到缓存redis中。
总结
redis-缓存中间件-暂时的非关系key-value键值对数据库,StringRedisTemplate操作。
redisson-基于redis操作的数据库,提供分布式锁,解决分布式下操作redis缺陷。-使用redissonClient操作分布式锁(单体不需要redisson,本地锁就可以解决)
springcache-spring提供的分布式缓存框架,可以使用多种缓存中间件存储数据,本文type是redis,直接使用一些注解实现redis读-存储,写-写/删数据。不需要再单独使用redis执行一些列繁琐的代码实现。