手写批量缓存队列

手写批量缓存队列

场景

我希望从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();
    }
}
相关推荐
我爱娃哈哈23 分钟前
Spring Cloud Gateway + 请求聚合(GraphQL-like):一次调用合并多个微服务响应
后端
用户298698530141 小时前
C#:三行代码,给 Word 文档的文本框“一键清空”
后端·c#·.net
血小溅1 小时前
Claude Code Superpowers 插件基础教程
后端
树獭叔叔1 小时前
OpenClaw Agents 系统:多代理架构与智能编排的完整技术解析
后端·aigc·openai
蝎子莱莱爱打怪2 小时前
ESXi 强制断电后恢复CentOS7虚拟机避坑指南:解决重复注册&目录清理难题
linux·后端·程序员
ConardLi3 小时前
OpenClaw 完全指南:这可能是全网最新最全的系统化教程了!
前端·人工智能·后端
树獭叔叔3 小时前
OpenClaw Workspace 文件完整指南:从文件到 AI 行为的完整链路
后端·aigc·openai
神奇小汤圆4 小时前
别死记硬背!Java的CountDownLatch 核心原理:AQS state 才是关键
后端
ssshooter4 小时前
告别 Chat Completions:深度解析 AI 接口新标准 `/v1/responses`
人工智能·后端·开源
武子康4 小时前
大数据-244 离线数仓 - Hive ODS 层建表与分区加载实战(DataX→HDFS→Hive)
大数据·后端·apache hive