Cosmos DB -- глобально распределенная NoSQL-база данных Azure с гарантией задержки менее 10 мс. На AZ-204 особое внимание уделяется partition keys, consistency levels и работе с SDK.
Иерархия ресурсов
Account -> Database -> Container -> Items
- Account -- верхний уровень, определяет API, регионы, consistency по умолчанию
- Database -- логическая группировка контейнеров, shared throughput
- Container -- аналог таблицы, определяет partition key
- Items -- документы JSON (до 2 MB каждый)
Выбор Partition Key
Правильный выбор partition key -- самое важное архитектурное решение при работе с Cosmos DB. Он определяет, как данные распределяются между физическими разделами.
Хороший partition key:
- Имеет много уникальных значений (высокая кардинальность)
- Равномерно распределяет данные и запросы
- Часто используется в WHERE-условиях запросов
Примеры для разных сценариев:
- Заказы:
/customerId-- запросы обычно фильтруются по клиенту - Логи:
/dateили/tenantId-- равномерное распределение по дням или арендаторам - IoT данные:
/deviceId-- каждое устройство генерирует свои данные
Плохой partition key:
- Значение с низкой кардинальностью (
/country-- всего ~200 значений) - Монотонно растущие значения (
/timestamp-- все записи идут в один раздел) - Значение, которое не используется в запросах
Уровни согласованности (Consistency Levels)
Cosmos DB предлагает 5 уровней, от строгого к слабому:
| Уровень | Описание | Задержка | Доступность | RU стоимость |
|---|---|---|---|---|
| Strong | Линеаризуемое чтение | Высокая | Ниже | Высокая |
| Bounded Staleness | Чтение отстает не более чем на K версий или T секунд | Средняя | Средняя | Средняя |
| Session | Согласованность в рамках клиентской сессии | Низкая | Высокая | Низкая |
| Consistent Prefix | Чтения никогда не видят неупорядоченные записи | Низкая | Высокая | Низкая |
| Eventual | Нет гарантий порядка, максимальная производительность | Минимальная | Максимальная | Минимальная |
Session -- рекомендуемый уровень по умолчанию. Он гарантирует: ваша сессия видит свои записи (read-your-own-writes), но другие сессии могут видеть данные с небольшой задержкой.
Работа с Cosmos DB SDK в C#
Подключение
using Microsoft.Azure.Cosmos;
using Azure.Identity;
// Рекомендуемый способ -- через Managed Identity
var client = new CosmosClient(
"https://myaccount.documents.azure.com:443/",
new DefaultAzureCredential(),
new CosmosClientOptions
{
SerializerOptions = new CosmosSerializationOptions
{
PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase
},
ConnectionMode = ConnectionMode.Direct,
MaxRetryAttemptsOnRateLimitedRequests = 5
});
var database = client.GetDatabase("OrderDB");
var container = database.GetContainer("Orders");
CRUD-операции
// Create
var order = new Order
{
Id = Guid.NewGuid().ToString(),
CustomerId = "cust-001",
Total = 150.00m,
Status = "Created"
};
await container.CreateItemAsync(order, new PartitionKey(order.CustomerId));
// Read (Point Read -- самая быстрая операция, 1 RU)
var response = await container.ReadItemAsync<Order>(
order.Id,
new PartitionKey(order.CustomerId));
var readOrder = response.Resource;
// response.RequestCharge -- стоимость операции в RU
// Replace
readOrder.Status = "Confirmed";
await container.ReplaceItemAsync(readOrder, readOrder.Id,
new PartitionKey(readOrder.CustomerId));
// Delete
await container.DeleteItemAsync<Order>(order.Id,
new PartitionKey(order.CustomerId));
// Upsert (создать или обновить)
await container.UpsertItemAsync(order, new PartitionKey(order.CustomerId));
Запросы
// Параметризованный запрос
var query = new QueryDefinition(
"SELECT * FROM c WHERE c.customerId = @cid AND c.status = @status")
.WithParameter("@cid", "cust-001")
.WithParameter("@status", "Created");
var iterator = container.GetItemQueryIterator<Order>(query);
while (iterator.HasMoreResults)
{
var results = await iterator.ReadNextAsync();
Console.WriteLine($"RU charge: {results.RequestCharge}");
foreach (var item in results)
{
Console.WriteLine($"Order: {item.Id}, Total: {item.Total}");
}
}
Cross-partition vs Single-partition запросы
Если запрос включает partition key в WHERE -- это single-partition запрос (дешевый, быстрый). Если нет -- cross-partition запрос (сканирует все разделы, дорогой).
// Single-partition (быстрый): partition key в запросе
var query = new QueryDefinition("SELECT * FROM c WHERE c.customerId = @cid")
.WithParameter("@cid", "cust-001");
// Cross-partition (дорогой): нет partition key
var query = new QueryDefinition("SELECT * FROM c WHERE c.total > 100");
Change Feed
Change Feed -- поток изменений в контейнере Cosmos DB. Позволяет реагировать на каждое создание или обновление документа.
Сценарии использования:
- Синхронизация данных между базами (CQRS read model)
- Event-driven архитектура
- Real-time аналитика
- Инвалидация кеша
// Change Feed Processor
var changeFeedProcessor = container
.GetChangeFeedProcessorBuilder<Order>("orderProcessor", HandleChanges)
.WithInstanceName("instance-1")
.WithLeaseContainer(leaseContainer) // Separate container for tracking
.Build();
await changeFeedProcessor.StartAsync();
// Handler
static async Task HandleChanges(
IReadOnlyCollection<Order> changes,
CancellationToken ct)
{
foreach (var order in changes)
{
Console.WriteLine($"Changed order: {order.Id}, Status: {order.Status}");
// Update read model, invalidate cache, send notification...
}
}
Change Feed также работает как триггер Azure Functions:
[Function("SyncOrderViews")]
public async Task Run(
[CosmosDBTrigger(
databaseName: "OrderDB",
containerName: "Orders",
Connection = "CosmosDB",
LeaseContainerName = "leases",
CreateLeaseContainerIfNotExists = true)]
IReadOnlyList<Order> changes)
{
foreach (var doc in changes)
{
_logger.LogInformation("Processing change: {Id}", doc.Id);
}
}
Request Units (RU) и оптимизация
1 RU = стоимость чтения одного документа 1 KB по ID и partition key (Point Read).
Советы по оптимизации RU:
- Используйте Point Read вместо запросов, когда возможно
- Всегда указывайте partition key в запросах
- Проектируйте данные для чтения (денормализация)
- Мониторьте
response.RequestChargeдля каждой операции