Главная / 🗄️ Базы данных

🗄️ Базы данных

Безопасная работа с PostgreSQL и MySQL через Claude Code. Read-only роль, MCP-инструменты, защита миграций, настройки против OOM при ETL-операциях.

🛡️ Философия: Claude = только чтение

Главное правило: Claude Code имеет доступ к БД только для чтения. Все изменения схемы (миграции) выполняются разработчиком вручную. Это предотвращает случайную потерю данных.

Claude может
  • SELECT запросы к любым таблицам
  • EXPLAIN ANALYZE для анализа планов
  • Просмотр схемы (information_schema)
  • Анализ индексов и статистик
  • Написать SQL для миграции
  • Сгенерировать migration-файл
Claude не может
  • INSERT / UPDATE / DELETE данных
  • DROP TABLE / TRUNCATE
  • ALTER TABLE напрямую
  • Запускать миграции (migrate:fresh)
  • pg_dump / mysqldump
  • Изменять пользователей и права

🐘 PostgreSQL — настройка доступа

Шаг 1: Создать read-only роль

-- Выполнить от суперпользователя: CREATE ROLE claude_readonly WITH LOGIN PASSWORD 'STRONG_PASSWORD_HERE'; -- Дать доступ к схеме GRANT CONNECT ON DATABASE my_database TO claude_readonly; GRANT USAGE ON SCHEMA public TO claude_readonly; -- Только SELECT на все существующие таблицы GRANT SELECT ON ALL TABLES IN SCHEMA public TO claude_readonly; -- И на будущие таблицы ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO claude_readonly;

Шаг 2: Настроить postgres-mcp в .mcp.json

// project/.mcp.json { "mcpServers": { "postgres-pro": { "command": "uvx", "args": [ "--from", "postgres-mcp", "postgres-mcp", "--access-mode=restricted" // ← Только SELECT, блок DDL на уровне MCP ], "env": { "DATABASE_URI": "postgresql://claude_readonly:PASS@host.docker.internal:5432/my_db" } } } }
Двойная защита: роль claude_readonly (уровень PostgreSQL) + --access-mode=restricted (уровень MCP). Даже если Claude попытается выполнить UPDATE — PostgreSQL откажет.

Шаг 3: Настройки PostgreSQL против OOM

При ETL-операциях с большими объёмами данных PostgreSQL потреблял всю WSL2 память и убивался OOM. Зафиксированные параметры:

-- Применить без рестарта (кроме shared_buffers): ALTER SYSTEM SET shared_buffers = '4GB'; -- требует рестарта PG ALTER SYSTEM SET work_mem = '64MB'; ALTER SYSTEM SET max_parallel_workers_per_gather = '1'; SELECT pg_reload_conf(); -- применить без рестарта
ПараметрЗначениеПричина
shared_buffers4GBНе более 25% от WSL2 лимита (38GB). 16GB → OOM
work_mem64MBМножится на N workers × N processes. Большой = OOM
max_parallel_workers_per_gather1При ETL каждый воркер = отдельный work_mem блок
⚠️
ETL режим: перед массовым INSERT/UPDATE установить max_parallel_workers_per_gather=1, после ETL вернуть. Иначе OOM при параллельных операциях.

🔄 Миграции — безопасный процесс

Laravel Migrations

// ✅ Правильный процесс: // 1. Claude ГЕНЕРИРУЕТ migration файл: // database/migrations/2026_05_08_create_payments_table.php // 2. Разработчик ПРОВЕРЯЕТ файл вручную // 3. Разработчик ЗАПУСКАЕТ вручную: // docker exec backend-1 php artisan migrate // или через docker MCP: mcp__docker__docker_exec({ container: "backend-1", command: "php artisan migrate" }) // 4. Перед production — ВСЕГДА pg_dump вручную! // ❌ ЗАПРЕЩЕНО (hooks заблокируют): php artisan migrate:fresh // Удалит ВСЕ данные! php artisan db:wipe // Уничтожит БД! php artisan migrate:reset // Откатит все миграции!

Alembic (Python)

# ✅ Правильный процесс Alembic: # 1. Claude СОЗДАЁТ revision файл alembic revision --autogenerate -m "add_payments_table" # 2. Разработчик ПРОВЕРЯЕТ versions/*.py # 3. Claude или разработчик запускает через MCP: mcp__docker__docker_exec({ container: "python-etl-app-1", command: "alembic upgrade head" }) # 4. Откат при проблеме: # alembic downgrade -1
🚫
НИКОГДА не давать Claude выполнять alembic downgrade base — это откатит все миграции и уничтожит схему. Только -1 (один шаг назад).

🐬 MySQL — настройка доступа

Для MySQL/MariaDB — аналогичный подход с read-only пользователем:

-- Создать read-only пользователя MySQL: CREATE USER 'claude_ro'@'%' IDENTIFIED BY 'STRONG_PASSWORD'; GRANT SELECT, SHOW VIEW ON my_database.* TO 'claude_ro'@'%'; FLUSH PRIVILEGES;
// .mcp.json для MySQL (используем другой MCP или прямой запрос): { "mcpServers": { "mysql": { "command": "npx", "args": ["-y", "@benborla29/mcp-server-mysql"], "env": { "MYSQL_HOST": "host.docker.internal", "MYSQL_PORT": "3306", "MYSQL_USER": "claude_ro", "MYSQL_PASSWORD": "STRONG_PASSWORD", "MYSQL_DATABASE": "my_database", "ALLOW_INSERT_OPERATION": "false", "ALLOW_UPDATE_OPERATION": "false", "ALLOW_DELETE_OPERATION": "false" } } } }

🔍 Типичные запросы через postgres-mcp

Анализ структуры таблицы

-- Через mcp__postgres-pro__query: -- Структура таблицы SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = 'payments' ORDER BY ordinal_position; -- Индексы таблицы SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'payments'; -- Медленные запросы (pg_stat_statements) SELECT query, calls, mean_exec_time, total_exec_time FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;

EXPLAIN для анализа производительности

-- Анализ плана запроса (только читает, не меняет): EXPLAIN ANALYZE SELECT * FROM payments p JOIN users u ON p.user_id = u.id WHERE p.status = 'pending' AND p.created_at > NOW() - INTERVAL '7 days'; -- Это безопасно! EXPLAIN не модифицирует данные

⚡ ETL — правила безопасного импорта

При ETL-пайплайнах Claude Code помогает писать код, но сами операции выполняются в контейнере через MCP:

# Python ETL — правила из CLAUDE.md: # 1. Тестовая БД — всегда SQLite in-memory: from sqlalchemy import create_engine engine = create_engine("sqlite:///:memory:") # Никогда не prod в тестах! # 2. Батчинг — не более 10K строк за транзакцию: BATCH_SIZE = 10_000 for i in range(0, len(records), BATCH_SIZE): batch = records[i:i + BATCH_SIZE] with session.begin(): session.bulk_insert_mappings(Model, batch) # 3. Логирование каждого шага: logger.info(f"ETL step=extract start") # ... логика ... logger.info(f"ETL step=extract end count={len(records)}") # 4. Rollback при ошибке: except Exception as e: session.rollback() etl_errors_table.insert(error=str(e), step="transform")

💾 Резервное копирование перед рискованными операциями

# PostgreSQL дамп ПЕРЕД миграцией (вручную!): mcp__docker__docker_exec({ container: "my-project-db-1", command: "pg_dump -U postgres my_db > /tmp/backup_before_migration.sql" }) # Или через docker (если Bash не заблокирован): # docker exec my-project-db-1 pg_dump -U postgres my_db > backup.sql # Полный Caddy backup (включает сертификаты): # proxy backup
💡
Правило бэкапа: pg_dump должен выполняться разработчиком вручную, не Claude. Hooks блокируют pg_dump из bash. Если нужен дамп — выполняй ! docker exec ... напрямую в терминале вне Claude Code.