MySQL 表 summary_data 約 3000 萬行,欄位 first_path VARCHAR(256),需找出現次數最多的 Top 10 路徑。
SELECT first_path, COUNT(*) AS c
FROM summary_data
GROUP BY first_path
ORDER BY c DESC LIMIT 10;
EXPLAIN 顯示 type=ALL、Using temporary; Using filesort,索引 first_path(255) 前綴過長導致失效,全表掃描 + 磁碟排序極慢。
# 導出 first_path(耗時較長)
SELECT first_path FROM summary_data INTO OUTFILE '/tmp/first_path.txt';
# 管道處理
sort /tmp/first_path.txt | uniq -c | sort -nr | head -10
外部排序優化極佳,記憶體不足自動落盤歸併,單線程 1.5 分鐘完成。
first_path 字串,Score = 出現次數 (double)。ZINCRBY key 1 member 原子遞增,自動維護有序。O(log N)。/* hiredis 例程 */
FILE *fp = fopen("first_path.txt", "r");
char line[256];
while (fgets(line, sizeof(line), fp)) {
// 去除換行
line[strcspn(line, "\r\n")] = 0;
redisCommand(c, "ZINCRBY zset_firstpath 1 %s", line);
}
fclose(fp);
/* 取 Top 10 */
redisReply *r = redisCommand(c,
"ZREVRANGEBYSCORE zset_firstpath +inf -inf WITHSCORES LIMIT 0 10");
/* 輸出:
1) path_a 12345
2) path_b 9876
... */
ZINCRBY 網路往返 + Skip List 維護 = 16 分鐘,**慢於 Sort/Uniq**。pipeline 批量發送可大幅提升(未測試)。| 方案 | 耗時 | 優點 | 缺點 |
|---|---|---|---|
| MySQL GROUP BY | 9 分鐘 | 零外部依賴 | 全表掃描、臨時表、磁碟排序 |
| Sort + Uniq | 1.5 分鐘 | 極速、無額外基建 | 需導出檔案、單機單線程 |
| Redis ZSET | 16 分鐘 | 支援實時增量更新、范圍查詢 | 載入慢、記憶體大、單線程 CPU 瓶頸 |