【程式設計】方格子平台的蟲 BUG — 同一筆資料出現兩次的祕密

更新 發佈閱讀 7 分鐘

有沒有過這樣的經驗?你在方格子的後台編輯文章,明明只發表過一次,卻突然發現同一篇文章「重複出現兩次」。第一反應當然是以為自己搞錯,把其中一篇刪掉、重新寫一次。結果怪事又發生了:文章不是消失、就是再度出現,完全搞不清楚到底是系統問題,還是自己眼花。

別急,這其實不是你一個人的錯,也不是「靈異事件」,而是程式設計中相當常見的「資料唯一性」問題。


一個真實的故事:0.3 元的差距

在說明之前,先講一個曾經發生過的案例。

某家公司導入新的 ERP 系統,運作半年後一切看似順利:財報、損益表、資產負債表都能正確產出,數字也能勾稽。唯獨在「庫存報表」這一關,總金額和財務部門的數字總是對不起來。差距不大,有時候只差 0.3 元,有時候差幾塊錢,有時候甚至差上百塊。

別看金額小,這個差異卻讓電腦公司拿不到尾款。因為客戶堅持:只要報表不能互相驗證,系統就算不上合格。

為了解決問題,軟體公司派了好幾位工程師進駐,日以繼夜地檢查程式與資料。直到有一天,財務小姐發現:「怎麼這裡多了一個 0.3 元?」那正好是一個彈簧配件的價格,庫存數量為 1,總價 0.3 元。

可是,為什麼總金額會因此差 0.3 元?大家百思不得其解。最後只好把整份庫存明細一筆一筆印出來比對。這才發現,這個彈簧配件在「總庫存表」裡被列了兩次。也就是說,它被計算了兩次,導致總金額多出了 0.3 元。

當這個「小蟲」被抓出來之後,把重複的資料刪掉,財務報表立刻與庫存總金額對上。真相大白:問題出在程式設計師撈資料時,沒有確保唯一性(uniqueness),結果造成一筆資料重複計算。


方格子平台上的 BUG,其實如出一轍

回到我們在方格子遇到的狀況:同一篇文章明明只寫一次,卻在後台出現兩份。這和剛才的彈簧案例如出一轍 ——

raw-image

程式在抓取資料的時候,沒有處理「去重」與「唯一性」的問題。

raw-image

這種情況在任何內容平台都可能發生,成因大致有幾類:

  1. 資料庫設計問題 沒有設定主鍵或唯一索引,導致同一筆資料被插入兩次。 查詢語法 join 多張表,結果出現多對多關係,數據倍增。
  2. 併發與提交問題 使用者在網路延遲時多按了幾次「發布」,後端沒有防護,結果產生兩篇相同文章。
  3. 異步任務重試 後台的同步工作在失敗後重跑,沒有檢查「該筆文章是否已存在」。
  4. 前後端不同步 前端 cache 與資料庫版本不一致,造成 UI 顯示上「看似有兩篇」。

這些問題的本質,都是沒有做好「唯一性檢查」。


解決方法:從資料到應用的全方位守門

要解決「重複文章」的問題,必須分層處理,不能只靠某一層。

1. 資料庫層 —— 保證唯一

  • 給文章表設定主鍵(id)與唯一索引(slug、content_hash)。
  • 使用複合唯一條件,例如 (author_id, title)。 SQL 範例:
ALTER TABLE articles ADD CONSTRAINT uq_articles_slug UNIQUE (slug);

ALTER TABLE articles ADD COLUMN content_hash VARCHAR(64);
CREATE UNIQUE INDEX uq_articles_content_hash ON articles(content_hash);

2. 寫入層 —— 避免重複插入

  • 使用 transaction 確保一次請求只會建立一筆資料。
  • 善用 upsert(Postgres 的 ON CONFLICT 或 MySQL 的 ON DUPLICATE KEY)。 範例:
INSERT INTO articles (slug, title, body, content_hash)
VALUES ($1, $2, $3, $4)
ON CONFLICT (content_hash) DO UPDATE SET title = EXCLUDED.title;

3. 應用層 —— 保持冪等

  • 為每個請求生成 request id,避免多次提交。
  • 透過文章內容的 hash 判斷是否已存在。 範例(Python 伪碼):
def create_article(request):
if exists_request_id(request.id): return existing_article
h = sha256(request.body)
article = db.find_one({'content_hash': h})
if article: return article
return insert_article(..., content_hash=h)

4. 顯示層 —— 查詢要去重

  • 若因 join 導致一篇文章被拉回多次,記得 DISTINCT 或 GROUP BY。 範例:
SELECT DISTINCT a.id, a.title
FROM articles a
LEFT JOIN article_tags t ON t.article_id = a.id;

5. 維運層 —— 定期檢查

  • 每日產生 資料品質報表,比對是否有相同 slug、hash 的文章。
  • 提供後台工具,能快速合併或刪除重複文章。

結語:小小的 0.3 元,其實是系統設計的警鐘

那顆彈簧配件的 0.3 元,揭示了一個工程真理:哪怕最小的差異,背後都可能是程式設計上的缺口。


在方格子平台文章重複顯示的問題上,雖然對使用者來說只是困惑或不便,但對整個系統的資料一致性來說卻是警訊。


寫程式不只是「能跑就好」,而是要確保資料正確、唯一、能夠驗證。只有這樣,系統才能長久穩定,也不會因為一個小 bug,造成信任崩壞。

下次當你看到「怎麼同一篇文章出現兩次」時,或許可以笑著想:啊,這可能就是那個「0.3 元的彈簧」又出現了。

留言
avatar-img
留言分享你的想法!
avatar-img
普普文創
527會員
4.8K內容數
這裡就是一個小報社,普普文創、水彩速寫、迷你短篇、文創漫談、心靈雞湯、踏青步道、智慧音樂、美食天堂、超級房間。歡迎閱讀。。。謝謝。。
普普文創的其他內容
2025/09/22
9 月 16 日,我的文章有九篇同時入選「即時精選」。這原本是一個值得開心的事情,因為照理說被平台推送、被選中,就代表有機會獲得更多的曝光。然而,三天後回頭檢視數據,卻得到了一組耐人尋味的點閱數字: 36、31、2500、107、14、14、20、28、354。單純看這組數字,會覺得分布很奇怪
Thumbnail
2025/09/22
9 月 16 日,我的文章有九篇同時入選「即時精選」。這原本是一個值得開心的事情,因為照理說被平台推送、被選中,就代表有機會獲得更多的曝光。然而,三天後回頭檢視數據,卻得到了一組耐人尋味的點閱數字: 36、31、2500、107、14、14、20、28、354。單純看這組數字,會覺得分布很奇怪
Thumbnail
2025/09/21
近來有人提出「寫作品牌」這個概念,對我而言,這並不是一個新鮮的名詞。早在許多年以前,真正懂得經營創作的人就已經在實踐,只是做的人不多、懂的人更少。絕大部分的作者仍停留在「作品」與「靈感」的層次,卻沒有意識到,寫作其實也能是一種品牌經營。 如果我們把「寫作品牌」的進程拆開來看,它可以分成五個階段:
Thumbnail
2025/09/21
近來有人提出「寫作品牌」這個概念,對我而言,這並不是一個新鮮的名詞。早在許多年以前,真正懂得經營創作的人就已經在實踐,只是做的人不多、懂的人更少。絕大部分的作者仍停留在「作品」與「靈感」的層次,卻沒有意識到,寫作其實也能是一種品牌經營。 如果我們把「寫作品牌」的進程拆開來看,它可以分成五個階段:
Thumbnail
2025/09/20
寫作,追求的還是要有人看。無論是文學創作、散文心得,還是評論文章,讀者的回饋始終是創作者的「養分」。我們當然可以提醒自己:「不要過度在意流量,不要被點擊率牽著鼻子走,要專注於作品本身的價值與深度。」這樣的提醒確實重要,因為文字的價值遠遠超過數字的累積。然而現實卻殘酷地告訴我們:如果文字沒有傳遞到讀者
Thumbnail
2025/09/20
寫作,追求的還是要有人看。無論是文學創作、散文心得,還是評論文章,讀者的回饋始終是創作者的「養分」。我們當然可以提醒自己:「不要過度在意流量,不要被點擊率牽著鼻子走,要專注於作品本身的價值與深度。」這樣的提醒確實重要,因為文字的價值遠遠超過數字的累積。然而現實卻殘酷地告訴我們:如果文字沒有傳遞到讀者
Thumbnail
看更多
你可能也想看
Thumbnail
剛剛開始在方格子胡言亂語,對一些設定十分好奇,那一天在那看了那半天,然後房間管理,竟然還有分頁設定,嘿嘿~好精細唷!我喜歡… 然後的然後,我就弄老半天,怎地我在房間管理弄的文,就是沒法子搞到分頁設定,弄得我有給它頭昏昏腦悵悵,但我還是沒有整出來,總覺得遺憾… 想當然爾,我也去請教了google大
Thumbnail
剛剛開始在方格子胡言亂語,對一些設定十分好奇,那一天在那看了那半天,然後房間管理,竟然還有分頁設定,嘿嘿~好精細唷!我喜歡… 然後的然後,我就弄老半天,怎地我在房間管理弄的文,就是沒法子搞到分頁設定,弄得我有給它頭昏昏腦悵悵,但我還是沒有整出來,總覺得遺憾… 想當然爾,我也去請教了google大
Thumbnail
距離第一篇文章居然已經過了兩年,當初發表後不久即找到正職工作,忙著學習打理一間小店的事務,卻放空了寫作的初衷。 期間信箱還是會不時收到方格子的通知,但直到最近平台轉型的樣態呈現出來,才讓我產生回頭研究的好奇心。比起初期主攻專業內容帶動虛擬貨幣收入,如今的方格子比較像是分享想法的社群平台,每個人開闢
Thumbnail
距離第一篇文章居然已經過了兩年,當初發表後不久即找到正職工作,忙著學習打理一間小店的事務,卻放空了寫作的初衷。 期間信箱還是會不時收到方格子的通知,但直到最近平台轉型的樣態呈現出來,才讓我產生回頭研究的好奇心。比起初期主攻專業內容帶動虛擬貨幣收入,如今的方格子比較像是分享想法的社群平台,每個人開闢
Thumbnail
紀錄自己在方格子發表文章三個月的經驗與成長。 回顧3~5月的發文變化和互動成果。
Thumbnail
紀錄自己在方格子發表文章三個月的經驗與成長。 回顧3~5月的發文變化和互動成果。
Thumbnail
這篇文章宣布由於忙碌,發文頻率將從每天一篇調整為兩天一篇。作者回顧在方格子平台的發文歷程,共發表了63篇文章。雖然追蹤者不多,但作者表示此平台是佛系經營,重在紀錄和分享。最後,作者宣傳了一個遊戲創作交流的Discord群組,並邀請有興趣的讀者加入。
Thumbnail
這篇文章宣布由於忙碌,發文頻率將從每天一篇調整為兩天一篇。作者回顧在方格子平台的發文歷程,共發表了63篇文章。雖然追蹤者不多,但作者表示此平台是佛系經營,重在紀錄和分享。最後,作者宣傳了一個遊戲創作交流的Discord群組,並邀請有興趣的讀者加入。
Thumbnail
親愛的讀者們 時光飛逝,已經有一段時間我沒有在部落格更新文章了。 如今,事業上的一些轉變,給予我更多時間可以調適生活步調。我想,或許正是時候重新開始
Thumbnail
親愛的讀者們 時光飛逝,已經有一段時間我沒有在部落格更新文章了。 如今,事業上的一些轉變,給予我更多時間可以調適生活步調。我想,或許正是時候重新開始
Thumbnail
在方格子開了帳號,一轉眼兩個禮拜過去了。 為何明明心中有滿溢的想法想分享,卻連一篇文章都生不出來呢?
Thumbnail
在方格子開了帳號,一轉眼兩個禮拜過去了。 為何明明心中有滿溢的想法想分享,卻連一篇文章都生不出來呢?
Thumbnail
文章更新頁面升格沙龍後原專題區無法新增圖片,之後更新文章會轉為同方格子的沙龍區囉 https://vocus.cc/salon/naturelake/room/naturelake0331
Thumbnail
文章更新頁面升格沙龍後原專題區無法新增圖片,之後更新文章會轉為同方格子的沙龍區囉 https://vocus.cc/salon/naturelake/room/naturelake0331
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News