分散ロック【売られ過ぎ問題を解決する同期ロック、分散ロック解決策、悲観ロックによる分散ロックをベースに】(2)~徹底詳細解説(学習のまとめ~入門から深化まで)

 

目次

 分散ロックの問題_デモの問題

 同期ロックに基づいて売られすぎ問題を解決

分散ロックソリューション

 分散ロック実装スキーム

 分散ロック ソリューション_データベース悲観的ロックによって実装された分散ロック

 プロジェクト内の更新に使用します


 

 分散ロックの問題_デモの問題

 オーダーサービス9090を開始

 スタートオーダーサービス9091

 

 2 つの SpringBoot サービスを作成する

 Nginxサービスを開始する

Nginx Windows サービスをダウンロード、公式 Web サイト 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リクエストパラメータを設定する

 在庫データの表示

 注文データの表示

 同期ロックに基づいて売られすぎ問題を解決

 Spring は統一された抽象化を実行して、PlatformTransactionManager トランザクション マネージャー インターフェイスを形成し、トランザクションの送信やロールバックなどのすべての操作はそれに委ねられます。

トランザクション機能の全体的なインターフェース設計 

3 つのインターフェース機能 つまり、トランザクションマネージャーは、トランザクションを操作する際に、トランザクションの基本情報に基づいてトランザクションの状態を更新します。

 メソッドのロック

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 理論によれば、「分散システムは一貫性、可用性、およびパーティション耐性を同時に満たすことはできず、同時に 2 つしか満たせない」ため、多くのシステムは設計の開始時に、これら 3 つの間でトレードオフを行う必要があります。インターネット分野のほとんどのシナリオでは、システムの高可用性と引き換えに強い一貫性を犠牲にする必要があり、多くの場合、システムは、最終時刻がユーザーの許容範囲内である限り、「最終一貫性」を確保するだけで済みます。

 分散ロック実装スキーム

データベース実装に基づいた分散ロック

 データベースに基づく分散ロックは、主にデータベースの一意のインデックスを使用して実装されます。一意のインデックスは当然排他的であり、ロックの要件を満たしています。同時にロックを取得できるのは 1 つの競合他社のみです。

 Redis に基づく分散ロック

 Redis を使用して分散ロックを実装すると、最も高い効率と最速のロック速度が得られます。これは、Redis がほぼ純粋なメモリ操作である一方、データベース ベースと Zookeeper ベースのソリューションは両方ともディスク ファイル IO を必要とするため、効率が比較的低いためです。一般に、Redis は、Redis の SETNX キー値コマンドを使用して分散ロックを実装します。キーが存在しない場合にのみ成功し、キーがすでに存在する場合、コマンドは失敗します。

Zookeeper に基づく分散ロック 

 Zookeeper は一般的に設定センターとして使用されます. 分散ロックの実装原理は Redis と同様です. Zookeeper で一時的に順次ノードを作成し、ノードを繰り返し作成できないという機能を利用して排他性を確保します。

 分散ロック ソリューション_データベース悲観的ロックによって実装された分散ロック

 悲観的ロックとは何ですか

名前が示すように、これは悲観的なロックであり、常に最悪のケースを想定しており、データを取得するたびに、他の人がそのデータを変更すると考え、データを取得するたびにロックし、他の人がデータを取得したい場合は、ロックを取得するまでブロックされます。

アップデートのデモ

2 つのコマンド ライン インターフェイスを開く

 アップデートのテスト

 プロジェクト内の更新に使用します

設定ファイルに設定を追加する

mybatis-plus:
 mapper-locations: classpath:mapper/*.xml

マッパー追加メソッド

public interface ProductMapper extends BaseMapper<Product> {
    Product  findById(@Param("id")Integer id);
}

マッパーがステートメントを書きます

<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();
   }

圧力測定スループット

おすすめ

転載: blog.csdn.net/m0_58719994/article/details/131711163