← Назад к вопросам

Как оценить надёжность всей системы, если известна надёжность отдельных компонентов?

3.0 Senior🔥 81 комментариев
#DevOps и инфраструктура#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Оценка надёжности системы из компонентов

Это вопрос, который часто встречается в практической разработке. Надёжность системы зависит от архитектуры, взаимодействия компонентов и критических точек отказа.

Теория: умножение вероятностей

Для последовательных компонентов (один отказ обвалит всю систему) надёжность умножается.

Радёжность системы = R1 × R2 × R3 × ... × Rn

Если:  R1 = 0.99 (БД)
       R2 = 0.99 (API Server)
       R3 = 0.99 (Cache)

То:    Rsystem = 0.99 × 0.99 × 0.99 = 0.970299 (97%)

Это показывает критическую проблему: каждый добавленный компонент понижает общую надёжность.

Параллельные компоненты (Redundancy)

Для параллельных компонентов (достаточно одного рабочего) формула другая.

Вероятность отказа всей системы = (1 - R1) × (1 - R2) × (1 - R3)
Надёжность = 1 - (вероятность отказа)

Если у нас 3 сервера, каждый с R = 0.99:
P(все отказали) = (1 - 0.99)³ = 0.01³ = 0.000001
Надёжность = 1 - 0.000001 = 0.999999 (99.9999%)
def system_reliability_parallel(individual_reliabilities: list[float]) -> float:
    """Вычисляет надёжность системы с параллельными компонентами"""
    failure_probability = 1.0
    for reliability in individual_reliabilities:
        failure_probability *= (1 - reliability)
    return 1 - failure_probability

def system_reliability_series(individual_reliabilities: list[float]) -> float:
    """Вычисляет надёжность системы с последовательными компонентами"""
    reliability = 1.0
    for r in individual_reliabilities:
        reliability *= r
    return reliability

# Примеры
print(system_reliability_series([0.99, 0.99, 0.99]))  # 0.970299 (97%)
print(system_reliability_parallel([0.99, 0.99, 0.99]))  # 0.999999 (99.9999%)

Реальная архитектура: смешанная модель

На практике системы состоят из обеих типов компонентов.

from typing import Union

class Component:
    def __init__(self, name: str, reliability: float):
        self.name = name
        self.reliability = reliability

class SeriesSystem:  # Последовательная система
    def __init__(self, components: list[Component]):
        self.components = components
    
    @property
    def reliability(self) -> float:
        result = 1.0
        for comp in self.components:
            result *= comp.reliability
        return result

class ParallelSystem:  # Параллельная система
    def __init__(self, components: list[Component]):
        self.components = components
    
    @property
    def reliability(self) -> float:
        failure_prob = 1.0
        for comp in self.components:
            failure_prob *= (1 - comp.reliability)
        return 1 - failure_prob

# Реальный пример
db_server = Component('PostgreSQL', 0.9995)  # 99.95%
db_failover = Component('PostgreSQL Replica', 0.9995)
db_system = ParallelSystem([db_server, db_failover])  # Высокая доступность

api_node_1 = Component('API Server 1', 0.999)
api_node_2 = Component('API Server 2', 0.999)
load_balancer = Component('Load Balancer', 0.9999)
api_system = SeriesSystem([
    ParallelSystem([api_node_1, api_node_2]),
    load_balancer
])

whole_system = SeriesSystem([db_system, api_system])
print(f"DB System reliability: {db_system.reliability:.6f}")  # 0.999998
print(f"API System reliability: {api_system.reliability:.6f}")
print(f"Whole system reliability: {whole_system.reliability:.6f}")

Практический подход: MTBF и MTTR

Вместо абстрактных вероятностей используем реальные метрики:

from dataclasses import dataclass
from datetime import timedelta

@dataclass
class ComponentMetrics:
    name: str
    mtbf: timedelta  # Mean Time Between Failures (среднее время между отказами)
    mttr: timedelta  # Mean Time To Recover (среднее время восстановления)
    
    @property
    def availability(self) -> float:
        """Availability = MTBF / (MTBF + MTTR)"""
        total = self.mtbf + self.mttr
        return self.mtbf.total_seconds() / total.total_seconds()

# Реальные данные из мониторинга
db_metrics = ComponentMetrics(
    name='PostgreSQL',
    mtbf=timedelta(days=365),  # Падает раз в год
    mttr=timedelta(minutes=30)   # Восстанавливается за 30 минут
)

api_metrics = ComponentMetrics(
    name='API Server',
    mtbf=timedelta(days=180),   # Падает раз в полгода
    mttr=timedelta(minutes=5)    # Восстанавливается за 5 минут
)

print(f"DB Availability: {db_metrics.availability:.6f}")  # 99.994%
print(f"API Availability: {api_metrics.availability:.6f}")  # 99.998%

# Availability системы = произведение availabilities компонентов
system_availability = db_metrics.availability * api_metrics.availability
print(f"System Availability: {system_availability:.6f}")  # 99.992%

Отказоустойчивость: Graceful Degradation

Надёжная система не полностью падает при отказе компонента — она деградирует.

from enum import Enum

class SystemStatus(Enum):
    HEALTHY = 'healthy'
    DEGRADED = 'degraded'
    FAILING = 'failing'

class ResilientSystem:
    def __init__(self, db_available: bool, cache_available: bool, api_available: bool):
        self.db = db_available
        self.cache = cache_available
        self.api = api_available
    
    @property
    def status(self) -> SystemStatus:
        # БД — критично (последовательный компонент)
        if not self.db:
            return SystemStatus.FAILING
        
        # API — важно
        if not self.api:
            return SystemStatus.FAILING
        
        # Cache — опционально (параллельный компонент)
        if not self.cache:
            return SystemStatus.DEGRADED  # Работает, но медленнее
        
        return SystemStatus.HEALTHY
    
    def handle_request(self, data: dict) -> dict:
        match self.status:
            case SystemStatus.HEALTHY:
                result = self._get_cached(data['id'])  # Быстро из кеша
                if not result:
                    result = self._query_db(data['id'])  # Из БД
                return result
            
            case SystemStatus.DEGRADED:
                # Кеш не работает, берём из БД
                return self._query_db(data['id'])
            
            case SystemStatus.FAILING:
                raise SystemUnavailable("Database is down")
    
    def _get_cached(self, id):
        if self.cache:
            # Redis запрос
            pass
        return None
    
    def _query_db(self, id):
        if self.db:
            # PostgreSQL запрос
            pass
        return None

Практические рекомендации

1. Определить критические пути — какие компоненты НЕ могут падать

# Критический путь: User → API → DB
# Опциональные: Cache, Queue, Analytics

2. Добавить избыточность критическим компонентам

# DB: Master-Replica setup (99.98% → 99.9998%)
# API: 3+ серверов за LB
# Cache: может упасть — дополнительная проверка в коде

3. Отслеживать реальные метрики

# Prometheus + Grafana
get_from_cache_success_rate
db_query_latency_p95
api_error_rate_5xx
service_restart_count

4. Тестировать отказы (Chaos Engineering)

import random

class ChaoticSystem:
    def __init__(self, failure_rate: float):
        self.failure_rate = failure_rate
    
    async def call_service(self):
        if random.random() < self.failure_rate:
            raise ServiceDown("Chaos: component failure")
        return await real_service()

# Тестируем: что происходит, если 5% запросов к API падают?

Итоги

Надёжность системы зависит от:

  • Архитектуры: последовательные vs параллельные компоненты
  • Избыточности: как мы обрабатываем отказы
  • Гейсовой деградации: какой функционал доступен при отказе
  • Реальных метрик: MTBF, MTTR, Availability

Формула: для последовательных компонентов Rsystem = R1 × R2 × R3, для параллельных Rsystem = 1 - (1 - R1) × (1 - R2) × (1 - R3).