手写批量缓存队列

手写批量缓存队列

场景

我希望从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();
    }
}
相关推荐
小池先生几秒前
springboot启动不了 因一个spring-boot-starter-web底下的tomcat-embed-core依赖丢失
java·spring boot·后端
小蜗牛慢慢爬行1 小时前
如何在 Spring Boot 微服务中设置和管理多个数据库
java·数据库·spring boot·后端·微服务·架构·hibernate
波音彬要多做1 小时前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法
Noah_aa1 小时前
代码随想录算法训练营第五十六天 | 图 | 拓扑排序(BFS)
数据结构
wm10432 小时前
java web springboot
java·spring boot·后端
KpLn_HJL2 小时前
leetcode - 2139. Minimum Moves to Reach Target Score
java·数据结构·leetcode
龙少95433 小时前
【深入理解@EnableCaching】
java·后端·spring
溟洵5 小时前
Linux下学【MySQL】表中插入和查询的进阶操作(配实操图和SQL语句通俗易懂)
linux·运维·数据库·后端·sql·mysql
SomeB1oody8 小时前
【Rust自学】6.1. 定义枚举
开发语言·后端·rust
AC使者8 小时前
5820 丰富的周日生活
数据结构·算法