目次
分散ロック ソリューション_データベース悲観的ロックによって実装された分散ロック
分散ロックの問題_デモの問題
オーダーサービス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();
}
圧力測定スループット