Zimbra 有一個很少人注意、但預設就開著的功能:郵件重複資料刪除(Message-ID dedupe)。同一封信如果重複投遞到同一個信箱,Zimbra 會直接忽略,使用者只會收到一份。
最近幫客戶查一封「明明有寄、收件者卻說只收到一封」的信,才回頭仔細看了這個功能。這篇整理它的運作方式、怎麼檢查設定,以及為什麼我不建議停用。
運作方式
依 Zimbra 的屬性定義,zimbraMessageIdDedupeCacheSize 的說明是:
LMTP 去重快取中保留的 Message-Id 數量。之後若有相同 Message-Id 的郵件要投遞到同一個信箱,該次投遞會被忽略。設為 0 代表停用。
重點有三個:
- 判斷依據是郵件表頭的
Message-ID - 範圍是同一個信箱:同一封信寄給 A 和 B,兩人都會正常收到;只有「同一封信第二次要進 A 的信箱」才會被忽略
- 發生在 LMTP 投遞階段,也就是 Postfix 把信交給 Zimbra 信箱服務的那一步
什麼時候會遇到重複投遞
- 收件者同時在收件者欄與通訊群組裡:直接寄給 A,又寄給 A 所在的群組,A 會收到兩次投遞
- 多個通訊群組重疊:A 同時是兩個群組的成員,信寄給這兩個群組
- 投遞重試:Zimbra 已經收下信件,但回應 Postfix 之前連線中斷或逾時,Postfix 以為沒送到而重送
最後一種最容易被忽略,系統忙碌、信箱伺服器負載高的時候特別容易發生。有去重的話使用者只看到一封,沒有的話就會收到兩封一模一樣的信。
相關屬性
| 屬性 | 層級 | 預設值 | 說明 |
|---|---|---|---|
zimbraMessageIdDedupeCacheSize | 全域 | 3000 | 快取保留的 Message-ID 數量,0 代表停用 |
zimbraMessageIdDedupeCacheTimeout | 全域 | 0 | 每筆 Message-ID 在快取中的保留時間,0 代表不逾時;設定非 0 時會忽略上面的數量限制 |
zimbraPrefMessageIdDedupingEnabled | 帳號、COS | TRUE | 帳號層級的開關,管理介面翻譯為「啟用郵件重複資料刪除」 |
zimbraPrefDedupeMessagesSentToSelf | 帳號、COS | dedupeNone | 寄給自己的信要怎麼處理,和上面的機制不同 |
最後一個常被搞混:它處理的是「我寄出的信裡收件者也有我自己」的情況,選項有放進收件匣、只在收件者或副本欄有自己時放進收件匣、直接丟棄等,和 LMTP 的去重快取是兩件事。
檢查目前設定
以 zimbra 身分執行:
su - zimbra
# 全域設定
zmprov gacf zimbraMessageIdDedupeCacheSize zimbraMessageIdDedupeCacheTimeout
# 所有和 dedupe 相關的全域屬性
zmprov gacf | grep -i dedupe
輸出例如:
zimbraMessageIdDedupeCacheSize: 3000
zimbraMessageIdDedupeCacheTimeout: 0
這兩個屬性都只能設在全域,zmprov gs 查伺服器層級是看不到的。帳號與 COS 層級的開關:
# 某個帳號
zmprov ga user@example.com zimbraPrefMessageIdDedupingEnabled
# 某個 COS
zmprov gc default zimbraPrefMessageIdDedupingEnabled
zimbraMessageIdDedupeCacheSize 大於 0、帳號的 zimbraPrefMessageIdDedupingEnabled 是 TRUE,這個帳號的去重就是啟用的。
為什麼不建議停用
- 使用者會收到重複的信:群組重疊、投遞重試時,同一封信會出現兩次以上,最直接的反應就是打電話來問「是不是中毒了」
- 重複佔用信箱空間:大型附件的信重複兩三份,配額很快就滿
- 治標不治本:會想停用通常是因為「某人說少收一封」,但那一封其實是被正確地當成重複,停掉之後問題沒解決,只是換成另一種困擾
遇到「收件者說少收」的狀況,比較好的做法是用郵件追蹤(zmmsgtrace 或查 /var/log/zimbra.log)確認那封信的 Message-ID 與投遞紀錄。如果同一個 Message-ID 對同一個收件者有兩筆投遞,第二筆沒有進信箱就是去重的正常行為。
另外要注意,Zimbra 內建的 zmmsgtrace 主要依 Postfix 的記錄來追蹤,遇到通訊群組展開、被去重忽略的投遞,有時候會看不出完整的收件者。我自己寫了 jt_zmmsgtrace,除了 Postfix 記錄之外,也會比對 Amavis 的記錄找出所有收件者,追這類問題會清楚很多。
真的要停用時
少數情況下可能需要暫時停用,例如用同一份原始郵件重新匯入到原本的信箱(還原、搬遷測試),因為 Message-ID 相同,匯入的信會被當成重複而忽略。
只停用特定帳號(建議)
zmprov ma user@example.com zimbraPrefMessageIdDedupingEnabled FALSE
# 處理完改回來
zmprov ma user@example.com zimbraPrefMessageIdDedupingEnabled TRUE
影響範圍最小,其他人不受影響。
停用全域
zmprov mcf zimbraMessageIdDedupeCacheSize 0
zmmailboxdctl restart
恢復成預設值:
zmprov mcf zimbraMessageIdDedupeCacheSize 3000
zmmailboxdctl restart
zmmailboxdctl restart 會讓信箱服務中斷一下,網頁信箱、IMAP、ActiveSync 都會短暫斷線,請在維護時段進行。
注意:去重快取是存在信箱服務的記憶體裡,重新啟動 mailboxd 後快取會清空,之前記住的 Message-ID 都會重新開始累積。所以重新啟動後的一段時間內,重複投遞不一定會被擋下來,這是正常現象。
調整快取大小
預設 3000 筆對小型環境夠用,但每天收信量很大的環境,3000 筆可能幾分鐘就被新的信擠掉,比較晚到的重複投遞就擋不住。可以依收信量調大:
zmprov mcf zimbraMessageIdDedupeCacheSize 10000
或改用時間來控制,例如保留 30 分鐘內的 Message-ID(設定非 0 時就不看數量限制):
zmprov mcf zimbraMessageIdDedupeCacheTimeout 30m
用時間控制要注意記憶體用量,收信量越大,快取裡要記的 Message-ID 越多。
結論
郵件重複資料刪除是 Zimbra 預設就開好、平常默默在幫忙的功能,判斷依據是 Message-ID,範圍是同一個信箱。建議維持啟用;真的有需要,用帳號層級的 zimbraPrefMessageIdDedupingEnabled 暫時關掉特定帳號就好,不要動全域設定。
參考資料
- Zimbra 原始碼:屬性定義檔 zimbra-attrs.xml
https://github.com/Zimbra/zm-mailbox/blob/develop/store/conf/attrs/zimbra-attrs.xml - Zimbra 常用 CLI 指令整理
https://blog.jason.tools/2021/05/zimbra-cli.html



