近期架構一個低延遲行情接入分發系統,需支持 Level-1、Level-2 等多源。Level-1 行情由行情小站定時更新 DBF 文件(上海 show2003.dbf、深圳 sjshq.dbf),目標是在文件更新完成後,以最短時間讀取入內存、轉換對象與數據類型。本系列共採用 6 種優化策略,前兩篇已介紹 4 項:
DBF 中價格字段帶 2-3 位小數,存儲為 Long/Int 而非 Float/Double 以避免精度丟失(如 1.23 存為 1230)。未優化流程需:讀取 byte[]→String→trim→Float→×1000→Long,涉及多次拷貝與對象創建,效率低下。
優化方案:在 DBFReader 中新增 get_long_efficiently_and_multiply_1000,單次掃描完成解析與乘法:
public long get_long_efficiently_and_multiply_1000(byte[] src, final int index) {
long multiplicand = 3;
long result = 0;
Field field = getFields()[index];
boolean in_decimal_part = false;
boolean negative = false;
int offset = field.getOffset();
int length = field.getLength();
int end = offset + length;
for (int i = offset; i < end; i++) {
byte ch = src[i];
if (ch >= 48 && ch <= 57) { // 數字
result *= 10;
result += ch - 48;
if (in_decimal_part) multiplicand--;
if (multiplicand == 0) break;
continue;
}
if (ch == 32) continue; // 空格
if (ch == 46) { in_decimal_part = true; continue; } // 小數點
if (ch == '-') { negative = true; }
throw new NumberFormatException();
}
if (multiplicand == 3) result *= 1000;
else if (multiplicand == 2) result *= 100;
else if (multiplicand == 1) result *= 10;
if (negative) result = 0 - result;
return result;
}
整型字段同理優化為 get_long_efficiently。測試顯示,讀取耗時從 50-70ms 降至 15-20ms,為提升最大的單項優化。
5000+ 行數據轉換天然適合多線程。傳統等分任務(如 5 線程各 1000 行)因 OS 時間片調度導致負載不均。採用「工作窃取」式動態分配:每線程每次申請 16 行,完成後再取,快線程多幹,慢線程少幹。協調靠 AtomicInteger (CAS) 無鎖實現:
class ReaderTask implements Runnable {
// ... 構造器省略
@Override public void run() {
try {
int length = recordList.size();
final int step = 16;
do {
int endIndex = cursor.addAndGet(step);
int startIndex = endIndex - step;
for (int i = startIndex; i < endIndex && i < length; i++) {
byte[] row = recordList.get(i);
MarketRealtimeData data = new MarketRealtimeData();
data.setMarketType(Constants.MARKET_SH_STOCK);
data.setIdNum(reader.get_string_efficiently(row, 0));
data.setPreClosePrice(reader.get_long_efficiently_and_multiply_1000(row, 2));
// ... 其餘字段省略
if (collector != null) collector.collect(data);
}
} while (cursor.get() < length);
} finally {
if (countDownLatch != null) countDownLatch.countDown();
}
}
}
主流程:
private void readHangqingFile(String path, String name) throws Exception {
DBFReader dbfreader = new DBFReader(new File(path + File.separator + name));
List<byte[]> list = dbfreader.recordsWithOutDel_efficiently(cacheManager);
AtomicInteger cursor = new AtomicInteger(0);
CountDownLatch latch = new CountDownLatch(WORK_THREAD_COUNT);
for (int i = 0; i < WORK_THREAD_COUNT - 1; i++) {
globalExecutor.execute(new ReaderTask(collector, dbfreader, list, cursor, latch));
}
new ReaderTask(collector, dbfreader, list, cursor, latch).run(); // 主線程也參與
latch.await();
}
4 線程並行下,處理時間從 15-20ms 縮短至 4-7ms。綜合 6 項優化,端到端耗時從 300ms+ 降至 5-10ms。後續可從減少對象創建、控制 GC 執行時機進一步穩定延遲。