携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情 >>
SpringBoot整合Redis
- 1、引入maven
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 高版本redis的lettuce需要commons-pool2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
<!-- MyBatis 逆向工程 插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<!--允许移动生成的文件 -->
<verbose>true</verbose>
<!-- 是否覆盖 -->
<overwrite>true</overwrite>
<!-- 自动生成的配置 -->
<configurationFile>
${basedir}/src/main/resources/generator/generatorConfig.xml
</configurationFile>
</configuration>
<!--下面这两个可以不配置-->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.6</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
- 2、新建RedisConfig作为配置类
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory factory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
}
- 3、新建RedisUtil作为Redis工具类
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
public boolean set(String key,Object value){
try{
redisTemplate.opsForValue().set(key,value);
return true;
}catch (Exception e){
return false;
}
}
public boolean set(String key,Object value,long time){
try{
redisTemplate.opsForValue().set(key,value,time, TimeUnit.SECONDS);
return true;
}catch (Exception e){
return false;
}
}
public Object get(String key){
return key == null ? null : redisTemplate.opsForValue().get(key);
}
......
}
- 4、application.properties加入Redis相关配置
# redis
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接超时时间(毫秒)
spring.redis.timeout=10000
# Lettuce
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.lettuce.pool.max-wait=10000
# 连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0
# 关闭超时时间
spring.redis.lettuce.shutdown-timeout=100
- 5、测试redis
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTest {
@Autowired
private RedisUtil redisUtil;
@Test
public void test() throws InterruptedException {
redisUtil.set("yibo","你好",3);
System.out.println(redisUtil.get("yibo"));
Thread.sleep(3000);
System.out.println(redisUtil.get("yibo"));
}
}
企业级缓存使用
- 定义仓储层接口PersonRepository,采用数据库和缓存实现
public interface PersonRepository {
Person getPersonById(Integer id);
}
@Component
public class PersonRepositoryImpl implements PersonRepository {
@Autowired
private PersonMapper personMapper;
public Person getPersonById(Integer id){
return personMapper.selectByPrimaryKey(id);
}
}
@Component
@Slf4j
public class PersonRepositoryCacheImpl implements PersonRepository {
private static final String CACHE_PREFIX="cache_prefix_";
@Autowired
private RedisUtil redisUtil;
@Resource(name="personRepositoryImpl")
private PersonRepository personRepository;
//这个用作缓存穿透使用
private Person nullPerson = new Person(-1);
//用随机数防止缓存雪崩
private static final Random random = new Random();
@Override
public Person getPersonById(Integer id) {
Person person = getPersonFromCache(id);
if(person == null){
log.info("cache not hit");
person = personRepository.getPersonById(id);
cachePerson(id,person);
}else if(-1 == person.getId()){
log.warn("cache hit null");
return null;
}
log.info("cache hit id");
return person;
}
private void cachePerson(Integer id,Person person){
if(person != null){
redisUtil.set(generateCacheKey(id),JSON.toJSONString(person),random.nextInt(10)+5);
}else {
redisUtil.set(generateCacheKey(id),JSON.toJSONString(nullPerson),random.nextInt(10)+5);
}
}
private Person getPersonFromCache(Integer id){
String person = (String)redisUtil.get(generateCacheKey(id));
if(StringUtils.isEmpty(person)){
return null;
}
return JSON.parseObject(person,Person.class);
}
private String generateCacheKey(Integer id){
return CACHE_PREFIX + id;
}
}
- 定义服务接口PersonService,实现服务接口api并调用仓储层
public interface PersonService {
Person getPersonById(Integer id);
}
@Service
public class PersonServiceImpl implements PersonService {
@Resource(name="personRepositoryCacheImpl")
private PersonRepository personRepository;
@Override
public Person getPersonById(Integer id) {
return personRepository.getPersonById(id);
}
}
缓存击穿
- 原因:一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,导致DB瞬间压力过大,压垮DB,就像在一个屏障上凿开了一个洞。
- 方案:热点数据缓存永不过期
缓存穿透
- 原因:恶意攻击去查数据库一定不存在的数据,对数据库造成压力,甚至压垮数据库
- 方案:使用特殊缓存空值标识对象不存在
缓存雪崩
- 原因:在某一个时间段,缓存集中过期失效,对于数据库而言,就会产生周期性的压力波峰
- 方案:设置过期时间加上随机因子,尽可能分散缓存过期时间