如何計算連續事件的機率:軟體工程中的實務應用

🌏 Read the English version


在軟體工程中,我們經常需要評估連續事件發生的機率: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}")

總結

理解連續事件機率對軟體工程師的實務意義:

  1. 系統設計 – 計算 retry 次數、設定 timeout、設計降級策略
  2. 監控策略 – 決定告警閾值、平衡誤報與檢測延遲
  3. 測試規劃 – 評估 CI/CD 管道可靠性、決定測試覆蓋率目標
  4. SLA 承諾 – 計算可用性預算、規劃維護窗口

關鍵原則:

  • 獨立事件的連續機率 = P^n,但要小心驗證獨立性假設
  • 成功率看似很高,連續次數多時機率會快速下降
  • 實務中要結合時間、負載、依賴關係綜合考量
  • 使用程式碼驗證你的假設,不要憑直覺判斷

下次設計系統可靠性策略時,這些機率計算將幫助你做出更明智的技術決策。

相關文章

Leave a Comment