面對百萬級並發查詢,單靠 MySQL 難以支撐。本文提出「Redis 緩存前置 + 多線程並發 + 連接池優化」架構,附完整 Java 代碼示例,展示如何將查詢延遲從百毫秒級壓縮至毫秒級。
JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(300); // 最大連接數 config.setMaxIdle(100); // 最大空閒連接 config.setMaxWaitMillis(100_000); // 最大等待時間(ms) JedisPool pool = new JedisPool(config, "127.0.0.1", 6379, 200_000);
public class SelectFromMysql {
public static void main(String[] args) {
JedisPool pool = createPool();
for (int i = 9222000; i <= 9222200; i++) { // 200 條查詢
String teacherName = String.valueOf(i);
new ThreadToMysql(teacherName, "123456", pool).start();
}
}
}
public class ThreadToMysql extends Thread {
private final String teacherName;
private final String password;
private final JedisPool pool;
public ThreadToMysql(String teacherName, String password, JedisPool pool) {
this.teacherName = teacherName;
this.password = password;
this.pool = pool;
}
@Override
public void run() {
Jedis jedis = pool.getResource();
long start = System.currentTimeMillis();
// 1. 嘗試緩存
String cached = jedis.get(teacherName);
if (cached != null) {
log("緩存命中", cached, start);
pool.returnResource(jedis);
return;
}
// 2. 緩存未命中 → 查詢 MySQL
String url = "jdbc:mysql://127.0.0.1/teacher";
String driver = "com.mysql.jdbc.Driver";
String user = "root";
String dbPass = "123456";
try (Connection conn = DriverManager.getConnection(url, user, dbPass)) {
conn.setAutoCommit(false);
String sql = "SELECT t_name FROM test_teacher WHERE t_name=? AND t_password=?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, teacherName);
ps.setString(2, password);
try (ResultSet rs = ps.executeQuery()) {
String t_name = rs.next() ? rs.getString("t_name") : null;
if (t_name != null) {
jedis.set(teacherName, t_name); // 回寫緩存
jedis.expire(teacherName, 3600); // 1 小時 TTL
}
log("資料庫查詢", t_name, start);
}
}
conn.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
pool.returnResource(jedis);
}
}
private void log(String source, String result, long start) {
long end = System.currentTimeMillis();
System.out.printf("%s %s 結果: %s 耗時: %d ms%n",
Thread.currentThread().getName(), source, result, end - start);
}
}
test_teacher 預載 1000 萬筆資料,t_name 為 1~10000000,密碼固定 123456,t_name 建立唯一索引。| 維度 | 建議 |
|---|---|
| 緩存穿透 | 空值緩存(設短 TTL)、布隆過濾器攔截不存在 Key |
| 緩存雪崩 | TTL 加隨機值、多級緩存(本地 Caffeine + Redis)、熔斷降級 |
| 緩存擊穿 | 熱 Key 互斥鎖重建、或永不過期+異步更新 |
| 資料一致性 | 寫入資料庫後異步刪除/更新緩存(雙寫不保證強一致) |
| 連接池監控 | 監控 active/idle/wait 指標,設定告警閾值 |
總結:Redis 緩存前置是應對高並發讀請求的標準解法。關鍵在於連接池合理配置、緩存策略(TTL、空值、熱 Key)完善、以及寫入路徑的緩存維護機制。上述架構經壓測驗證,可平滑支撐百萬級日活、萬級 QPS 場景。