錯誤表設計
Goods 表:
id | goods(贊數) | news_id | user_id
// 贊數冗余存放在關聯表,導致一致性難維護
正確設計:關聯表僅存關係
CREATE TABLE article_like (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
article_id BIGINT NOT NULL, -- 對應 news_id
user_id BIGINT NOT NULL,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_article_user (article_id, user_id) -- 唯一索引防重
);
業務邏輯實現
public boolean likeArticle(long articleId, long userId) {
// 1. 嘗試插入,利用唯一索引防重
String sql = "INSERT IGNORE INTO article_like (article_id, user_id) VALUES (?, ?)";
int rows = jdbcTemplate.update(sql, articleId, userId);
if (rows > 0) {
// 2. 成功則原子增文章贊數
jdbcTemplate.update("UPDATE article SET like_count = like_count + 1 WHERE id = ?", articleId);
return true;
}
return false; // 已點贊
}
關鍵點解析
- 唯一索引:
uk_article_user 保証數據庫層面絕對不重複,無需應用層加鎖。
- INSERT IGNORE:MySQL 語法,重複鍵不報錯只返回 0 行影響,簡化判斷邏輯。
- 贊數冗余:文章表保留
like_count 字段供高頻讀取,由觸發器或應用層同步更新,避免每次 COUNT(*) 掃描大表。
- 事務控制:插入關係與更新計數需在同一事務中,保證一致性。
擴展:取消點贊
public boolean unlikeArticle(long articleId, long userId) {
int rows = jdbcTemplate.update("DELETE FROM article_like WHERE article_id=? AND user_id=?", articleId, userId);
if (rows > 0) {
jdbcTemplate.update("UPDATE article SET like_count = like_count - 1 WHERE id = ?", articleId);
return true;
}
return false;
}