Azure SQL Database
Полноценный SQL Server без сервера: кто-то другой ставит патчи, делает бэкапы и держит реплики.
Azure SQL Database — управляемая (PaaS) реляционная база на движке SQL Server. Вы получаете базу и строку подключения; операционная система, установка обновлений, резервное копирование и отказоустойчивость — забота Microsoft.
Зачем платить за «управляемость»
Сравните два мира. В первом вы поднимаете виртуальную машину, ставите SQL Server, настраиваете расписание бэкапов, проверяете, что они восстанавливаются, следите за местом на диске, раз в квартал ставите патчи в четыре утра и собираете кластер из двух узлов, чтобы пережить перезагрузку. Во втором — выполняете одну команду CLI и получаете базу, у которой бэкапы уже настроены, а SLA доступности прописан в договоре.
За это берут наценку. Но посчитайте стоимость часа инженера, который поднимает HA-кластер и потом сопровождает его три года — наценка перестаёт казаться большой. Именно поэтому в новых проектах Azure SQL берут по умолчанию, а голый SQL Server на VM оставляют для случаев, где нужен доступ к самой ОС, к агентам, к нестандартным расширениям или к очень старой версии движка.
Три формы: одиночная база, пул и Managed Instance
| Форма | Суть | Когда брать |
| Single database | Одна база с собственными ресурсами. Изолирована, масштабируется отдельно. | Дефолт для приложения с одной базой. |
| Elastic pool | Много баз делят общий пул ресурсов. | SaaS с базой на каждого клиента: пики у всех в разное время, платите за пул, а не за 200 отдельных баз. |
| Managed Instance | Почти полноценный экземпляр SQL Server: SQL Agent, cross-database запросы, CLR, Service Broker. | Переезд «как есть» со своего SQL Server, где приложение опирается на инстанс-уровневые фичи. |
Ключевая ловушка новичка — ожидать от Single database поведения инстанса. Внутри неё не работают ни USE OtherDatabase, ни запросы к соседней базе через три точки, ни SQL Server Agent с его джобами. Каждая база — остров. Если приложение так устроено, вам либо в Managed Instance, либо в рефакторинг.
Модели покупки: DTU против vCore
Мощность базы в Azure измеряют двумя способами, и от выбора зависит и цена, и предсказуемость.
DTU — «попугаи»
DTU (Database Transaction Unit) — синтетическая единица, в которой смешаны процессор, память и операции ввода-вывода в фиксированной пропорции. Вы не выбираете «4 ядра и 16 ГБ», вы выбираете «100 DTU» и получаете какой-то усреднённый набор. Уровни: Basic (крошечный, для песочницы), Standard (S0…S12), Premium (P1…P15, быстрые диски и read-реплика).
Плюс — простота: одна ручка, крути её. Минус — непрозрачность: если ваша нагрузка упирается только в CPU, вы всё равно доплачиваете за пропорционально накрученный IO.
vCore — честные ядра
vCore-модель показывает то, за что вы платите: количество виртуальных ядер, поколение процессора, объём хранилища — отдельно. Уровни:
| General Purpose | Рабочая лошадка: вычисления и хранилище разнесены, удалённые SSD. 95% приложений. |
| Business Critical | Локальные NVMe-диски и встроенный кластер из реплик: минимальная задержка, есть бесплатная read-only реплика. Дорого. |
| Hyperscale | Хранилище до 100 ТБ, мгновенные бэкапы, быстрое добавление реплик. Для больших баз. |
Отдельно стоит Serverless — вариант General Purpose, где вы задаёте диапазон ядер (например, от 0.5 до 2), а Azure сам масштабирует базу под нагрузку и ставит её на паузу после периода простоя. На паузе вы платите только за хранилище. Для dev- и тестовых сред, а также для внутренних систем, которыми пользуются в рабочие часы, это способ срезать счёт в разы.
Плата за автопаузу — холодный старт: первый запрос после простоя ждёт, пока база проснётся (обычно десятки секунд) и может отвалиться по таймауту. Приложение обязано это переживать.
Практика: создаём сервер и базу
В Azure SQL есть логический сервер — это не машина, а всего лишь конечная точка с именем, аутентификацией и правилами файрвола. Базы живут внутри него.
az group create --name rg-sql-demo --location westeurope
az sql server create \
--name ccsql-demo7 \
--resource-group rg-sql-demo \
--location westeurope \
--admin-user sqladmin \
--admin-password 'S3cure-P@ss-changeme'
# Serverless-база: от 0.5 до 2 ядер, засыпает после часа простоя
az sql db create \
--name shopdb \
--server ccsql-demo7 \
--resource-group rg-sql-demo \
--edition GeneralPurpose \
--family Gen5 \
--compute-model Serverless \
--min-capacity 0.5 \
--capacity 2 \
--auto-pause-delay 60 \
--backup-storage-redundancy Local
Firewall-правила: кто вообще сюда достучится
По умолчанию сервер закрыт для всех. Пока вы не добавите правило, даже вы сами не подключитесь.
# разрешить свой текущий IP (Azure подставит его сам)
az sql server firewall-rule create \
--name my-laptop \
--server ccsql-demo7 \
--resource-group rg-sql-demo \
--start-ip-address 203.0.113.17 \
--end-ip-address 203.0.113.17
# «Allow Azure services» — особое правило 0.0.0.0
az sql server firewall-rule create \
--name allow-azure-services \
--server ccsql-demo7 \
--resource-group rg-sql-demo \
--start-ip-address 0.0.0.0 \
--end-ip-address 0.0.0.0
Второе правило выглядит пугающе, но 0.0.0.0/0.0.0.0 здесь — не «весь интернет», а зарезервированный маркер «разрешить ресурсам Azure». Проблема в другом: любым ресурсам Azure, включая чужие подписки. Для продакшена это слишком широко — там базу прячут за Private Endpoint, и она получает приватный IP внутри вашей виртуальной сети, а публичный доступ отключается вовсе.
Бэкапы, которые уже работают
Это то, за что PaaS любят. Azure SQL непрерывно снимает резервные копии сам:
- PITR (point-in-time restore) — восстановление на любой момент внутри окна хранения: 7 дней по умолчанию, настраивается до 35.
- LTR (long-term retention) — еженедельные/ежемесячные/ежегодные копии до 10 лет, для регуляторных требований.
- Geo-restore — восстановление в другом регионе из георепличной копии бэкапа.
az sql db restore \
--name shopdb \
--dest-name shopdb-restored \
--server ccsql-demo7 \
--resource-group rg-sql-demo \
--time "2026-07-14T09:30:00Z"
Запомните главное: восстановление всегда создаёт новую базу. Поверх существующей Azure не пишет — и это правильно, иначе неудачный restore уничтожил бы последний живой экземпляр. Дальше вы сами решаете: переключить приложение на восстановленную базу или вытащить из неё пару таблиц.
Подключение из приложения
Строка подключения выглядит привычно для любого, кто видел SQL Server:
Driver={ODBC Driver 18 for SQL Server};
Server=tcp:ccsql-demo7.database.windows.net,1433;
Database=shopdb;
Uid=sqladmin;
Pwd=***;
Encrypt=yes;
TrustServerCertificate=no;
Connection Timeout=30;
import os
import pyodbc
# строку подключения держим в переменной окружения, а не в коде
conn_str = os.environ["SQL_CONNECTION_STRING"]
with pyodbc.connect(conn_str, timeout=30) as conn:
cursor = conn.cursor()
cursor.execute(
"SELECT TOP 5 id, email FROM users ORDER BY created_at DESC"
)
for row in cursor.fetchall():
print(row.id, row.email)
Пароль в строке подключения — вчерашний день. Правильный способ для сервисов, живущих в Azure, — Managed Identity: приложение получает токен у платформы, и никакого секрета вообще не существует.
import struct
import pyodbc
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
token = credential.get_token("https://database.windows.net/.default")
# драйвер ждёт токен в виде UTF-16 с четырёхбайтовым префиксом длины
raw = token.token.encode("utf-16-le")
token_struct = struct.pack(f"<I{len(raw)}s", len(raw), raw)
conn = pyodbc.connect(
"Driver={ODBC Driver 18 for SQL Server};"
"Server=tcp:ccsql-demo7.database.windows.net,1433;"
"Database=shopdb;Encrypt=yes;",
attrs_before={1256: token_struct}, # SQL_COPT_SS_ACCESS_TOKEN
)
Повторные попытки — не опция, а обязанность
Облачная база иногда разрывает соединение: плановое обновление движка, переезд на другой узел, срабатывание лимита. Клиент получит ошибку вроде 40613 Database is not currently available или 40501 Service is busy. Это transient errors — временные, и через секунду-другую всё работает.
Приложение обязано их переживать: повторить запрос с растущей паузой. Вот как выглядит классический экспоненциальный откат:
def backoff_delays(attempts=5, base=0.5, cap=8.0):
"""Паузы перед повторными попытками: удваиваем, но не бесконечно."""
return [min(cap, base * 2 ** i) for i in range(attempts)]
print("Паузы перед повторами, сек:", backoff_delays())
Результат:
Паузы перед повторами, сек: [0.5, 1.0, 2.0, 4.0, 8.0]
В реальном коде к каждой паузе добавляют случайный «джиттер», чтобы сотня инстансов приложения не ломанулась переподключаться в одну и ту же миллисекунду. В Entity Framework Core за это отвечает EnableRetryOnFailure(), в Python-мире — обёртки вроде tenacity.
Как это работает
Под капотом это тот же движок SQL Server, но упакованный иначе. Ваш «логический сервер» ccsql-demo7.database.windows.net — это DNS-имя, за которым стоит шлюз (gateway) на порту 1433. Клиент сначала приходит на шлюз, тот аутентифицирует и говорит, на каком узле сейчас живёт ваша база.
Дальше есть два режима. В Redirect клиент подключается к узлу базы напрямую — быстрее, но нужно открыть порты 11000–11999. В Proxy весь трафик идёт через шлюз — работает через любой корпоративный файрвол, но добавляет задержку. Изнутри Azure по умолчанию выбирается Redirect, снаружи — Proxy.
Отказоустойчивость встроена: в General Purpose данные лежат в реплицированном хранилище, и при сбое узла база просто поднимается на другом (десятки секунд). В Business Critical рядом крутится кворум реплик — переключение почти мгновенное. Для катастрофы регионального масштаба существуют failover group: вторая база в другом регионе плюс общее DNS-имя, которое само переезжает при аварии.
Лимиты по DTU/vCore применяются жёстко: если вы упёрлись в потолок IO, запросы начинают ждать, а не отваливаться. В sys.dm_db_resource_stats видно, какой именно ресурс исчерпан — CPU, IO или лог транзакций.
Частые ошибки
- Правило файрвола 0.0.0.0–255.255.255.255. Оно появляется, когда «ничего не работает, надо срочно». Так база оказывается открыта всему интернету, и остаётся только пароль администратора. Это находят сканеры, которые непрерывно перебирают
*.database.windows.net. - Business Critical на dev-стенде. Разница с General Purpose — кратная. Для разработки берут Serverless с автопаузой или Basic.
- «База ведь не используется ночью». Provisioned-база тарифицируется 24/7, независимо от того, приходил ли к ней хоть один запрос. На паузу умеет вставать только Serverless — и то надо явно задать
--auto-pause-delay. - Нет retry-логики. Плановое обновление платформы — это несколько секунд недоступности примерно раз в месяц. Без повторных попыток они превращаются в инцидент и разбор полётов.
- Удалили логический сервер вместе с бэкапами. Бэкапы живут при сервере. Снесли сервер — снесли и точки восстановления баз в нём. LTR-копии переживают удаление базы, но не удаление сервера без предварительной настройки.
- Ожидание, что заработает
USE another_db. В Single database межбазовые запросы и SQL Agent недоступны. Это архитектурное ограничение, а не баг. - Холодный старт Serverless под нагрузкой. Автопауза на проде у сервиса, который дёргают раз в 10 минут, даёт постоянные пробуждения и рваную задержку. Автопаузу включают там, где простой действительно длинный.
Итоги
- Azure SQL Database — SQL Server как сервис: патчи, бэкапы и отказоустойчивость входят в цену.
- Формы: Single database (по умолчанию), Elastic pool (много баз, общий бюджет), Managed Instance (переезд «как есть»).
- DTU — простая, но непрозрачная единица; vCore показывает реальные ядра и уровни General Purpose / Business Critical / Hyperscale.
- Serverless умеет засыпать и экономить кратно — ценой холодного старта.
- Бэкапы автоматические: PITR 7–35 дней, LTR до 10 лет; восстановление всегда создаёт новую базу.
- Сервер закрыт файрволом; для продакшена — Private Endpoint, а не «разрешить всё».
- Transient errors неизбежны — повторные попытки с экспоненциальной паузой обязательны в любом клиенте.