1. 正文
1. 使用redis作为分布式锁。
2. redis中常见的面试题。
(1)redis缓存穿透,以及缓存雪崩。
2. 使用redis作为分布式锁。
锁场景:
package com.ykq.distributedlock.service;
import com.ykq.distributedlock.dao.StockDao;
import com.ykq.distributedlock.entity.Stock;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @Author 闫克起
* @Date 2021/4/16 14:30
* @Version 1.0
*/
@Service
public class StockService02 {
@Resource
private StockDao stockDao;
public String decrStock(Integer productId) {//synchronized () 同步方法 同步代码块
//查询对应的id的库存
synchronized (this) {
Stock stock = stockDao.selectById(productId);
if (stock.getNum() > 0) {
//根据id修改库存
stock.setNum(stock.getNum() - 1);
stockDao.updateById(stock);
System.out.println("库存剩余:" + (stock.getNum()));
return "库存减少成功";
} else {
return "库存不足";
}
}
}
}
这中锁不适合分布式项目。
使用redis解决分布式锁的问题
package com.ykq.distributedlock.service;
import com.ykq.distributedlock.dao.StockDao;
import com.ykq.distributedlock.entity.Stock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* @Author 闫克起
* @Date 2021/4/16 14:30
* @Version 1.0
*/
@Service
public class StockService {
@Resource
private StockDao stockDao;
@Autowired
private StringRedisTemplate redisTemplate;
public String decrStock(Integer productId) {//synchronized () 同步方法 同步代码块
Boolean flag = redisTemplate.opsForValue().setIfAbsent("product::" + productId, "ykq",30, TimeUnit.SECONDS);
//查询对应的id的库存
if(flag) {//获取锁了
try {
Stock stock = stockDao.selectById(productId);
if (stock.getNum() > 0) {
//根据id修改库存
stock.setNum(stock.getNum() - 1);
stockDao.updateById(stock); //异常发生
// int c=10/0;
System.out.println("库存剩余:" + (stock.getNum()));
return "库存减少成功";
} else {
return "库存不足";
}
}catch (Exception e){
throw new RuntimeException(e.getMessage());
}
finally {
redisTemplate.delete("product::" + productId);//释放锁资源 一定再finally
}
}else{
System.out.println("服务器正忙请稍后再试..........");
return "服务器正忙请稍后再试..........";
}
}
}
使用第三方组件redisson-----专门用于解决分布式问题。
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.4</version>
</dependency>
@Bean
public RedissonClient getRedisson(){
Config config=new Config();
config.useSingleServer().setAddress("redis://192.168.213.188:6379");
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
package com.ykq.distributedlock.service;
import com.ykq.distributedlock.dao.StockDao;
import com.ykq.distributedlock.entity.Stock;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* @Author 闫克起
* @Date 2021/4/16 14:30
* @Version 1.0
*/
@Service
public class StockService {
@Resource
private StockDao stockDao;
@Autowired
private RedissonClient redisson;
public String decrStock(Integer productId) {//synchronized () 同步方法 同步代码块
RLock lock = redisson.getLock("product::" + productId);//获取锁对象
try {
lock.tryLock(60,20,TimeUnit.SECONDS); //自己别设置时间。
Stock stock = stockDao.selectById(productId);
if (stock.getNum() > 0) {
//根据id修改库存
stock.setNum(stock.getNum() - 1);
stockDao.updateById(stock); //异常发生
// int c=10/0;
// Thread.sleep(35000);
System.out.println("库存剩余:" + (stock.getNum()));
return "库存减少成功";
} else {
return "库存不足";
}
}catch (Exception e){
throw new RuntimeException(e.getMessage());
}
finally {
lock.unlock();
}
}
}
3. redis的面试题
1. redis缓存穿透? 如何避免缓存穿透。
(1)数据库中没有该记录,而缓存中没有该记录。而这时有人恶意访问这种数据。 直接访问数据库。
解决方案:
(1) 如果数据库中不存在该对象,则往缓存中放入一个空对象,并且设置很短的过期时间。
2.缓存的雪崩?如何避免?
(1)再某一时刻,缓存中没有该记录,而这时会有大量的请求。这些请求打压到数据库。
比如: 项目刚刚上线。------预访问热点数据。
某一个缓存的时间到了。----> 失效时间是一个散列值。
使用队列: ----->每次有限的请求访问数据库。