累加容器

544 阅读2分钟

场景: 在并发场景下更新数据库同一条数据(update *** from ** where ***), 当并发量上来时会造成上一条更新未完成,下一条更新指令又发起了,这时新的请求会被挂起,等待前一个命令执行完毕,当积累很多会造成明显的慢sql;

方案:one: 合并多次请求,当更新数到达一定量级时在更新数据到DB

源代码


importJava.util.HashMap;
importJava.util.Map;
importJava.util.Objects;
importJava.util.concurrent.atomic.AtomicInteger;
importJava.util.function.BiFunction;

/**
 * 累加容器 The type Accumulation container.
 *
 * @author zhairuihao
 */
public class AccumulationContainer {
  private final int limit;

  private static final int DEFAULT_SIZE = 100;

  private static final int DEFAULT_INITIAL_CAPACITY = 1000;

  private static Map<String, AtomicInteger> MAP;

  /** Instantiates a new Accumulation container. */
  public AccumulationContainer() {
    this.limit = DEFAULT_SIZE;
    MAP = new HashMap<>(DEFAULT_INITIAL_CAPACITY);
  }

  /**
   * Instantiates a new Accumulation container.
   *
   * @param size the size
   * @param initialCapacity the initial capacity
   */
  public AccumulationContainer(final Integer size, final int initialCapacity) {
    this.limit = size;
    MAP = new HashMap<>(initialCapacity);
  }

  /**
   * Execute.
   *
   * @param <T> the type parameter
   * @param key the key
   * @param val the val
   * @param function the consumer
   * @return the t
   */
  public synchronized <T> T execute(
      final String key, final int val, final BiFunction<String, Integer, T> function) {
    AtomicInteger atomicInteger = MAP.get(key);
    if (Objects.isNull(atomicInteger)) {
      atomicInteger = new AtomicInteger(0);
      MAP.put(key, atomicInteger);
    }
    final int value = atomicInteger.addAndGet(val);
    if (value >= this.limit) {
      this.remove(key);
      return function.apply(key, value);
    }
    return null;
  }

  /**
   * Execute.
   *
   * @param <T> the type parameter
   * @param key the key
   * @param function the consumer
   * @return the t
   */
  public synchronized <T> T execute(
      final String key, final BiFunction<String, Integer, T> function) {
    return this.execute(key, 1, function);
  }

  private synchronized void remove(final String key) {
    MAP.put(key, new AtomicInteger(0));
  }
}

测试代码

public class AccumulationContainer {
static AtomicInteger atomicInteger =  AtomicInteger(0);
  public static void main(String args[]) {
    AccumulationContainer accumulationContainer = new AccumulationContainer();

    for (int i = 0; i < 1000; i++) {
      new Thread(() -> test(accumulationContainer)).start();
    }
  }

  private static void test(AccumulationContainer accumulationContainer) {
    for (int i = 0; i < 100; i++) {
      System.out.println(" i: " + i);
      accumulationContainer.execute(
          "1",
          (key, val) -> {
            System.out.println(" key val" + key + "---" + val);
            System.out.println(
                "   atomicInteger.incrementAndGet()" + atomicInteger.incrementAndGet());
            return val;
          });
    }
  }
}

测试结果

 i: 94
 i: 95
 i: 96
 i: 97
 i: 98
 i: 99
 i: 98
 i: 99
 i: 95
 i: 96
 i: 97
 i: 98
 i: 99
 i: 97
 i: 98
 i: 99
 key val1---100
   atomicInteger.incrementAndGet()1000

Process finished with exit code 0

方案:two: 更新指令变更为插入指令, 通过定时任务来完成数据的最终一致性