原创

你知道JDK的延迟队列吗?

1. 我用延迟队列保证redis的key执行速度均匀!

最近,在搞redis的时候,有个问题让人头大:redis过期数据内存占用过大,没有及时清理(redis觉得内存还没大到影响命令执行速度的时候,是不会触发大批量清理的),因此内存已经大到13G了。

alt

但是我们可以不借助redis清除策略去清理,去访问每一个过期的key,先通过scan来安全扫描redis的key,再ttl检测key是否过期,过期数据删除,这样就能清楚redis的过期key内存,保证redis的安全运行。

alt

为了保证key的del速度均匀,不影响线上业务。这里借助延迟队列来实现:DelayQueue

2. 实现原理

将每次扫描得到的过期key放入到延迟队列的item中,并随机设置一个大范围内将来的删除时间(保证时间点分布均匀)。
开启另外一个单线程池来执行删除item中的key

DelayQueue的Item类

延迟元素类需要实现Delaye接口,覆盖compareTo,getDelay方法。这样的元素类对象才会被延迟队列识别并处理。

class Item implements Delayed ,Runnable{
    /* 触发时间*/
    private long time;
    String name;

    public Item(String name, long time, TimeUnit unit) {
        this.name = name;
        this.time = System.currentTimeMillis() + (time > 0? unit.toMillis(time): 0);
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return time - System.currentTimeMillis();
    }

    @Override
    public int compareTo(Delayed o) {
        Item item = (Item) o;
        long diff = this.time - item.time;
        if (diff >= 0) {// 改成>=会造成问题
            return 1;
        }else {
            return -1;
        }
    }

    @Override
    public String toString() {
        return "Item{" +
                "time=" + time +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
         System.out.format("name:{%s}, time:{%s}\n",this.name, LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
    }
}

测试模块

public class DelayedQueneTest {

    static ExecutorService executor = Executors.newSingleThreadExecutor();

    public static void main(String[] args) throws InterruptedException {
        Item item1 = new Item("item1", 5, TimeUnit.SECONDS);
        Item item2 = new Item("item2",10, TimeUnit.SECONDS);
        Item item3 = new Item("item3",15, TimeUnit.SECONDS);
        DelayQueue<Item> queue = new DelayQueue<>(); 
        queue.put(item1);
        queue.put(item2);
        queue.put(item3);
        System.out.println("begin time:" + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

        System.out.println("2323".split(",")[0]);
        executor.submit(queue.take());
        executor.submit(queue.take());
        executor.submit(queue.take()); 


//        for (int i = 0; i < 3; i++) {
//            Item take = queue.take();
//            System.out.format("name:{%s}, time:{%s}\n",take.name, LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
//        }
    }

}

好的,今天延迟队列的分享就到这里了,大家可以实现下redis过期key清理的方案。

alt

正文到此结束
本文目录