第一步在pom.xml文件中加入redis依赖
基于2.1.6.RELEASE版本
org.springframework.boot
spring-boot-starter-data-redis
org.redisson
redisson-spring-boot-starter
3.11.6
第二步在application.yml配置redis端口号连接信息
spring:
redis:
host: ip
port: 6379
password: #没有密码就可以不填
第三步 创建config包RedisConfig类, 直接cv
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import java.time.Duration;
@Component
@Configuration // 定义一个配置类
public class RedisConfig {
@Value("${spring.redis.host}")
private String address;
@Value("${spring.redis.port}")
private String port;
@Bean
public RedissonClient singletonModeRedisson() {
Config config = new Config();
// 使⽤"redis://"来启⽤SSL连接
config.useSingleServer().setAddress("redis://"+address+":"+port);
return Redisson.create(config);
}
@Bean
public RedisTemplate
补充:RedisTemplate对象提供了各种往redis数据库中存储数据的类型:
@Autowired
private RedisTemplate redisTemplate;
补充:RedissonClient提供了一些高级功能,如分布式锁、分布式集合、分布式对象等,可以帮助简化分布式系统中的开发和管理。
@Autowired
private RedissonClient redisson;
分布式锁也很简单,举个例子
这里是我自己写的秒杀商品的业务
controller层
@GetMapping("buy/{vid}/{num}")
public synchronized ResponseResult buyVeggies(@PathVariable String vid,@PathVariable Integer num){
// 1.获取锁对象
RLock redissonLock = redisson.getLock("order");
// 2.加锁(设置锁的过期时间为30秒)
// redissonLock.lock();
redissonLock.lock(30, TimeUnit.SECONDS);
ordersService.subShopNum(vid,num);
// 3.释放锁
redissonLock.unlock();
return ResponseResult.getResponseResult("下单成功");
}
service层
public interface OrdersService {
String subShopNum(String id,Integer num);
void updateOrdersStatus(String oid);
void payVeggies(String oid);
}
业务逻辑层 这里我还用了RabbitMQ去实现订单超时的业务
@Override
public String subShopNum(String vid, Integer num) {
ValueOperations opsForValue = redisTemplate.opsForValue();
// 获取到商品信息
Veggies veggie = veggiesMapper.selectById(vid);
// redis中查询该商品
Object o = opsForValue.get("veggie:" + veggie.getId());
// 如果没有该商品就从mysql中去查询并存入redis中
if (o == null || o == ""){
opsForValue.set("veggie:" + veggie.getId(),veggie);
o = opsForValue.get("veggie:" + veggie.getId());
}
// 获取到redis中的商品
Veggies redisVeggies = (Veggies) o;
// 获取商品库存数
Integer shopRepertory = redisVeggies.getShopRepertory();
// 库存数<0时抛出异常 无库存
if (shopRepertory-num<0){
throw new RepertoryException();
}else {
// 每次下单刷新redis中的库存
redisVeggies.setShopRepertory(shopRepertory - num);
opsForValue.getAndSet("veggie:" + veggie.getId(),redisVeggies);
// 将下单信息加入订单表中
Orders orders = new Orders();
orders.setVid(redisVeggies.getId());
orders.setShopName(redisVeggies.getShopName());
orders.setShopDesc(redisVeggies.getShopDesc());
orders.setShopNum(num);
orders.setShopPrice(redisVeggies.getShopPrice()*num);
orders.setPayStatus("待支付");
int i = ordersMapper.insert(orders);
if (i == 1){
// 将订单id传给延迟队列
rabbitTemplate.convertAndSend("business.exchange","dl.delay",orders.getOid());
}
if (i < 0){
throw new InsertException();
}
}
return "下单成功";
}
@Override
@RabbitListener(bindings = @QueueBinding(
value = @Queue("dead.letter.queue"),
exchange = @Exchange("dead.letter.exchange"),
key = "dead.letter"
))
public void updateOrdersStatus(String oid) {
// 超过规定时间未支付后的操作
Orders order = ordersMapper.selectById(oid);
if ("待支付".equals(order.getPayStatus())){
order.setPayStatus("已超时");
ordersMapper.updateById(order);
System.out.println(order.getOid()+" : "+"该订单已超时");
}
}
在业务中我将一些热数据缓存到redis里面,这时候数据量比较大的话,我们就要对这些热数据进行分页,分页的方式有2种:
第一:从redis拿出所有数据后,再做内存分页(不推荐),热点数据小的时候可以这样做,性能相差不是很大,但是当数据量大的时候,分页期间就会占用大量内存,或撑爆;
第二:就是先进行分页然后在存入redis中,每一页都有一个key
但是在进行增删改的时候会有数据不同步的问题
解决方案:
在增删改的时候将redis数据库中的分页key模糊查询,然后删除(这里会有一个雪崩的问题),在进行优化我加了分布式锁。