在軟體工程中,我們經常需要評估連續事件發生的機率:API 連續失敗的風險、部署連續成功的可能性、測試連續通過的期望值。這些看似簡單的機率問題,實際上對系統可靠性設計、監控策略、測試覆蓋率都有直接影響。
為什麼工程師需要懂機率?
在實務中,我們面對的典型場景:
場景 1:API 成功率監控
如果你的 API 單次呼叫成功率是 99.9%(three nines),那麼連續 1000 次呼叫都成功的機率是多少?這直接關係到你的 SLA 承諾。
場景 2:CI/CD 管道可靠性
如果每個測試階段通過率是 95%,而你有 10 個階段,整個管道成功的機率會低到讓你意外。
場景 3:故障檢測靈敏度
健康檢查每分鐘執行一次,如果允許連續 3 次失敗才觸發告警,你的平均檢測延遲是多少?
機率基礎
機率 P 表示事件發生的可能性,範圍在 0 到 1 之間(或 0% 到 100%)。
獨立事件的連續機率計算:
P(連續 n 次) = P^n
重點: 這個公式只適用於獨立事件。如果事件之間有相依性(例如快取失效、雪崩效應),需要更複雜的模型。
實務範例 1:API 可靠性計算
假設你的 API 單次成功率 99%(P = 0.99),計算連續成功的機率:
def consecutive_success_probability(single_prob, n_calls):
"""
計算連續 n 次呼叫都成功的機率
Args:
single_prob: 單次成功率 (0-1)
n_calls: 連續呼叫次數
Returns:
連續成功機率
"""
return single_prob ** n_calls
# 範例:API 單次成功率 99%
success_rate = 0.99
print(f"連續 10 次成功: {consecutive_success_probability(success_rate, 10):.4f}")
# 輸出: 0.9044 (90.44%)
print(f"連續 100 次成功: {consecutive_success_probability(success_rate, 100):.4f}")
# 輸出: 0.3660 (36.60%)
print(f"連續 1000 次成功: {consecutive_success_probability(success_rate, 1000):.4f}")
# 輸出: 0.0000 (幾乎不可能)
關鍵洞察:
- 99% 成功率看似很高,但連續 100 次呼叫只有 36.6% 機會全部成功
- 這說明為什麼需要 retry 機制和降級策略
實務範例 2:測試覆蓋率與管道可靠性
假設你有一個 CI/CD 管道,包含多個測試階段:
import pandas as pd
def pipeline_reliability_table(stage_prob, max_stages):
"""
產生管道可靠性表格
Args:
stage_prob: 每個階段通過率
max_stages: 最多階段數
Returns:
DataFrame 包含各階段數的整體通過率
"""
data = []
for n in range(1, max_stages + 1):
overall_prob = stage_prob ** n
data.append({
'階段數': n,
'單階段通過率': f"{stage_prob:.1%}",
'整體通過率': f"{overall_prob:.2%}",
'整體失敗率': f"{(1-overall_prob):.2%}"
})
return pd.DataFrame(data)
# 假設每階段通過率 95%
print(pipeline_reliability_table(0.95, 10))
輸出:
階段數 單階段通過率 整體通過率 整體失敗率
0 1 95.0% 95.00% 5.00%
1 2 95.0% 90.25% 9.75%
2 3 95.0% 85.74% 14.26%
3 4 95.0% 81.45% 18.55%
4 5 95.0% 77.38% 22.62%
5 6 95.0% 73.51% 26.49%
6 7 95.0% 69.83% 30.17%
7 8 95.0% 66.34% 33.66%
8 9 95.0% 63.02% 36.98%
9 10 95.0% 59.87% 40.13%
重要發現:
- 10 個階段的管道,即使每階段 95% 通過率,整體失敗率高達 40.13%
- 這說明為什麼需要盡可能提高每個階段的測試品質
實務範例 3:監控告警靈敏度
健康檢查設定:每 30 秒檢查一次,連續 3 次失敗觸發告警。
def alert_sensitivity_analysis(check_interval, consecutive_failures, failure_prob):
"""
分析監控告警的靈敏度
Args:
check_interval: 檢查間隔(秒)
consecutive_failures: 連續失敗次數觸發告警
failure_prob: 單次檢查失敗率
Returns:
dict 包含平均檢測時間和誤報率
"""
# 連續失敗機率(誤報率)
false_positive_prob = failure_prob ** consecutive_failures
# 平均檢測延遲
avg_detection_time = check_interval * consecutive_failures
return {
'avg_detection_time_sec': avg_detection_time,
'false_positive_rate': false_positive_prob,
'false_positive_pct': f"{false_positive_prob:.4%}"
}
# 範例:每 30 秒檢查,連續 3 次失敗觸發
result = alert_sensitivity_analysis(
check_interval=30,
consecutive_failures=3,
failure_prob=0.01 # 1% 檢查失敗率(網路抖動等)
)
print(f"平均檢測延遲: {result['avg_detection_time_sec']} 秒")
print(f"誤報率: {result['false_positive_pct']}")
# 輸出:
# 平均檢測延遲: 90 秒
# 誤報率: 0.0001%
設計權衡:
- 連續失敗次數越多 → 誤報率越低,但檢測延遲越高
- 需要根據服務的 SLA 要求找到平衡點
實務範例 4:SLA 可用性計算
假設你承諾 99.9% SLA(three nines),計算各時間區間的允許停機時間:
def calculate_downtime_budget(uptime_sla, time_period_days):
"""
計算 SLA 允許的停機時間
Args:
uptime_sla: 可用性 SLA (如 0.999 表示 99.9%)
time_period_days: 時間週期(天)
Returns:
dict 包含允許停機時間
"""
total_minutes = time_period_days * 24 * 60
downtime_minutes = total_minutes * (1 - uptime_sla)
return {
'period_days': time_period_days,
'sla_pct': f"{uptime_sla:.1%}",
'downtime_minutes': downtime_minutes,
'downtime_hours': downtime_minutes / 60
}
# 計算各時間區間
for days, label in [(1, '每日'), (7, '每週'), (30, '每月'), (365, '每年')]:
result = calculate_downtime_budget(0.999, days)
print(f"{label} ({result['sla_pct']}): "
f"{result['downtime_minutes']:.1f} 分鐘 "
f"({result['downtime_hours']:.2f} 小時)")
# 輸出:
# 每日 (99.9%): 1.4 分鐘 (0.02 小時)
# 每週 (99.9%): 10.1 分鐘 (0.17 小時)
# 每月 (99.9%): 43.2 分鐘 (0.72 小時)
# 每年 (99.9%): 525.6 分鐘 (8.76 小時)
常見陷阱與解決方案
陷阱 1:誤以為事件是獨立的
錯誤假設: 部署失敗是獨立事件
實際情況: 如果第一次部署失敗,可能是環境問題,後續部署也會失敗(非獨立)
解決方案: 使用條件機率或貝氏定理建模
陷阱 2:忽略時間因素
錯誤假設: 只看機率,不考慮時間
實際情況: 連續 10 次 API 呼叫在 1 秒內完成 vs 1 小時內完成,風險完全不同
解決方案: 結合時間窗口分析(rate limiting, circuit breaker)
陷阱 3:過度依賴歷史數據
錯誤假設: 過去成功率能預測未來
實際情況: 系統負載、網路狀況、依賴服務都在變化
解決方案: 動態調整機率模型,使用滑動窗口
工具與函式庫
# 使用 scipy 處理更複雜的機率分布
from scipy import stats
# 二項分布:n 次獨立試驗中恰好 k 次成功的機率
n, p = 100, 0.99 # 100 次呼叫,成功率 99%
prob_all_success = stats.binom.pmf(k=100, n=n, p=p)
print(f"100 次全部成功: {prob_all_success:.4f}")
# 至少成功 95 次的機率
prob_at_least_95 = sum(stats.binom.pmf(k, n, p) for k in range(95, 101))
print(f"至少成功 95 次: {prob_at_least_95:.4f}")
總結
理解連續事件機率對軟體工程師的實務意義:
- 系統設計 – 計算 retry 次數、設定 timeout、設計降級策略
- 監控策略 – 決定告警閾值、平衡誤報與檢測延遲
- 測試規劃 – 評估 CI/CD 管道可靠性、決定測試覆蓋率目標
- SLA 承諾 – 計算可用性預算、規劃維護窗口
關鍵原則:
- 獨立事件的連續機率 = P^n,但要小心驗證獨立性假設
- 成功率看似很高,連續次數多時機率會快速下降
- 實務中要結合時間、負載、依賴關係綜合考量
- 使用程式碼驗證你的假設,不要憑直覺判斷
下次設計系統可靠性策略時,這些機率計算將幫助你做出更明智的技術決策。