1.分布式锁redisson使用
①分布式锁简介
②分布式锁学习过程
③最终分析
加入锁为原子操作:设置过期时间+设置锁
删除锁为原子操作:业务流程+删除锁
将锁的过期时间调长
④Redisson的使用
- 引入依赖
- 程序化配置
- 使用
- 创建redisson客户端
Redisson.create(“config”)
config中保存redis的地址 - 获取一把锁
redisson.getLock(“lock”) - 设置逻辑
try{lock.lock;
业务逻辑}
finally{lock.unlock;} - 解决问题
- 创建redisson客户端
⑤解决问题:
- 锁的续期
不配置过期时间:有配置的看门狗,即锁会自动续期,默认30s,如果在执行业务期间锁过完三分之一10s,即重新恢复到30s,执行业务结束后,自动续期不生效
自己
配置过期时间:则没有看门狗效应,即为设置多少过期时间,则为多少 - 最佳实战:
自己设置过期时间,这样省去续期操作
⑥读写锁
- 实战使用
redisson.getReadWriteLock(“rw-lock”);
lock.writelock();
写操作
lock.unlock();
lock.readlock();
读操作
lock.unlock(); - 介绍
写操作为互斥锁,读操作为共享锁
写时不可写和读
读时只能读不能写
⑦信号量(分布式信号量)
- 信号量=多把锁
存入一个"park":3
每次停车 park.acquire() park-1
每次释放 park.release() park+1
⑧闭锁
- 相当于固定数量的资源(用完即没)
存入一个"door":5
每次door.countDown() door-1
直到door-1到0 执行door.await()之后的方法
⑨更新数据需求
- 需求
缓存里的数据如何保证和数据库里的数据一致?
双写模式:数据更新时,改数据库也改缓存数据
(卡顿容易出现脏数据)
失效模式:数据库改完之后,删掉缓存
(还会出现脏数据)
⑩最佳实战
- 数据经常修改:直接读数据库
- 数据并发几率小,容忍大程度不一致:设置缓存
缓存数据+读写锁+过期时间
2.springche的使用
①整体结构
②注解的使用(底层都为原子操作)
- @Cacheable(value=“category”,key=“#root.methodName”)
- 当前结果需要缓存
- 缓存没有调用方法,缓存有则使用缓存
- key自动生成 value序列化 TTL永不过期
- 解决问题
- key:spring.cache.redis.use-key-prefix=true设置默认加前缀为注解的value值(为cache的组)
key="#root.methodName"指定名字为当前的方法名 - TTL:spring.cache.redis.time-to-live=3600000设置过期时间为1个小时
- value值:设置存储为json数据
原理:CacheAutoConfig->RedisCacheConfig->RedisCacheManager->初始所有的缓存->每个缓存用的配置->如果有redisConfiguration则用,没有则用默认->自定义注入redisConfiguration
- key:spring.cache.redis.use-key-prefix=true设置默认加前缀为注解的value值(为cache的组)
@EnableConfigurationProperties(CacheProperties.class)
@EnableCaching
@Configuration
public class MyCacheConfig {
/**
* 配置文件中的东西没有用到
* <p>
* 1、原来和配置文件绑定的配置类是这样的
* @return
* @ConfigurationProperties(prefix="spring.cache") public class CacheProperties
* 2、让他生效
* @EnableConfigurationProperties(CacheProperties.class)
*/
@Bean
RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
//更改序列化机制
config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
//保存value为JSON
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
//将配置文件中的所有配置都生效
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
//设置配置文件中的各项配置,如过期时间
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
- @CacheEvict(value={“category”},key = “‘getLevel1Categorys’”)
- 为失效模式,即当前写操作成功后,删除缓存中保存的数据
- 需求:更新一个表,删除两个缓存
- @Caching(evict = {
@CacheEvict(value={“category”},key = “‘getLevel1Categorys’”),
@CacheEvict(value={“category”},key = “‘getCatelogJson’”)
}) - @cacheEvict(value=“category”)直接删除分区的所有缓存
- @Caching(evict = {
- spring-cache的不足
- 读模式:
缓存穿透:查询一个null数据。缓存空数据
解决方案:spring.cache.redis.cache-null-values=true
缓存击穿:大量并发同时查询一个正好过期的数据
解决方案:加锁(让一个人查数据库)默认不加锁。
@Cacheable(value={“category”},key=“#root.methodName”,sync = true)
sync可以解决击穿
缓存雪崩:大量key同时过期
解决方案:指定过期时间。spring.cache.redis.time-to-live=3600000 - 写模式:
读写加锁:
引入cannel:记录mysql更新来更新数据库
读多写多:直接查询数据库 - 总结
常规数据(读多写少一致性要求不高):完全可以使用springcache
特殊数据:特殊涉及