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

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 分布式锁【 基于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

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
3月前
|
存储 SQL 微服务
常用的分布式事务解决方案(三)
常用的分布式事务解决方案(三)
|
3月前
|
关系型数据库 MySQL
常见分布式事务的解决方案(一)
常见分布式事务的解决方案(一)
|
4月前
|
存储 SQL 分布式数据库
OceanBase 入门:分布式数据库的基础概念
【8月更文第31天】在当今的大数据时代,随着业务规模的不断扩大,传统的单机数据库已经难以满足高并发、大数据量的应用需求。分布式数据库应运而生,成为解决这一问题的有效方案之一。本文将介绍一款由阿里巴巴集团自主研发的分布式数据库——OceanBase,并通过一些基础概念和实际代码示例来帮助读者理解其工作原理。
350 0
|
2月前
|
消息中间件 关系型数据库 Java
‘分布式事务‘ 圣经:从入门到精通,架构师尼恩最新、最全详解 (50+图文4万字全面总结 )
本文 是 基于尼恩之前写的一篇 分布式事务的文章 升级而来 , 尼恩之前写的 分布式事务的文章, 在全网阅读量 100万次以上 , 被很多培训机构 作为 顶级教程。 此文修改了 老版本的 一个大bug , 大家不要再看老版本啦。
|
3月前
|
消息中间件 中间件 关系型数据库
常用的分布式事务解决方案(四)
常用的分布式事务解决方案(四)
|
3月前
常用的分布式事务解决方案(二)
常用的分布式事务解决方案(二)
|
3月前
|
Dubbo Java 应用服务中间件
分布式-dubbo的入门
分布式-dubbo的入门
|
3月前
|
机器学习/深度学习 算法 自动驾驶
深度学习之分布式智能体学习
基于深度学习的分布式智能体学习是一种针对多智能体系统的机器学习方法,旨在通过多个智能体协作、分布式决策和学习来解决复杂任务。这种方法特别适用于具有大规模数据、分散计算资源、或需要智能体彼此交互的应用场景。
173 4
|
4月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
这篇文章介绍了如何在SpringBoot项目中整合Redis,并探讨了缓存穿透、缓存雪崩和缓存击穿的问题以及解决方法。文章还提供了解决缓存击穿问题的加锁示例代码,包括存在问题和问题解决后的版本,并指出了本地锁在分布式情况下的局限性,引出了分布式锁的概念。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
|
4月前
|
机器学习/深度学习 并行计算 PyTorch
PyTorch与DistributedDataParallel:分布式训练入门指南
【8月更文第27天】随着深度学习模型变得越来越复杂,单一GPU已经无法满足训练大规模模型的需求。分布式训练成为了加速模型训练的关键技术之一。PyTorch 提供了多种工具来支持分布式训练,其中 DistributedDataParallel (DDP) 是一个非常受欢迎且易用的选择。本文将详细介绍如何使用 PyTorch 的 DDP 模块来进行分布式训练,并通过一个简单的示例来演示其使用方法。
452 2