目录
分布式锁问题_演示问题
基于synchronized锁解决超卖问题
分布式锁解决方案
分布式锁实现方案
分布式锁解决方案_数据库悲观锁实现的分布式锁
项目中使用for update
分布式锁问题_演示问题
启动订单服务9090
启动订单服务9091
创建两个SpringBoot服务
启动Nginx服务
下载Nginx windows服务,官网http://nginx.org/en/download.html
配置负载均衡
编辑nginx.conf文件添加负载均衡的配置。
upstream test{
server localhost:9090 ;
server localhost:9091 ;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://test;
}
}
启动Nginx服务
在nginx目录下面
启动JMeter压测工具
添加线程组 -> HTTP请求 -> 察看结果树 -> 聚合报告
设置Http请求参数
查看库存数据
查看订单数据
基于synchronized锁解决超卖问题
Spring进行了统一的抽象,形成了 PlatformTransactionManager事务管理器接口 , 事务的 提交、回滚等操作 全部交给它来实现。
事务功能的总体接口设计
三个接口功能一句话总的来说事务管理器基于事务基础信息在操作 事务时候对事务状态进行更新。
方法加锁
public synchronized String createOrder(Integer produceId, Integer purchaseNum) { }
声明事务管理器
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
手动创建事务
TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
手动提交事务
/**
* 创建订单
*
* @param produceId 商品id
* @param purchaseNum 购买数量
* @return
*/
@Override
public synchronized String createOrder(Integer produceId, Integer purchaseNum) {
TransactionStatus transaction = platformTransactionManager.getTransaction(trans
actionDefinition);
// 1、根据商品id获取商品信息
Product product = productMapper.selectById(produceId);
// 2、判断商品是否存在
if (product == null) {
platformTransactionManager.rollback(transaction);
throw new RuntimeException("购买商品不存在");
}
log.info(Thread.currentThread().getName() + "库存数量" + product.getCount());
// 3、校验库存
if (purchaseNum > product.getCount()) {
platformTransactionManager.rollback(transaction);
throw new RuntimeException("库存不足");
}
// 4、更新库存操作
int count = product.getCount() - purchaseNum;
product.setCount(count);
productMapper.updateById(product);
// 5、创建订单
TOrder order = new TOrder();
order.setOrderStatus(1);//订单状态
order.setReceiverName("张三");
order.setReceiverMobile("18587781058");
order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseNum)));//订单价格
baseMapper.insert(order);
// 6、创建订单和商品关联
OrderItem orderItem = new OrderItem();
orderItem.setOrderId(order.getId());//订单id
orderItem.setProduceId(product.getId());// 商品id
orderItem.setPurchasePrice(product.getPrice());// 购买价格
orderItem.setPurchaseNum(purchaseNum);// 购买数量
orderItemMapper.insert(orderItem);
//提交事务
platformTransactionManager.commit(transaction);
return order.getId();
}
分布式锁解决方案
分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一 致性(Consistency)、可用性(Availability)和分区容错性 (Partition tolerance),最多只能同时满足两项。”所以,很多系 统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的 场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只 需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。
分布式锁实现方案
基于数据库实现的分布式锁
基于数据库实现分布式锁主要是利用数据库的唯一索引来实现,唯 一索引天然具有排他性,这刚好符合我们对锁的要求:同一时刻只能允许一个竞争者获取锁。
基于 Redis 实现的分布式锁
使用Redis来实现分布式锁的效率最高,加锁速度最快,因为Redis 几乎都是纯内存操作,而基于数据库的方案和基于Zookeeper的方 案都会涉及到磁盘文件IO,效率相对低下。一般使用Redis来实现分 布式锁都是利用Redis的 SETNX key value 这个命令,只有当key不存在时 才会执行成功,如果key已经存在则命令执行失败。
基于 Zookeeper 实现的分布式锁
Zookeeper一般用作配置中心,其实现分布式锁的原理和Redis类 似,我们在Zookeeper中创建临时顺序节点,利用节点不能重复创建的特性来保证排他性。
分布式锁解决方案_数据库悲观锁实现的分布式锁
什么是悲观锁
顾名思义,就是比较悲观的锁,总是假设最坏的情况,每次去拿数 据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁, 这样别人想拿这个数据就会阻塞直到它拿到锁。
演示for update
开启2个命令行界面
测试for update
项目中使用for update
配置文件添加配置
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
mapper添加方法
public interface ProductMapper extends BaseMapper<Product> {
Product findById(@Param("id")Integer id);
}
mapper编写语句
<select id="findById" parameterType="int" resultType="com.tong.lock.entity.Product">
select * from product where id = #{id} for update
</select>
修改订单接口实现类
@Transactional(rollbackFor = Exception.class)
@Override
public String createOrder(Integer productId, Integer count) {
// 1、根据商品id查询商品信息
Product product = productMapper.findById(productId);
// 2、判断商品是否存在
if (product == null) {
throw new RuntimeException("购买商品不存在:" + productId + "不存在");
}
// 3、校验库存
if (count > product.getCount()) {
throw new RuntimeException("商品" +productId + "仅剩" + product.getCount() + "件,无法购买");
}
// 4、计算库存
Integer leftCount = product.getCount() - count;
// 5、更新库存
product.setCount(leftCount);
productMapper.updateById(product);
// 6、 创建订单
TOrder order = new TOrder();
order.setOrderStatus(1);//待处理
order.setReceiverName("张三");
order.setReceiverMobile("18587781068");
order.setOrderAmount(product.getPrice().multiply(new BigDecimal(count)));//订单价格
baseMapper.insert(order);
// 7、 创建订单和商品关系数据
OrderItem orderItem = new OrderItem();
orderItem.setOrderId(order.getId());
orderItem.setProduceId(product.getId());
orderItem.setPurchasePrice(product.getPrice());
orderItem.setPurchaseNum(count);
orderItemMapper.insert(orderItem);
return order.getId();
}
压测吞吐量