← Назад к вопросам
В чем разница между реляционными и нереляционными БД с точки зрения масштабирования?
1.7 Middle🔥 181 комментариев
#Архитектура и паттерны#Базы данных (NoSQL)#Базы данных (SQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между реляционными и нереляционными БД с точки зрения масштабирования
Основные подходы
- Реляционные БД (SQL): масштабирование "вертикальное" — добавляем больше ресурсов одному серверу
- Нереляционные БД (NoSQL): масштабирование "горизонтальное" — добавляем больше серверов
Вертикальное масштабирование (SQL)
Одна большая машина:
┌─────────────────────┐
│ PostgreSQL │
│ (32 CPU, 512 GB) │
└─────────────────────┘
Принцип
# Когда нужно больше мощности — покупаем сервер мощнее
# До масштабирования
server = {"cpu": 4, "ram": 32, "storage": 1000} # ГБ
# После масштабирования
server = {"cpu": 64, "ram": 512, "storage": 10000} # ГБ
# Нужно перестартовать БД — downtime!
Преимущества
# 1. Консистентность данных
# Все данные на одной машине → ACID гарантии
db.execute("""
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- Либо всё выполнится, либо ничего
""")
# 2. Простые запросы с JOINами
query = """
SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.country = 'Russia'
""" # Работает на одной машине
# 3. Foreign Keys и constraints
db.execute("""
ALTER TABLE orders
ADD CONSTRAINT fk_user_id
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE
""")
Недостатки
# 1. Дорого — серверы растут экспоненциально
servers_cost = {
"4 CPU, 32 GB": 100,
"8 CPU, 64 GB": 250,
"16 CPU, 256 GB": 800, # Не в 4 раза дороже!
"64 CPU, 512 GB": 4000
}
# 2. Есть потолок — максимальный сервер
max_performance = 512 # CPU максимум
# 3. Нет отказоустойчивости — если упадёт, упадёт всё
data_loss = "Если один сервер сломается, потеря данных"
# 4. Downtime при масштабировании
process = "выключить БД → добавить ресурсы → включить"
Горизонтальное масштабирование (NoSQL)
Множество маленьких машин (шардирование):
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Node 1 │ │ Node 2 │ │ Node 3 │
│ shard │ │ shard │ │ shard │
│ (4 CPU) │ │ (4 CPU) │ │ (4 CPU) │
└─────────┘ └─────────┘ └─────────┘
↑ ↑ ↑
100 GB 100 GB 100 GB
Принцип
import hashlib
# Когда нужно больше мощности — добавляем новый сервер
servers = 3 # Было 3 сервера
servers = 4 # Добавили 4-й
servers = 5 # Добавили 5-й
# Без downtime!
# Данные распределяются по шардам (примеру по user_id)
def get_shard(user_id, total_shards):
hash_value = int(hashlib.md5(str(user_id).encode()).hexdigest(), 16)
return hash_value % total_shards
user_id = 42
shard = get_shard(user_id, 3) # Узел 1
print(f"User {user_id} in shard {shard}")
Преимущества
# 1. Доступно — добавляем маленькие серверы
servers_cost = {
"4 CPU, 32 GB": 100,
"4 CPU, 32 GB": 100, # Добавляем ещё один
"4 CPU, 32 GB": 100 # И ещё один
} # Итого 300 за 3 сервера
# 2. Нет потолка — можем добавлять бесконечно
for i in range(1000):
add_new_server() # Просто добавляем
# 3. Отказоустойчивость
# Если один сервер упадёт, остальные работают
if server1_down:
# Реплики на других серверах
data_still_available = True
# 4. Без downtime при добавлении сервера
process = "добавить новый сервер → перебалансировка данных"
Недостатки
# 1. Нет транзакций между шардами
try:
# Деньги на разных шардах
shard_a.update("transfer -100 from user 1")
shard_b.update("transfer +100 to user 2")
# Если shard_b упадёт между операциями — деньги потеряются!
except Exception:
# Откатить shard_a?
pass
# 2. Сложные JOINы работают плохо
# Если юзер на shard 1, а его заказы на shard 2
query = """
SELECT u.name, o.amount FROM users u JOIN orders o
-- Данные на разных серверах!
""" # Нужно запросить оба сервера → медленно
# 3. Сложность — нужно решать проблемы
# Распределённое консенсус, eventual consistency
is_consistent_now = False # Данные синхронизуются с задержкой
Практическое сравнение
# SQL (вертикальное) — для критичных систем
class BankDatabase:
def transfer_money(self, from_id, to_id, amount):
with db.transaction(): # ACID гарантия
account1 = db.query(f"SELECT * FROM accounts WHERE id={from_id}")
account2 = db.query(f"SELECT * FROM accounts WHERE id={to_id}")
if account1.balance >= amount:
db.update(f"UPDATE accounts SET balance = balance - {amount} WHERE id={from_id}")
db.update(f"UPDATE accounts SET balance = balance + {amount} WHERE id={to_id}")
# Либо обе операции выполнены, либо ни одна
# NoSQL (горизонтальное) — для больших объёмов данных
class LogDatabase:
def log_event(self, user_id, event):
# Данные распределяются по шардам
shard = get_shard(user_id)
shard.insert({
"user_id": user_id,
"event": event,
"timestamp": datetime.now()
}) # Пишется асинхронно, eventual consistency
Когда выбрать SQL
# 1. Финансовые системы (нужна консистентность)
case = "Банки, платёжные системы"
# 2. Сложные связи между сущностями
case = "E-commerce (users, products, orders, reviews)"
# 3. Если объём данных < 100 TB
case = "Большинство приложений"
# 4. Нужны сложные запросы с JOINами
query = """
SELECT p.name, SUM(o.amount) as total_revenue
FROM products p
JOIN order_items oi ON p.id = oi.product_id
JOIN orders o ON oi.order_id = o.id
GROUP BY p.name
ORDER BY total_revenue DESC
""" # Работает эффективно на SQL
Когда выбрать NoSQL
# 1. Очень большие объёмы данных (> 100 TB)
data_size = "tera bytes"
# 2. Быстрый рост данных
case = "Социальные сети (миллиарды постов/день)"
# 3. High write throughput
case = "IoT, логирование, аналитика"
# 4. Гибкая схема
data = {
"user1": {"name": "Alice", "age": 30},
"user2": {"name": "Bob", "email": "bob@example.com"} # Разные поля
}
# 5. Нужна отказоустойчивость
replicas = 3 # Копируем на 3 сервера
Гибридный подход
# Используем оба подхода
architecture = {
"primary": "PostgreSQL (главные данные, ACID)",
"secondary": "MongoDB (логирование, аналитика)",
"cache": "Redis (кэш для быстрого доступа)"
}
# Операция: сохраняем заказ
def create_order(user_id, items):
# SQL — критичные данные
with postgres.transaction():
order = postgres.insert("orders", data)
# NoSQL — логирование
mongodb.insert("logs", {
"event": "order_created",
"order_id": order.id,
"timestamp": datetime.now()
})
# Cache — быстрый доступ
redis.set(f"order:{order.id}", order.to_json(), ttl=3600)