分布式锁问题_演示问题
启动订单服务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(); }
压测吞吐量