首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

高併發無鎖數據庫操作架構:Disruptor + 雙級緩存 + 異步落庫實戰

     編輯:Java 程式設計入門

核心思想:將並發轉化為單線程序列化處理

傳統高併發依賴鎖 (synchronized、Lock、CAS),上下文切換與爭用開銷大。LMAX Disruptor 證明:**單線程循環 (Event Loop) + 環形緩衝區** 可達百萬級 TPS。本文架構採用相同哲學:

  1. 並發環境 (Web 執行緒):只負責接收請求、寫入「一級緩存」、發布事件到 Disruptor RingBuffer
  2. 單線程消費者 (Event Processor):從 RingBuffer 取事件、聚合批次、異步寫入 DB、更新「二級緩存」

架構分層


┌─────────────┐     ┌──────────────────┐     ┌─────────────────┐     ┌──────────┐
│  Web Thread │────▶│  L1 Cache (Thread│────▶│  Disruptor      │────▶│  DB      │
│  (Producer) │     │   Local / Shared)│     │  RingBuffer     │     │  Writer  │
└─────────────┘     └──────────────────┘     └────────┬────────┘     └──────────┘
                                                      │
                        ┌─────────────────────────────┘
                        ▼
               ┌──────────────────┐
               │  L2 Cache (Shared│
               │  ConcurrentHashMap)│
               └──────────────────┘

關鍵組件實現

1. 領域事件

public class DbEvent {
    public enum Op { INSERT, UPDATE, DELETE }
    public final Op op;
    public final String table;
    public final Object pk;           // 主鍵
    public final Map<String, Object> columns; // 列值
    public final long seq;            // 全局遞增序號 (排序/去重用)
    
    // 建構子、getter 省略
}

2. 雙級緩存設計

public class DualCache<K, V> {
    // L1: 僅單線程消費者寫入,多線程讀取 (無鎖讀)
    private final Map<K, V> l1Cache = new HashMap<>(); 
    
    // L2: 併發環境讀寫 (ConcurrentHashMap)
    private final ConcurrentHashMap<K, V> l2Cache = new ConcurrentHashMap<>();
    
    // 讀取:優先 L1,再 L2
    public V get(K key) {
        V v = l1Cache.get(key);
        return v != null ? v : l2Cache.get(key);
    }
    
    // 併發環境寫入:直接寫 L2
    public void putL2(K key, V value) { l2Cache.put(key, value); }
    
    // 消費者寫入:寫 L1,異步刷新 L2
    public void putL1AndFlushL2(K key, V value) {
        l1Cache.put(key, value);
        // 提交異步任務刷新 L2 (或由消費者批次刷新)
        asyncFlush(key, value);
    }
    
    // 失效:同時清理兩級
    public void invalidate(K key) {
        l1Cache.remove(key);
        l2Cache.remove(key);
    }
}

3. Disruptor 消費者 (單線程無鎖)

public class DbEventProcessor implements EventHandler<DbEvent> {
    private final JdbcTemplate jdbc;
    private final DualCache<String, Object> cache;
    private final List<DbEvent> batch = new ArrayList<>(1000);
    private static final AtomicLong SEQ = new AtomicLong();

    @Override
    public void onEvent(DbEvent event, long sequence, boolean endOfBatch) {
        batch.add(event);
        
        // 批次觸發條件:滿 1000 條 或 批次結束
        if (batch.size() >= 1000 || endOfBatch) {
            flushBatch();
        }
    }
    
    private void flushBatch() {
        if (batch.isEmpty()) return;
        
        // 1. 排序:按 table -> pk -> seq,便於合併同主鍵操作
        batch.sort(Comparator.comparing((DbEvent e) -> e.table)
            .thenComparing(e -> e.pk.toString())
            .thenComparingLong(e -> e.seq));
        
        // 2. 去重優化:同 table+pk 只保留最後一條 UPDATE;有 DELETE 則前序無效
        List<DbEvent> optimized = optimizeBatch(batch);
        
        // 3. 批次執行
        executeBatch(optimized);
        
        // 4. 更新 L1 Cache (單線程安全)
        for (DbEvent e : optimized) {
            String cacheKey = e.table + ":" + e.pk;
            if (e.op == DbEvent.Op.DELETE) {
                cache.invalidate(cacheKey);
            } else {
                cache.putL1AndFlushL2(cacheKey, e.columns);
            }
        }
        batch.clear();
    }
    
    private List<DbEvent> optimizeBatch(List<DbEvent> batch) {
        List<DbEvent> res = new ArrayList<>();
        DbEvent last = null;
        for (DbEvent e : batch) {
            if (last != null && last.table.equals(e.table) && last.pk.equals(e.pk)) {
                if (e.op == DbEvent.Op.DELETE) {
                    res.remove(last); // 刪除操作使前序無效
                    last = e;
                } else if (e.op == DbEvent.Op.UPDATE) {
                    last = e; // 只保留最後一次 UPDATE
                }
            } else {
                res.add(e);
                last = e;
            }
        }
        return res;
    }
}

4. 生產者 (Web 線程) 快速發布

@RestController
public class OrderController {
    private final RingBuffer<DbEvent> ringBuffer;
    private final DualCache<String, Object> cache;

    @PostMapping("/order")
    public Result createOrder(@RequestBody OrderDTO dto) {
        // 1. 業務邏輯、生成主鍵
        String orderId = IdGenerator.next();
        
        // 2. 寫入 L1 Cache (供後續同請求或其他請求立即讀取)
        String cacheKey = "t_order:" + orderId;
        Map<String, Object> cols = toColumns(dto);
        cache.putL1AndFlushL2(cacheKey, cols); // 或直接 putL2
        
        // 3. 發布事件到 RingBuffer (無鎖、極快)
        long seq = ringBuffer.next();
        try {
            DbEvent event = ringBuffer.get(seq);
            event.op = DbEvent.Op.INSERT;
            event.table = "t_order";
            event.pk = orderId;
            event.columns = cols;
            event.seq = SEQ.getAndIncrement();
        } finally {
            ringBuffer.publish(seq);
        }
        return Result.ok(orderId);
    }
}

全局唯一序號生成器 (無鎖)

public final class IdGenerator {
    private static final AtomicLong SEQ = new AtomicLong(0);
    public static long nextSeq() { return SEQ.getAndIncrement(); }
    
    // 雪花算法變體 (含時間戳、機器碼、序號)
    public static String nextId() { /* ... */ return ""; }
}

關鍵優化點總結

優化說明
批次合併同主鍵操作減少 DB 往返,避免重複 UPDATE
DELETE 失效前序操作同批次中出現 DELETE,前面的 INSERT/UPDATE 直接丟棄
異步落庫Web 線程不阻塞於 DB I/O,響應時間穩定
L1 Cache 單線程寫消費者線程獨佔寫入,讀取無鎖,保證強一致性
L2 Cache 併發讀寫ConcurrentHashMap 支援高併發讀取
序號排序保證跨線程操作的全局順序,支援冪等/去重

一致性模型與權衡

  • 最終一致性: 寫入 L1 後立即可讀,DB 落庫有延遲 (通常 < 10ms)
  • 讀寫分離風險: 若讀從庫,需確保從庫同步延遲 < 業務容忍度
  • 崩潰恢復: RingBuffer 持久化 (如寫入 Kafka/檔案) 或定期 Checkpoint

適用場景與避坑

  • ✅ 寫入密集、讀多寫少、允許毫秒級最終一致 (訂單、日誌、計數、狀態流轉)
  • ❌ 強一致性要求極高 (資金扣款、庫存扣減需即時生效) → 需同步調用或分佈式事務
  • ❌ 極其複雜的多表事務 → 單線程消費者難以維護事務邊界

結語

「大循環 + 雙級緩存 + 異步批次」是高併發無鎖寫入的經典模式。核心在於:**接受最終一致性、將隨機 I/O 轉為順序批次 I/O、用單線程換取無鎖吞吐**。實施時務必配合監控 (RingBuffer 積壓、DB 延遲、緩存命中率) 與熔斷降級機制。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved