本篇將計數器持久化層升級為 MySQL 資料庫,利用資料庫行級鎖與事務天然解決集群環境下的併發安全與資料共享問題,代碼採用截圖形式避免格式亂碼。
CREATE TABLE counter (
id INT PRIMARY KEY AUTO_INCREMENT,
count_value BIGINT NOT NULL DEFAULT 0,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB;
INSERT INTO counter (count_value) VALUES (0);

<%@ page import="java.sql.*,javax.sql.*" %>
<%@ page import="javax.naming.*" %>
<%
// 從 JNDI 取得連接池 DataSource(生產環境最佳實踐)
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/counterDB");
long count = 0;
try (Connection conn = ds.getConnection()) {
conn.setAutoCommit(false); // 開啟事務
try (PreparedStatement psLock = conn.prepareStatement(
"SELECT count_value FROM counter WHERE id = 1 FOR UPDATE")) {
// SELECT ... FOR UPDATE 獲取行鎖,阻塞其他事務直到提交
try (ResultSet rs = psLock.executeQuery()) {
if (rs.next()) count = rs.getLong("count_value");
}
}
count++;
try (PreparedStatement psUpd = conn.prepareStatement(
"UPDATE counter SET count_value = ? WHERE id = 1")) {
psUpd.setLong(1, count);
psUpd.executeUpdate();
}
conn.commit(); // 釋放鎖
} catch (Exception e) {
e.printStackTrace();
}
%>
當前訪問次數:<%= count %>
setAutoCommit(false) → 查詢+更新 → commit(),原子性保證。<Resource name="jdbc/counterDB" auth="Container" type="javax.sql.DataSource"
maxTotal="20" maxIdle="10" maxWaitMillis="-1"
username="root" password="123456"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=UTF-8"/>
| 優點 | 缺點 |
|---|---|
| 集群多節點天然共享、強一致性 | 每次訪問需資料庫往返,延遲較高 |
| 資料庫備份/遷移/審計完善 | 高併發下資料庫連接池成瓶頸 |
| 支援複雜查詢(按日期、IP 統計) | 需額外維護 MySQL 服務 |