分布式锁【 基于synchronized锁解决超卖问题、分布式锁解决方案、悲观锁实现的分布式锁】(二)-全面详解(学习总结---从入门到深化)

简介: 分布式锁【 基于synchronized锁解决超卖问题、分布式锁解决方案、悲观锁实现的分布式锁】(二)-全面详解(学习总结---从入门到深化)

分布式锁问题_演示问题



启动订单服务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();
   }


压测吞吐量

image.png

目录
相关文章
|
11月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
915 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
NoSQL Java 中间件
【📕分布式锁通关指南 02】基于Redis实现的分布式锁
本文介绍了从单机锁到分布式锁的演变,重点探讨了使用Redis实现分布式锁的方法。分布式锁用于控制分布式系统中多个实例对共享资源的同步访问,需满足互斥性、可重入性、锁超时防死锁和锁释放正确防误删等特性。文章通过具体示例展示了如何利用Redis的`setnx`命令实现加锁,并分析了简化版分布式锁存在的问题,如锁超时和误删。为了解决这些问题,文中提出了设置锁过期时间和在解锁前验证持有锁的线程身份的优化方案。最后指出,尽管当前设计已解决部分问题,但仍存在进一步优化的空间,将在后续章节继续探讨。
1730 131
【📕分布式锁通关指南 02】基于Redis实现的分布式锁
|
负载均衡 NoSQL 算法
Redisson分布式锁数据一致性解决方案
通过以上的设计和实现, Redisson能够有效地解决分布式环境下数据一致性问题。但是, 任何技术都不可能万无一失, 在使用过程中还需要根据实际业务需求进行逻辑屏障的设计和错误处理机制的建立。
675 48
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
3721 7
分布式锁—7.Curator的分布式锁
本文详细解析了Apache Curator库中多种分布式锁的实现机制,包括可重入锁、非可重入锁、可重入读写锁、MultiLock和Semaphore。可重入锁通过InterProcessMutex实现,支持同一线程多次加锁,锁的获取和释放通过Zookeeper的临时顺序节点实现。非可重入锁InterProcessSemaphoreMutex基于Semaphore实现,确保同一时间只有一个线程获取锁。可重入读写锁InterProcessReadWriteLock通过组合读锁和写锁实现,支持读写分离。Multi
|
NoSQL Java 测试技术
【📕分布式锁通关指南 05】通过redisson实现分布式锁
本文介绍了如何使用Redisson框架在SpringBoot中实现分布式锁,简化了之前通过Redis手动实现分布式锁的复杂性和不完美之处。Redisson作为Redis的高性能客户端,封装了多种锁的实现,使得开发者只需关注业务逻辑。文中详细展示了引入依赖、配置Redisson客户端、实现扣减库存功能的代码示例,并通过JMeter压测验证了其正确性。后续篇章将深入解析Redisson锁实现的源码。
699 0
【📕分布式锁通关指南 05】通过redisson实现分布式锁
|
运维 NoSQL 算法
【📕分布式锁通关指南 04】redis分布式锁的细节问题以及RedLock算法原理
本文深入探讨了基于Redis实现分布式锁时遇到的细节问题及解决方案。首先,针对锁续期问题,提出了通过独立服务、获取锁进程自己续期和异步线程三种方式,并详细介绍了如何利用Lua脚本和守护线程实现自动续期。接着,解决了锁阻塞问题,引入了带超时时间的`tryLock`机制,确保在高并发场景下不会无限等待锁。最后,作为知识扩展,讲解了RedLock算法原理及其在实际业务中的局限性。文章强调,在并发量不高的场景中手写分布式锁可行,但推荐使用更成熟的Redisson框架来实现分布式锁,以保证系统的稳定性和可靠性。
1322 0
【📕分布式锁通关指南 04】redis分布式锁的细节问题以及RedLock算法原理
|
NoSQL 关系型数据库 MySQL
分布式系统学习9:分布式锁
本文介绍了分布式系统中分布式锁的概念、实现方式及其应用场景。分布式锁用于在多个独立的JVM进程间确保资源的互斥访问,具备互斥、高可用、可重入和超时机制等特点。文章详细讲解了三种常见的分布式锁实现方式:基于Redis、Zookeeper和关系型数据库(如MySQL)。其中,Redis适合高性能场景,推荐使用Redisson库;Zookeeper适用于对一致性要求较高的场景,建议基于Curator框架实现;而基于数据库的方式性能较低,实际开发中较少使用。此外,还探讨了乐观锁和悲观锁的区别及适用场景,并介绍了如何通过Lua脚本和Redis的`SET`命令实现原子操作,以及Redisson的自动续期机
1445 7
|
消息中间件 算法 调度
分布式系统学习10:分布式事务
本文是小卷关于分布式系统架构学习系列的第13篇,重点探讨了分布式事务的相关知识。随着业务增长,单体架构拆分为微服务后,传统的本地事务无法满足需求,因此需要引入分布式事务来保证数据一致性。文中详细介绍了分布式事务的必要性、实现方案及其优缺点,包括刚性事务(如2PC、3PC)和柔性事务(如TCC、Saga、本地消息表、MQ事务、最大努力通知)。同时,还介绍了Seata框架作为开源的分布式事务解决方案,提供了多种事务模式,简化了分布式事务的实现。
817 5
|
存储 负载均衡 NoSQL
【赵渝强老师】Redis Cluster分布式集群
Redis Cluster是Redis的分布式存储解决方案,通过哈希槽(slot)实现数据分片,支持水平扩展,具备高可用性和负载均衡能力,适用于大规模数据场景。
804 2

热门文章

最新文章