消息消费结果需要按插入顺序去重,团队在 LinkedHashSet 与 ConcurrentHashMap.newKeySet() 之间选型,如何结合并发、顺序、内存和吞吐要求落地?
进阶版来源:shopee中级工程师消息消费幂等去重插入顺序去重并发集合选型锁粒度优化
草稿
答案解析
如果题目明确要求“按插入顺序去重”,我不会只看线程安全,而是先确认两个语义:一是这里的插入顺序,是消息到达后的全局顺序,还是某个消费分区内的顺序;二是去重集合是否允许过期、淘汰,以及是否要求严格实时可见。
LinkedHashSet 本身不是线程安全的。它能天然保留插入顺序,并且去重逻辑符合要求,但在并发消费场景下,多个线程同时 add 可能导致数据竞争,遍历时也可能出现不一致。可以用显式锁保护 add、判断和遍历,例如统一使用 ReentrantLock,或者使用 Collections.synchronizedSet,但后者只能保证单个方法调用的线程安全,遍历仍然需要在外部加锁。这样实现简单、顺序准确,但所有操作都竞争同一把锁,消费线程多、消息量大时吞吐会明显下降。
ConcurrentHashMap.newKeySet() 的并发性能和扩展性更好,适合高并发下只关心“是否已经处理过”的场景,但它不保证插入顺序。因此它不能直接满足严格的顺序去重要求。可以在业务上接受无序去重时优先选择它;如果还要输出有序结果,就需要额外维护顺序信息,例如给消息分配递增序列
登录后查看完整内容
