<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          CAS+失敗重試方式實(shí)現(xiàn)數(shù)據(jù)庫的原子性更新

          共 11340字,需瀏覽 23分鐘

           ·

          2022-07-31 17:15

          在前面一篇文章中我們講了Java底層基于Unsafe的CAS實(shí)現(xiàn)《什么是CAS?如果說不清楚,這篇文章要讀一讀!》,這篇文章來看看基于Spring和CAS理念來實(shí)現(xiàn)的樂觀鎖方案。

          在數(shù)據(jù)庫修改單條數(shù)據(jù)時,常用的方式是select for update的悲觀鎖機(jī)制,如果鎖競爭比較大,沒有獲得鎖的操作會阻塞。使用CAS樂觀鎖的方式,可以大大提高并發(fā)性。例如,在分布式服務(wù)中,多個用戶并發(fā)下單操作前會先扣減庫存時,服務(wù)1、服務(wù)2和服務(wù)3為不同機(jī)器上的庫存服務(wù)。

          庫存扣減操作流程如下:

          扣減庫存

          使用CAS方式的樂觀鎖,當(dāng)庫存還剩3個,3個用戶同時下單,服務(wù)同時扣減庫存,可以并發(fā)地扣減成功,提高了并發(fā)性。如果庫存還剩1個,3個用戶同時下單,同時扣減庫存,這時只有1個用戶會操作成功,其余2個會失敗,避免了超賣。

          在庫存的CAS操作中,先進(jìn)行庫存查詢操作,然后根據(jù)value+version方式修改庫存,如果操作失敗,則冪等地重復(fù)操作。通常會從操作次數(shù)和執(zhí)行時間兩個條件限制CAS的操作,如果兩個條件中有某個條件觸發(fā),可以拋出樂觀鎖異常。

          在Java項(xiàng)目中,通過AOP+注解的方式實(shí)現(xiàn)數(shù)據(jù)庫操作的CAS冪等操作的統(tǒng)一處理。定義OptimisticRetry注解,標(biāo)識CAS重試Spring AOP的切點(diǎn),并且設(shè)置屬性value(最大重試次數(shù)條件),以及屬性maxExecuteTime(最大執(zhí)行時間條件)。OptimisticRetryAOP定義CAS重試的切面,樂觀鎖的實(shí)現(xiàn)。OptimisticRetry注解定義代碼如下:

          /**
           * 樂觀鎖的重試
           **/
          @Retention(RetentionPolicy.RUNTIME)
          @Target(ElementType.METHOD)
          public @interface OptimisticRetry {
           
              /**
               * 最大重試次數(shù)
               */
              int value() default 200;
           
              /**
               * 最大執(zhí)行時間
               */
              int maxExecuteTime() default 2 * 60 * 60 * 1000;
          }

          OptimisticRetryAOP切面,定義標(biāo)注了OptimisticRetry注解的方法則進(jìn)行,CAS冪等重試,如果達(dá)到最大重試次數(shù)限制或者最大執(zhí)行時間限制,則拋出樂觀鎖異常OptimisticLockingFailureException。

          代碼如下:

          /**
           * 樂觀鎖的重試
           **/
          @Aspect
          @Component
          @Order(10000)
          @Slf4j
          public class OptimisticRetryAOP {
           
              @Pointcut("@annotation(com.smcx.winemall.common.lock.OptimisticRetry)|| @within(com.smcx.winemall.common.lock.OptimisticRetry)")
              public void optimisticRetryPointcut() {
           
              }
           
              @Around("optimisticRetryPointcut()")
              public Object doConcurrentOperation(ProceedingJoinPoint joinPoint) throws Throwable {
           
                  Signature signature = joinPoint.getSignature();
                  MethodSignature methodSignature = (MethodSignature) signature;
                  // 代理目標(biāo)對象Class
                  Class targetClazz = joinPoint.getTarget().getClass();
                  Method method = targetClazz.getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
                  Optional<OptimisticRetry> optimisticRetryOption = getOptimisticRetry(method.getAnnotations());
                  if (!optimisticRetryOption.isPresent()) {
           
                      log.warn("no OptimisticRetry Annotation to execute!");
                      return joinPoint.proceed();
                  }
                  OptimisticRetryConfig optimisticRetryConfig = obtainOptimisticRetryConfig(optimisticRetryOption.get());
                  int numAttempts = 0;
                  OptimisticLockingFailureException lockFailureException;
                  long startTime = System.currentTimeMillis();
                  do {
                      numAttempts++;
                      try {
                          return joinPoint.proceed();
                      } catch (OptimisticLockingFailureException ex) {
           
                          lockFailureException = ex;
                          long executeTime = System.currentTimeMillis() - startTime;
                          long maxExecuteTime = optimisticRetryConfig.getMaxExecuteTime();
                          if (isLargerThanMaxExecuteTime(executeTime, maxExecuteTime)) {
           
                              log.warn("throw optimistic locking failure exception!num attempts [{}],start time [{}]," +
                                              "actual execute time [{}] ms, max execute time [{}] ms",
                                      numAttempts, startTime, executeTime, maxExecuteTime);
                              throw lockFailureException;
                          }
                      }
                  }
                  while (numAttempts <= optimisticRetryConfig.getMaxTryCount());
           
                  log.warn("throw optimistic locking failure exception!num attempts {} ", numAttempts);
                  throw lockFailureException;
              }
           
              /**
               * 大于限制的重試執(zhí)行時間
               *
               * @param executeTime
               * @return
               */
              private boolean isLargerThanMaxExecuteTime(long executeTime, long maxExecuteTime) {
           
                  if (executeTime <= 0) {
                      return false;
                  }
                  if (maxExecuteTime > executeTime) {
                      return true;
                  }
                  return false;
              }
           
              private OptimisticRetryConfig obtainOptimisticRetryConfig(OptimisticRetry optimisticRetry) {
           
                  // 從注解中獲取配的值
                  OptimisticRetryConfig optimisticRetryConfig = new OptimisticRetryConfig();
                  optimisticRetryConfig.setMaxTryCount(optimisticRetry.value());
                  optimisticRetryConfig.setMaxExecuteTime(optimisticRetry.maxExecuteTime());
                  return optimisticRetryConfig;
              }
           
              private Optional<OptimisticRetry> getOptimisticRetry(Annotation[] annotations) {
           
                  if (ArrayUtils.isEmpty(annotations)) {
                      return Optional.empty();
                  }
                  for (Annotation anno : annotations) {
                      if (anno.annotationType().getName().equals(OptimisticRetry.class.getName())) {
                          return Optional.of((OptimisticRetry) anno);
                      }
                  }
                  return Optional.empty();
              }
           
              /**
               * 重試配置
               */
              @Data
              private static class OptimisticRetryConfig implements Serializable {
           
                  private static final long serialVersionUID = -182211651320526367L;
           
                  /**
                   * 最大重試次數(shù)
                   */
                  private int maxTryCount;
           
                  /**
                   * 最大執(zhí)行時間
                   */
                  private long maxExecuteTime;
              }
          }

          業(yè)務(wù)代碼中操作,代碼如下:

            @Override
              @Transactional(rollbackFor = Exception.class)
              @OptimisticRetry
              public void decreaseStockRemainingAmount(OperateStockDto operateStock) {
           
                  // 獲取存儲
                  WinemallStock existStock = winemallStockMapper.selectByOperateStock(operateStock);
                  // TODO 庫存是否充足判斷等條件判斷
           
                  // 修改庫存數(shù)據(jù)
                  if (0 == winemallStockMapper.updateStockRemainingAmount(existStock, 1)) {
                      throw new OptimisticLockingFailureException("decrease stock remaining amount optimistic locking failure!");
                  }
              }

          來源:blog.csdn.net/new_com/article/details/103834871

          面試被問Linux 命令su和sudo的區(qū)別?

          2022-07-26

          到底如何保證線程安全,你真的清楚嗎?(干貨推薦)

          2022-07-25

          4種 Redis 集群方案介紹+優(yōu)缺點(diǎn)對比

          2022-07-24

          2.2w Star,這是一款什么樣的Nginx可視化配置神器?

          2022-07-23

          什么是CAS?如果說不清楚,這篇文章要讀一讀!

          2022-07-21





          如果你覺得這篇文章不錯,那么,下篇通常會更好。備注“公眾號”添加微信好友(微信號:zhuan2quan)。

          ▲ 按關(guān)注”程序新視界“,洞察技術(shù)內(nèi)幕


          瀏覽 94
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  手机在线免费看黄色电影一级片 | 久久香蕉精品视频 | 操逼操逼操逼操逼操 | 婷婷综合五月激情 | 北条麻妃一区二区三区 |