目录
1.分布式并发问题
2.如何解决分布式并发问题呢 ?
3.使⽤Redis实现分布式锁-代码实现
4.解决因线程异常导致⽆法释放锁的问题
5.解决因t1过期释放t2锁的问题
6.看⻔狗机制
7.分布式锁框架-Redisson
7.1 Redisson介绍
7.2 在SpringBoot应⽤中使⽤Redisson
7.3 Redisson⼯作原理
7.4 Redisson使⽤扩展
7.4.1 Redisson单机连接
7.4.2 Redisson集群连接
7.4.3 Redisson主从连接
7.5 分布式锁总结
7.5.1 分布式锁特点
7.5.2 锁的分类
7.5.3 Redission的使⽤
@Transactional
public Map addOrder(String cids,Orders order) throws
SQLException {
logger.info("add order begin...");
Map map = null;
//1.校验库存:根据cids查询当前订单中关联的购物⻋记录详情(包括库存)
String[] arr = cids.split(",");
List cidsList = new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
cidsList.add(Integer.parseInt(arr[i]));
}
//根据⽤户在购物⻋列表中选择的购物⻋记录的id 查询到对应的购物⻋记录
List list =
shoppingCartMapper.selectShopcartByCids(cidsList);
//从购物⻋信息中获取到要购买的 skuId(商品ID) 以skuId为key写到redis中: 1
2 3
boolean isLock = true;
String[] skuIds = new String[list.size()]; //记录已经锁定的商品的ID
for (int i = 0; i list =
shoppingCartMapper.selectShopcartByCids(cidsList);
boolean f = true;
String untitled = "";
for (ShoppingCartVO sc : list) {
if (Integer.parseInt(sc.getCartNum()) >
sc.getSkuStock()) {
f = false;
}
untitled = untitled + sc.getProductName() + ",";
}
if (f) {
//2.添加订单
//3.保存快照
//4.修改库存
//5.删除购物⻋
map = new HashMap<>();
logger.info("add order finished...");
map.put("orderId", orderId);
map.put("productNames", untitled);
}
}catch(Exception e){
e.printStackTrance();
}finally{
//释放锁
for (int m = 0; m < skuIds.length ; m++) {
String skuId = skuIds[m];
if(skuId!=null && !"".equals(skuId)){
stringRedisTemplate.delete(skuId);
}
}
}
return map;
}else{
//表示加锁失败,订单添加失败
// 当加锁失败时,有可能对部分商品已经锁定,要释放锁定的部分商品
for (int i = 0; i < skuIds.length ; i++) {
String skuId = skuIds[i];
if(skuId!=null && !"".equals(skuId)){
stringRedisTemplate.delete(skuId);
}
}
return null;
}
}
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
@Bean
public DefaultRedisScript defaultRedisScript(){
DefaultRedisScript defaultRedisScript = new
DefaultRedisScript<>();
defaultRedisScript.setResultType(List.class);
defaultRedisScript.setScriptSource(new ResourceScriptSource(new
ClassPathResource("unlock.lua")));
return defaultRedisScript; }
@AutoWired
private DefaultRedisScript defaultRedisScript;
//执⾏lua脚本
List keys = new ArrayList<>();
keys.add(skuId);
List rs = stringRedisTemplate.execute(defaultRedisScript,keys ,
values.get(skuId));
System.out.println(rs.get(0));
org.redisson redisson 3.12.0
redisson :addr :singleAddr :host : redis : //47.96.11.185 : 6370password : 12345678database : 0
@Configuration
public class RedissonConfig {
@Value("${redisson.addr.singleAddr.host}")
private String host;
@Value("${redisson.addr.singleAddr.password}")
private String password;
@Value("${redisson.addr.singleAddr.database}")
private int database;
@Bean
public RedissonClient redissonClient(){
Config config = new Config();
config.useSingleServer().setAddress(host).setPassword(password).se
tDatabase(database);
return Redisson.create(config);
}
}
redisson :addr :singleAddr :host : redis : //47.96.11.185 : 6370password : 12345678database : 0
@Configuration
public class RedissonConfig {
@Value("${redisson.addr.singleAddr.host}")
private String host;
@Value("${redisson.addr.singleAddr.password}")
private String password;
@Value("${redisson.addr.singleAddr.database}")
private int database;
@Bean
public RedissonClient redissonClient(){
Config config = new Config();
config.useSingleServer().setAddress(host).setPassword(password).se
tDatabase(database);
return Redisson.create(config);
}
}
redisson :addr :cluster :hosts : redis : //47.96.11.185 : 6370,...,redis : //47.96.11.185 : 6373password : 12345678
@Configuration
public class RedissonConfig {
@Value("${redisson.addr.cluster.hosts}")
private String hosts;
@Value("${redisson.addr.cluster.password}")
private String password;
/**
* 集群模式
* @return
*/
@Bean
public RedissonClient redissonClient(){
Config config = new Config();
config.useClusterServers().addNodeAddress(hosts.split("
[,]"))
.setPassword(password)
.setScanInterval(2000)
.setMasterConnectionPoolSize(10000)
.setSlaveConnectionPoolSize(10000);
return Redisson.create(config);
}
}
redisson :addr :masterAndSlave :masterhost : redis : //47.96.11.185 : 6370slavehosts :redis : //47.96.11.185 : 6371,redis : //47.96.11.185 : 6372password : 12345678database : 0
@Configuration
public class RedissonConfig3 {
@Value("${redisson.addr.masterAndSlave.masterhost}")
private String masterhost;
@Value("${redisson.addr.masterAndSlave.slavehosts}")
private String slavehosts;
@Value("${redisson.addr.masterAndSlave.password}")
private String password;
@Value("${redisson.addr.masterAndSlave.database}")
private int database;
/**
* 主从模式
* @return
*/
@Bean
public RedissonClient redissonClient(){
Config config = new Config();
config.useMasterSlaveServers()
.setMasterAddress(masterhost)
.addSlaveAddress(slavehosts.split("[,]"))
.setPassword(password)
.setDatabase(database)
.setMasterConnectionPoolSize(10000)
.setSlaveConnectionPoolSize(10000);
return Redisson.create(config);
}
}
// 获取公平锁RLock lock = redissonClient . getFairLock ( skuId );// 获取⾮公平锁RLock lock = redissonClient . getLock ( skuId );
// 阻塞锁(如果加锁成功之后,超时时间为 30s ;加锁成功开启看⻔狗,剩 5s 延⻓过期时间)lock . lock ();// 阻塞锁(如果加锁成功之后,设置⾃定义 20s 的超时时间)lock . lock ( 20 , TimeUnit . SECONDS );// ⾮阻塞锁(设置等待时间为 3s ;如果加锁成功默认超时间为 30s )boolean b = lock . tryLock ( 3 , TimeUnit . SECONDS );// ⾮阻塞锁(设置等待时间为 3s ;如果加锁成功设置⾃定义超时间为 20s )boolean b = lock . tryLock ( 3 , 20 , TimeUnit . SECONDS );
lock . unlock ();
// 公平⾮阻塞锁RLock lock = redissonClient . getFairLock ( skuId );boolean b = lock . tryLock ( 3 , 20 , TimeUnit . SECONDS );
HashMap map = null ;加锁try {if ( isLock ){校验库存if ( 库存充⾜ ){保存订单保存快照修改库存删除购物⻋map = new HashMap ();...}}} catch ( Exception e ){e . printStackTrace ();} finally {释放锁}return map ;
/**
* 保存订单业务
*/
@Transactional
public Map addOrder(String cids, Orders order)
throws SQLException {
logger.info("add order begin...");
Map map = null;
//1.校验库存:根据cids查询当前订单中关联的购物⻋记录详情(包括库存)
String[] arr = cids.split(",");
List cidsList = new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
cidsList.add(Integer.parseInt(arr[i]));
}
//根据⽤户在购物⻋列表中选择的购物⻋记录的id 查询到对应的购物⻋记录
List list =
shoppingCartMapper.selectShopcartByCids(cidsList);
//加锁
boolean isLock = true;
String[] skuIds = new String[list.size()];
Map locks = new HashMap<>(); //⽤于存放当前订单的锁
for (int i = 0; i < list.size(); i++) {
String skuId = list.get(i).getSkuId();
boolean b = false;
try {
RLock lock = redissonClient.getLock(skuId);
b = lock.tryLock(10, 3, TimeUnit.SECONDS);
if (b) {
skuIds[i] = skuId;
locks.put(skuId, lock);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
isLock = isLock & b;
}
//如果isLock为true,表示“加锁”成功
try {
if (isLock){
//1.检验库存
boolean f = true;
String untitled = "";
list =
shoppingCartMapper.selectShopcartByCids(cidsList);
for (ShoppingCartVO sc : list) {
if (Integer.parseInt(sc.getCartNum()) >
sc.getSkuStock()) {
f = false;
}
untitled = untitled + sc.getProductName() + ",";
}
if (f) {
//如果库存充⾜,则进⾏下订单操作
logger.info("product stock is OK...");
//2.保存订单
order.setUntitled(untitled);
order.setCreateTime(new Date());
order.setStatus("1");
//⽣成订单编号
String orderId =
UUID.randomUUID().toString().replace("-", "");
order.setOrderId(orderId);
int i = ordersMapper.insert(order);
//3.⽣成商品快照
for (ShoppingCartVO sc : list) {
int cnum = Integer.parseInt(sc.getCartNum());
String itemId = System.currentTimeMillis() +
"" + (new Random().nextInt(89999) + 10000);
OrderItem orderItem = new OrderItem(itemId,
orderId, sc.getProductId(), sc.getProductName(),
sc.getProductImg(), sc.getSkuId(), sc.getSkuName(), new
BigDecimal(sc.getSellPrice()), cnum, new
BigDecimal(sc.getSellPrice() * cnum), new Date(), new Date(), 0);
orderItemMapper.insert(orderItem);
//增加商品销量
}
//4.扣减库存:根据套餐ID修改套餐库存量
for (ShoppingCartVO sc : list) {
String skuId = sc.getSkuId();
int newStock = sc.getSkuStock() -
Integer.parseInt(sc.getCartNum());
ProductSku productSku = new ProductSku();
productSku.setSkuId(skuId);
productSku.setStock(newStock);
productSkuMapper.updateByPrimaryKeySelective(productSku);
//5.删除购物⻋:当购物⻋中的记录购买成功之后,购物⻋中对应
做删除操作
for (int cid : cidsList) {
shoppingCartMapper.deleteByPrimaryKey(cid);
}
map = new HashMap<>();
logger.info("add order finished...");
map.put("orderId", orderId);
map.put("productNames", untitled);
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
//释放锁
for (int i = 0; i < skuIds.length; i++) {
String skuId = skuIds[i];
if (skuId != null && !"".equals(skuId)) {
locks.get(skuId).unlock();
System.out.println("-----------------------
unlock");
}
}
}
return map; }