目录
一.多数据源配置
1.参数配置(properties文件映射)
2.Mapper映射
二.Redis
1.@Cacheable(缓存)
2.空值问题
3.缓存的过期时间
4.过期策略
5.过期淘汰策略
6.数据备份和恢复
方法①:@Value
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
方法②:@ConfigurationProperties
@ConfigurationProperties(prefix = "spring.datasource")
方法③:注入Environment(不常用复用性差,可读性差)
@Autowired
private Environment environment;
public String getDatabase() {
//key为spring.datasource.xxx,一次获取一个值
return environment.getProperty(key);
}
properties:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.0.0.0:8000/XXXserverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=XXX
spring.datasource.password=XXX
基于MyBatis 操作数据库,实际上就是通过 SqlSession 获取一个 JDBC 连接调用API
@MapperScan:扫描package把Mapper接口生成实例并注入IOC容器
@MapperScan(basePackages = {"com.seatrend.pda.admin.dao.firstDao"},
sqlSessionTemplateRef = "sqlSessionTemplate")
①basePackages:Mapper文件包的路径
②sqlSessionTemplateRef:保证线程安全
相关注解:
@Cacheable 主要是针对方法的配置,能够根据方法的求情参数对其结果进行缓存
@CachePut 保证方法被调用,又希望结果被缓存(缓存更新)
@CacheEvict 清空缓存(删除数据时的方法)
@EnableCaching 开启基于注解的缓存
基于缓存的方法功能,在被调用的时候spring会优先查看是否被被调用过/存在缓存,如果存在则会到缓存中直接取数据,反之则调用接口并存入缓存(配置决定缓存的时间点),在主启动类上需要@EnableCaching注解。
创建方法:
方法①:@Cacheable
@Cacheable(key="'head' + #p0 + #p1 + p2")
其中p0、p1、p2代表我们接口上的参数
若接收Json对象(json),则key="#json.getKey('key')"
方法②:代码创建
String key = "head" + args0 + args1 + args2;
boolean hasKey = redisUtil.hasKey(key);
//如果存在缓存则从缓存中取值
if (hasKey) {
Object obj = redisUtil.get(key);
return obj;
} else {
//不存在缓存进行自己的逻辑返回值
Object result = xxx;
return result;
}
空值问题是指,例如账号注册时未注册账号存入缓存后,用户又去注册了,那么注册后再登录会从缓存中取出为空的数据,诸如此类的问题。
为空的结果集我们不想存入缓存,需要用到Cacheable的unless
@Cacheable(key="'head' + #p0 + #p1 + p2", unless = "#result==null")
unless的意义是"除了",也就是说除了unless为真的结果都存入缓存。(不与sync同时使用)
使用unless同时也会有别的问题,也就是所有空结果的请求都会对数据库进行交互,数据库的压力会增大,会有压力隐患,引出第三个问题"缓存的过期时间",通过定期的对空值的缓存进行清楚,就能解决空值问题。
Spring并没有单独为Cacheable提供过期时间配置,我们可以对CacheManager进行配置来设置缓存过期时间。
进入Redis的配置类
方式①:
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
config = config.entryTtl(Duration.ofMinutes(2)) // 设置缓存的默认过期时间,也是使用Duration设置
.disableCachingNullValues(); // 不缓存空值
// 设置一个初始化的缓存空间set集合
Set cacheNames = new HashSet<>();
cacheNames.add("catalog_test_id");
cacheNames.add("catalog_test_name");
// 对每个缓存空间应用不同的配置
Map configMap = new HashMap<>();
configMap.put("catalog_test_id", config);
configMap.put("catalog_test_name", config.entryTtl(Duration.ofMinutes(5)));
RedisCacheManager cacheManager = RedisCacheManager.builder(factory) // 使用自定义的缓存配置初始化一个cacheManager
.initialCacheNames(cacheNames) // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
.withInitialCacheConfigurations(configMap)
.build();
return cacheManager;
}
@CacheConfig(cacheNames = "catalog_test_name")
public class SsoCache{
@Cacheable(keyGenerator = "wiselyKeyGenerator")
public String getTokenByGsid(String gsid)
}
/**
* 自定义Redis Cache 自动化配置替换系统默认的cacheManager
* 扩展cache name 支持 # 号分隔 cache name 和 超时 ttl(单位秒)
* 示例:@CachePut(value = "user#300", key = "#id")
*/
//使用(name中增加“#”,后面是过期时间,不加则走默认时间)
@Cacheable(cacheNames = "catalog_test_name#120", unless = "#result==null")
public UserEntity findUserByUserName(String userName) {
return userRepository.findUserByUserName(userName);
}
/**
* redis 配置类
*/
@Slf4j
public class RedisConfigCacheManager extends RedisCacheManager {
public RedisConfigCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
}
private static final RedisSerializationContext.SerializationPair
@Configuration
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
ObjectMapper om = new ObjectMapper();
RedisSerializer redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer
方式②:
@Bean
@Primary
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
Map expires = new HashMap<>();
expires.put("timeout", 60L);
// 设置超时
// 根据特定名称设置有效时间
redisCacheManager.setExpires(expires);
// 设置默认的时间
redisCacheManager.setDefaultExpiration(cacheDefaultExpiration);
return redisCacheManager;
}
@Configuration
//@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600 * 12)//最大过期时间
@EnableCaching
public class RedisConfig {
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
//设置缓存过期时间
Map expires = new HashMap<>();
expires.put("12h", 3600 * 12L);
expires.put("1h", 3600 * 1L);
expires.put("10m", 60 * 10L);
rcm.setExpires(expires);
//rcm.setDefaultExpiration(60 * 60 * 12);//默认过期时间
return rcm;
}
}
@Cacheable(value = "12h", key = "#root.methodName")
@Override
public List getUserArticleRank() {
//获得排行榜前10名的用户,每12小时刷新一次
return userRepository.findTop10ByArticleSize();
}
①:定时过期
每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除
优点:该策略可以立即清除过期的数据,对内存很友好
缺点:若过期key很多,删除这些key会会占用大量的CPU资源去处理过期的数据,从而影响性能
②:惰性过期
只有当访问一个key时,才会判断该key是否已过期,过期则清除。
优点:该策略可以最大化地节省CPU资源:删除操作只发生在取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的key了)
缺点:对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存,甚至导致内存泄漏
③:定期过期
每隔一定的时间,会扫描一定数量的的key,并清除其中已过期的key。
该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
redis的过期删除策略就是:惰性删除和定期删除两种策略配合使用
惰性删除:在进行get或 setnx等操作时,先检查 key是否过期,若过期,删除key,然后执行键不存在的操作;未过期则不操作,继续执行原有的命令。
定期删除:遍历每个数据库,(就是redis.conf配置的database 数量,默认为16),从库中随机取出20个键检查是否过期,若没有一个过期键,继续遍历下一个库,若存在键过期,则删除该键。
定时删除的运行频率:在redis 2.6版本中,规定每秒运行10次,大概100ms运行一次。在Redis2.8版本后,可以通过修改配置文件redis.conf 的 hz 选项来调整这个次数。(建议不要超过100,否则对CPU压力大)
如果Redis占用内存过多的时候,此时会进行内存淘汰,有如下一些策略:
noeviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键
allkeys-lru:加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键
volatile-lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键
allkeys-random:加入键的时候如果过限,从所有key随机删除
volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐
volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键
volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
allkeys-lfu:从所有键中驱逐使用频率最少的键
①:RDB:数据备份之前会检查key是否过期,过期key不会进入RDB文件。数据恢复时,也会对key进行过期检查,过期key不导入数据库。
②:AOF:重写时会先判断key是否过期,已过期的key不会重写到AOF文件。
主要讲Cacheable中sync的使用(不与unless同时使用)
①:全量同步
理解为将需要的数据查询完后,再存入缓存,在使用时如果期间服务重启或出现假死,那么RedisWriteThroughCallback方法将不会执行unlok(清除缓存key)方法,那么等key失效后key在redis中永久有效就会进入死循环,这个时候可以给这个方法的key写一个定时任务定期删除key。
②:增量同步
理解为边查询边存入缓存,对于数据量大(查询慢)且访问量大的接口,在使用增量同步时容易造成数据崩溃(缓存击穿)。