1.应用
1.1可以用作缓存
作用:提交数据的查询效率,减少对数据库的访问频率
什么数据时候放入缓存
1.修改频率高
2.访问频率高的数据
3.对安全系数比较低
如何实现
@Service
public class DeptServer {
@Autowired
private DeptMapper deptMapper;
@Autowired
private RedisTemplate redisTemplate;
public Dept selectById(Integer id){
//查询是否在缓存
ValueOperations forValue = redisTemplate.opsForValue();
Object o = forValue.get("dept::" + id);
if(o!=null){
return (Dept) o;
}
Dept dept = deptMapper.selectById(id);
//存储缓存
forValue.set("dept::" + id,dept);
return dept;
}
}
1.1.2缓存注解
spring4.0以后,提供了缓存注解,可以使用注解完成缓存功能,无需自己写
1.在配置类中添加缓存配置
@Bean public CacheManager cacheManager(RedisConnectionFactory factory){ RedisSerializer<String> redisSerializer=new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer(Object.class); //解决缓存查询转换异常问题 ObjectMapper om=new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); //配置序列化(解决乱码的问题),过期时间600s RedisCacheConfiguration config=RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(600))//设置过期时间 //设置key序列化方式 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) //设置value序列化方式 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues(); RedisCacheManager cacheManager=RedisCacheManager.builder(factory).cacheDefaults(config).build(); return cacheManager; }
2.使用注解@Cacheable
@Service public class DeptServer { @Autowired private DeptMapper deptMapper; //@Cacheable针对查询的,缓存名为"dept::"+id,它的value为返回结果,返回结果为null则不存入,否则存入 @Cacheable(value = "dept",key = "#id") public Dept selectById(Integer id){ Dept dept = deptMapper.selectById(id); return dept; } }
3.开启缓存注解
package com.ghx.server;
import com.ghx.dao.DeptMapper;
import com.ghx.pojo.entity.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
/**
* @author :guo
* @date :Created in 2025/2/18 14:22
* @description:
* @version:
*/
@Service
public class DeptServer {
@Autowired
private DeptMapper deptMapper;
//@Cacheable针对查询的,缓存名为"dept::"+id,它的value为返回结果,返回结果为null则不存入,否则存入
@Cacheable(value = "dept",key = "#id")
public Dept selectById(Integer id){
Dept dept = deptMapper.selectById(id);
return dept;
}
//先执行方法体,再删除缓存
@CacheEvict(value = "dept",key = "#id")
public int delete(Integer id){
return deptMapper.deleteById(id);
}
//先修改数据库,再修改缓存,把dept对象存入
@CachePut(value = "dept",key = "#dept.id")
public Dept update(Dept dept){
deptMapper.updateById(dept);
return dept;
}
}
package com.ghx.controller;
import com.ghx.pojo.entity.Dept;
import com.ghx.server.DeptServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @author :guo
* @date :Created in 2025/2/18 14:24
* @description:
* @version:
*/
@RestController
public class DeptController {
@Autowired
private DeptServer deptServer;
@GetMapping("selectById/{id}")
public Dept selectById(@PathVariable Integer id) {
//@PathVariable获取请求参数的{}参数
return deptServer.selectById(id);
}
@GetMapping("delete/{id}")
public int delete(@PathVariable Integer id) {
return deptServer.delete(id);
}
@PutMapping("update")
public Dept update(@RequestBody Dept dept) {
return deptServer.update(dept);
}
}
1.1.3缓存注解
@Cacheable针对查询的,缓存名如"dept::"+id,它的value为返回结果,返回结果为null则不存入,否则存入@CacheEvict先执行方法体,再删除缓存@CachePut先修改数据库,再修改缓存,把dept对象存入
1.2可以作为分布式锁
synchronized:
lock:接口
实现类:
ReentrantLock:重入锁 ReentrantReadWriteLock:读写分离锁加的为读锁则线程不堵塞
加的是写锁线程堵塞
上面的都是本地锁
本地锁
//如果在单线程下---该功能没有任何问题。
//如果在多线程的情况---jmeter压测工具---发现出现线程安全问题了。==使用锁synchronized ()或lock锁。
//发现使用锁之前没有问题了。但是如果该项目是一个集群。--发现在集群的情况下本地锁【只对当前工程有效】无效了。
//解决方案就是集群工程共用一把锁就行。---可以使用redis来解决问题。
public String decrement(Integer productid) {
//根据id查询商品的库存
synchronized (this) {
int num = stockDao.findById(productid);
if (num > 0) {
//修改库存
stockDao.update(productid);
System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
} else {
System.out.println("商品编号为:" + productid + "的商品库存不足。");
return "商品编号为:" + productid + "的商品库存不足。";
}
}
}
手动实现
@Autowired
private StringRedisTemplate redisTemplate;
public String decrement(Integer productid) {
ValueOperations<String,String> forValue = redisTemplate.opsForValue();
//判断是否获取锁 如果程序死机了 ,无法执行完成
//如果代码没有执行完成,会释放锁。 ——开启了一个守护线程,每隔10s检查当前线程是否还持有锁,如果当前线程还持有锁,就重新为当前线程分配30s
//分配3次,33次后直接释放锁
Boolean aBoolean = forValue.setIfAbsent("product::" + productid, "~~~~");
if(aBoolean) {
try {
//根据id查询商品的库存
int num = stockDao.findById(productid);
if (num > 0) {
//修改库存
stockDao.update(productid);
System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
} else {
System.out.println("商品编号为:" + productid + "的商品库存不足。");
return "商品编号为:" + productid + "的商品库存不足。";
}
} finally {
redisTemplate.delete("product::" + productid);
}
}
System.out.println("服务器正忙");
return "服务器正忙";
}
Redisson
每隔10s检查当前线程是否还持有锁,如果当前线程还持有锁,就重新为当前线程分配30s
依赖
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.24.3</version> </dependency>
@Autowired
private Redisson redisson;
public String decrement(Integer productid) {
RLock lock = redisson.getLock("product::" + productid);
try {
//必须为30s
lock.lock(30, TimeUnit.SECONDS);
//根据id查询商品的库存
int num = stockDao.findById(productid);
if (num > 0) {
//修改库存
stockDao.update(productid);
System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
} else {
System.out.println("商品编号为:" + productid + "的商品库存不足。");
return "商品编号为:" + productid + "的商品库存不足。";
}
} finally {
lock.unlock();
}
}