下面我会给您讲解一下“Spring Boot整合 NoSQL 数据库 Redis”的完整攻略。
简介
Redis是一个基于内存的高性能key-value数据库,支持多种数据类型,可应用于缓存、消息队列、实时统计等场景。在Spring Boot应用中,我们可以很方便地集成Redis来实现快速高效的数据存取。
环境配置
要使用Redis,首先需要在本地安装Redis服务,可以去Redis官网下载并安装。安装完成后,启动Redis服务即可。
在Spring Boot项目中,我们需要引入Redis的依赖。可以在pom.xml
文件中添加以下内容:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Redis配置
在Spring Boot项目中,我们需要在application.properties
文件中配置Redis连接信息,如下所示:
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password= # 如果Redis服务设置了密码,则需要填写密码信息
使用Redis
在Spring Boot项目中,可以通过注入RedisTemplate
对象来使用Redis。RedisTemplate
是Spring封装的Redis客户端,提供了一组操作Redis数据的方法。
以保存一个String类型的值为例,示例代码如下:
@Autowired
private RedisTemplate<String, String> redisTemplate;
public void save(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
上面的代码中,redisTemplate.opsForValue()
方法返回一个ValueOperations
对象,可以使用这个对象来操作String类型的数据。set
方法用于保存一个键值对。
示例一
下面我们通过一个简单示例来展示如何使用Redis实现缓存信息的存取。
首先定义一个用于查询员工信息的接口:
public interface EmployeeService {
Employee getEmployeeById(Long id);
}
然后实现这个接口,这里我们通过休眠2秒钟的方式,模拟查询员工信息的耗时:
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Override
public Employee getEmployeeById(Long id) {
// 这里休眠2秒钟,模拟查询员工信息的耗时
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Employee(id, "张三", 30);
}
}
接下来,我们定义一个用于缓存员工信息的接口:
public interface EmployeeCache {
Employee getEmployeeById(Long id);
void putEmployee(Employee employee);
}
然后通过Redis实现这个接口:
@Service
public class EmployeeCacheRedisImpl implements EmployeeCache {
@Autowired
private RedisTemplate<String, Employee> redisTemplate;
@Override
public Employee getEmployeeById(Long id) {
String key = "employee:" + id;
if (redisTemplate.hasKey(key)) {
return redisTemplate.opsForValue().get(key);
}
Employee employee = getEmployeeFromDB(id);
redisTemplate.opsForValue().set(key, employee);
return employee;
}
@Override
public void putEmployee(Employee employee) {
String key = "employee:" + employee.getId();
redisTemplate.opsForValue().set(key, employee);
}
private Employee getEmployeeFromDB(Long id) {
// 这里只是简单模拟,实际开发中不应该这样做
return new Employee(id, "张三", 30);
}
}
上面的代码中,我们在getEmployeeById
方法中,先从Redis缓存中获取员工信息,如果不存在,则通过getEmployeeFromDB
方法从数据库中查询员工信息,并将查询结果保存到Redis缓存中。在保存到Redis缓存中时,我们使用了一个前缀employee:
来区分不同的缓存信息。
最后,我们可以在Controller中使用这些接口:
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@Autowired
private EmployeeCache employeeCache;
@GetMapping("/{id}")
public Employee getEmployeeById(@PathVariable("id") Long id) {
// 先从缓存中获取员工信息
Employee employee = employeeCache.getEmployeeById(id);
if (employee != null) {
return employee;
}
// 如果缓存中不存在,则从数据库中获取员工信息,并保存到缓存中
employee = employeeService.getEmployeeById(id);
if (employee != null) {
employeeCache.putEmployee(employee);
}
return employee;
}
}
上面的代码中,我们先从缓存中获取员工信息,如果缓存中不存在,则通过getEmployeeById
方法从数据库中获取,并将查询结果保存到缓存中。
示例二
接下来我们通过另一个简单示例来展示如何在Redis中使用Lua脚本。
首先定义一个用于扣减库存的接口:
public interface InventoryService {
boolean reduceInventory(String sku, int quantity);
}
然后实现这个接口,这里我们使用了Redis中的decrBy
命令来扣减库存,但是这个命令是原子操作,可能存在并发问题:
@Service
public class InventoryServiceImpl implements InventoryService {
@Autowired
private RedisTemplate<String, Integer> redisTemplate;
@Override
public boolean reduceInventory(String sku, int quantity) {
String key = "inventory:" + sku;
Integer stock = redisTemplate.opsForValue().get(key);
if (stock == null || stock < quantity) {
return false;
}
redisTemplate.opsForValue().decrement(key, quantity);
return true;
}
}
为了解决并发问题,我们可以使用Redis中的Lua脚本。Lua脚本可以保证操作的原子性,而且执行速度也很快。
我们可以将扣减库存的代码改写为Lua脚本,如下所示:
@Service
public class InventoryServiceImpl implements InventoryService {
@Autowired
private RedisTemplate<String, Integer> redisTemplate;
@Override
public boolean reduceInventory(String sku, int quantity) {
String key = "inventory:" + sku;
Integer stock = redisTemplate.opsForValue().get(key);
if (stock == null || stock < quantity) {
return false;
}
String script = "if redis.call('get', KEYS[1]) >= tonumber(ARGV[1]) then return redis.call('decrby', KEYS[1], ARGV[1]) else return 0 end";
List<String> keys = new ArrayList<>();
keys.add(key);
List<String> args = new ArrayList<>();
args.add(String.valueOf(quantity));
Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), keys, args);
return result != null && result > 0;
}
}
上面的代码中,我们使用了DefaultRedisScript
对象来执行Lua脚本。在脚本中,我们首先判断库存是否足够,如果足够,则使用decrBy
命令扣减库存。注意,在decrBy
命令中,我们使用了ARGV[1]
来表示传入的扣减数量。如果库存不足,则返回0。
最后,我们可以在Controller中使用这个接口:
@RestController
@RequestMapping("/inventory")
public class InventoryController {
@Autowired
private InventoryService inventoryService;
@PostMapping("/reduce")
public boolean reduceInventory(@RequestParam("sku") String sku, @RequestParam("quantity") int quantity) {
return inventoryService.reduceInventory(sku, quantity);
}
}
上面的代码中,我们通过POST请求来扣减库存。传入的参数包括产品SKU和扣减数量。
以上是关于Spring Boot整合Redis的完整攻略,提供了两个示例。希望可以对您有所帮助。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Boot整合 NoSQL 数据库 Redis详解 - Python技术站