设计超卖现象数据库
设计三张表
- order 订单
- order_item 订单项表
- product 商品表
product
product_name 商品名称
price 价格
count 数量
product_desc 商品描述
create_time 创建时间
create_user 创建人
update_time 更新时间
update_user 更新人
order
order_status 订单状态
receiver_name 收货人姓名
receiver_mobile 收货人电话
order_amount 订单金额
create_time 创建时间
create_user 创建人
update_time 更新时间
update_user 更新人
order_item
order_id 订单id
product_id 商品id
purchase_price 购买金额
purchase_num 购买数量
create_time 创建时间
create_user 创建人
update_time 更新时间
update_user 更新人
项目架构
package com.example.distributedemo.service;
import com.example.distributedemo.dao.OrderItemMapper;
import com.example.distributedemo.dao.OrderMapper;
import com.example.distributedemo.dao.ProductMapper;
import com.example.distributedemo.model.Order;
import com.example.distributedemo.model.OrderItem;
import com.example.distributedemo.model.Product;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Service
@Slf4j
public class OrderService {
@Resource
private OrderMapper orderMapper;
@Resource
private OrderItemMapper orderItemMapper;
@Resource
private ProductMapper productMapper;
//购买商品id
private int purchaseProductId = 100100;
//购买商品数量
private int purchaseProductNum = 1;
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
private Lock lock = new ReentrantLock();
// @Transactional(rollbackFor = Exception.class)
public Integer createOrder() throws Exception{
Product product = null;
lock.lock();
try {
TransactionStatus transaction1 = platformTransactionManager.getTransaction(transactionDefinition);
product = productMapper.selectByPrimaryKey(purchaseProductId);
if (product==null){
platformTransactionManager.rollback(transaction1);
throw new Exception("购买商品:"+purchaseProductId+"不存在");
}
//商品当前库存
Integer currentCount = product.getCount();
System.out.println(Thread.currentThread().getName()+"库存数:"+currentCount);
//校验库存
if (purchaseProductNum > currentCount){
platformTransactionManager.rollback(transaction1);
throw
new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件,无法购买");
}
//************
//计算剩余库存
Integer leftCount = currentCount - purchaseProductNum;
//更新库存
product.setCount(leftCount);
product.setUpdateTime(new Date());
product.setUpdateUser("xxx");
productMapper.updateByPrimaryKeySelective(product);
//*************
productMapper.updateProductCount(purchaseProductNum,"xxx",new Date(),product.getId());
platformTransactionManager.commit(transaction1);
}finally {
lock.unlock();
}
TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
Order order = new Order();
order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
order.setOrderStatus(1);//待处理
order.setReceiverName("xxx");
order.setReceiverMobile("13311112222");
order.setCreateTime(new Date());
order.setCreateUser("xxx");
order.setUpdateTime(new Date());
order.setUpdateUser("xxx");
orderMapper.insertSelective(order);
OrderItem orderItem = new OrderItem();
orderItem.setOrderId(order.getId());
orderItem.setProductId(product.getId());
orderItem.setPurchasePrice(product.getPrice());
orderItem.setPurchaseNum(purchaseProductNum);
orderItem.setCreateUser("xxx");
orderItem.setCreateTime(new Date());
orderItem.setUpdateTime(new Date());
orderItem.setUpdateUser("xxx");
orderItemMapper.insertSelective(orderItem);
platformTransactionManager.commit(transaction);
return order.getId();
}
}
测试用例
package com.example.distributedemo;
import com.example.distributedemo.service.OrderService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DistributeDemoApplicationTests {
@Autowired
private OrderService orderService;
@Test
public void concurrentOrder() throws InterruptedException {
Thread.sleep(60000);
CountDownLatch cdl = new CountDownLatch(5);
CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
ExecutorService es = Executors.newFixedThreadPool(5);
for (int i =0;i<5;i++){
es.execute(()->{
try {
cyclicBarrier.await();
Integer orderId = orderService.createOrder();
System.out.println("订单id:"+orderId);
} catch (Exception e) {
e.printStackTrace();
}finally {
cdl.countDown();
}
});
}
cdl.await();
es.shutdown();
}
}
CyclicBarrier cyclicBarrier = new CyclicBarrier(5);等5个线程再去执行
为什么要加CyclicBarrier,如果我们不加的话,for循环会一个一个执行,加上去可以让线程进行等待而达到同步的效果
CountDownLatch cdl = new CountDownLatch(5);的后面加了cdl.await();锁是想等子线程搞定后主线程再结束
如果想用数据库中的行锁解决超卖现象
将计算剩余库存和更新库存交由数据库去干
添加updateProductCount方法,利用增量更新库存
接口
数据库sql
但是你会发现一种现象