手写批量缓存队列

手写批量缓存队列

场景

我希望从MySQL同步数据到另一个大数据数据库中,如Hive,Doris。

我希望在这个同步的过程中做一定的数据处理,只保留可以用来分析的数据。

我对这个数据的实时性要求不高,最终一致即可。

这个大数据数据库暴露了HTTP接口来新增数据。

那么对于这么一个场景,我们该如何处理是好?

问题分析

首先,这个问题很明显是需要用队列解决。

如果是一个单纯的队列的话,MySQL每增加一个数据,那么我们就需要把MySQL的磁盘中的数据放入内存中进行逻辑处理,再发一次HTTP请求,去同步,这样做无疑效率是很低下的。

因为每一条数据都会对应一次从磁盘到内存的再到建立HTTP连接,结束HTTP连接的过程。

问题解决

那么参考MySQL对每条查询语句都会把它放入buffer pool的思路,我们可以想到,我们可以让这个队列具备等待的能力,只有这个队列的数量满足一定额度,我们才会处理。这样就可以做到让多条数据共用一次HTTP请求,减少与Doris连接的消耗。

那么如果此时队列已经满最大额度了,就让这个线程休眠等待即可。

代码实战

csharp 复制代码
public class BulkCacheQueue<T>  {
​
    private ConcurrentLinkedQueue<T> cacheQueue;
​
    private int capacity;
​
​
    private long timeout;
​
​
    private AtomicInteger counter;
​
 
    private final ReentrantLock lock;
​
    private long startTime;
​
​
    private AtomicBoolean reset = new AtomicBoolean(true);
​
 
    private final Condition notFull;
​
    /**
     * Constructor
     *
     * @param capacity
     * @param timeout
     */
    public BulkCacheQueue(int capacity, long timeout) {
        this.capacity = capacity;
        this.timeout = timeout;
        this.cacheQueue = new ConcurrentLinkedQueue<>();
        this.counter = new AtomicInteger(capacity);
        this.lock = new ReentrantLock();
        this.notFull = lock.newCondition();
    }
​
    /**
     * 
     * 此方法会一直等待直到push成功
     *
     * @param object
     */
    public void push(T object) {
        checkNotNull(object);
        final ReentrantLock currentLock = this.lock;
        currentLock.lock();
        try {
            while (counter.get() == 0) {
                notFull.await();
            }
            cacheQueue.offer(object);
            counter.decrementAndGet();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            currentLock.unlock();
        }
    }
​
    /**
     * 如果队列已满,立刻刷新
     * 如果超过时间也立刻刷新
     *
     * @return
     */
    public List<T> aPull() {
        final ReentrantLock currentLock = this.lock;
        currentLock.lock();
        if (reset.get()) {
            startTime = currentTimeMillis();
            reset.set(false);
        }
        try {
            if (counter.get() == 0 || currentTimeMillis() - startTime >= MILLISECONDS.toMillis(timeout)) {
                return flushToList();
            }
        } finally {
            currentLock.unlock();
        }
        return new ArrayList<>();
    }
​
    private List<T> flushToList() {
        final List<T> list = new ArrayList<>();
        while (!cacheQueue.isEmpty()) {
            list.add(cacheQueue.poll());
        }
        counter.set(capacity);
        reset.set(true);
        notFull.signal();
        return list;
    }
​
    /**
     * 立刻刷新队列
     */
    public List<T> flush(boolean force) {
        if (force) {
            counter.set(0);
        }
        return aPull();
    }
}
相关推荐
FPGA33 分钟前
探讨4B/5B编码、8B/10B编码区别以及FPGA实现
数据结构
秋难降33 分钟前
线段树的深度解析(最长递增子序列类解题步骤)
数据结构·python·算法
Java微观世界36 分钟前
Object核心类深度剖析
java·后端
MrSYJ40 分钟前
为什么HttpSecurity会初始化创建两次
java·后端·程序员
专注API从业者40 分钟前
基于 Flink 的淘宝实时数据管道设计:商品详情流式处理与异构存储
大数据·前端·数据库·数据挖掘·flink
用户4822137167751 小时前
C++——类和对象
后端
bcbnb1 小时前
移动端webview网页调试实战,首屏加载缓慢问题的排查与优化
后端
00后程序员1 小时前
Charles抓包工具全面解析 API调试与网络分析的必备利器
后端
AAA修煤气灶刘哥2 小时前
Java+AI 驱动的体检报告智能解析:从 PDF 提取到数据落地全指南
java·人工智能·后端
xcya2 小时前
深入理解“看门狗”机制:一种优雅的自动续期模式
后端