Redis实战
1. 安装并启动redis
docker run --name redis-demo -d -p 6379:6379 redis
tips:一些有帮助的命令
- 进入容器
docker exec -it redis-demo /bin/bash
- 使用 redis-cli 连接 redis
docker exec -it redis-demo redis-cli
2. maven依赖
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- json <——> instance -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
3. bean配置
- application.yml
spring:
data:
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码
# password: 123456
#连接池最大连接数(使用负值表示没有限制)
lettuce:
pool:
max-active: 100
# 连接池中的最大空闲连接
max-idle: 100
# 连接池中的最小空闲连接
min-idle: 0
#连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
# 连接超时时间(毫秒)
timeout: 5000
- RedisConfig.java
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @Author: Weiyin
* @Create: 2023/3/22 - 17:28
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 解决jackson2无法反序列化LocalDateTime的问题
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
// 指定序列化输入的类型,就是将数据库里的数据按照一定类型存储到redis缓存中。
// 整个类、除final外的的属性信息都需要被序列化和反序列化。
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(om, Object.class);
// 使用StringRedisSerializer来序列化和反序列化redis的key
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
}
4. 扣库存实战
- lua脚本
local count = tonumber(redis.call('get',KEYS[1]))
local quantity = tonumber(ARGV[1])
if (count >= quantity) then
return redis.call('decrby',KEYS[1],quantity)
else
return -1
end
- 测试代码
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @Author: Weiyin
* @Create: 2023/5/5 - 15:24
*/
@SpringBootTest
@Slf4j
public class LuaTest {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
private static final DefaultRedisScript<Long> script;
static {
script = new DefaultRedisScript<>();
script.setLocation(new ClassPathResource("stock-demo.lua"));
script.setResultType(Long.class);
}
@Test
void stock() throws InterruptedException {
//准备10件商品
redisTemplate.opsForValue().set("product", 10);
//准备1000个任务抢商品
List<Callable<Long>> users = new ArrayList<>(1000);
for (int i = 0; i < 1000; i++) {
users.add(
() -> {
Long product = redisTemplate.execute(script, List.of("product"), 1);
log.info("Status: {}", product);
return product;
}
);
}
ExecutorService pool = Executors.newFixedThreadPool(1000);
pool.invokeAll(users);
}
}