データベース
Azure Cosmos DB
概要
Azure Cosmos DBは、Microsoftが提供するグローバル分散型のマルチモデルデータベースサービスです。高可用性、自動スケーリング、一桁ミリ秒の応答時間を特徴とし、NoSQLとベクターデータベースの機能を組み合わせた最新のクラウドネイティブデータベースです。複数のAPIをサポートし、世界中のあらゆる規模のアプリケーションに対応します。
詳細
Azure Cosmos DBは2017年にMicrosoftによって発表され、従来のDocumentDBを進化させた次世代のデータベースサービスです。以下の特徴を持ちます。
主要特徴
- グローバル分散: 世界54以上のリージョンで即座にデータを複製
- マルチモデル: SQL、MongoDB、Cassandra、Gremlin、Table APIをサポート
- 自動スケーリング: オートパイロット機能による無制限スケーリング
- 低レイテンシ: 読み取り・書き込みともに10ms未満
- 複数の整合性レベル: Strong、Bounded staleness、Session、Consistent prefix、Eventualから選択
- AI統合: ベクター検索、フルテキスト検索、ハイブリッド検索
- 無制限のスループット: 任意のスケールでのパフォーマンス保証
- 包括的SLA: 可用性99.999%、レイテンシ、スループット、整合性を保証
2024-2025年の新機能
- ベクター検索: DiskANNアルゴリズムによる高性能ベクター検索
- フルテキスト検索: BM25アルゴリズムによるキーワード・フレーズ検索
- ハイブリッド検索: ベクター検索とフルテキスト検索の組み合わせ
- 多言語サポート: 英語、フランス語、スペイン語、ドイツ語対応
- AI Foundry統合: Azure AIとの統合によるエージェントアプリケーション開発
- ファジー検索: タイポや文字変更への耐性
アーキテクチャ
- データモデル: 階層構造(アカウント > データベース > コンテナ > アイテム)
- 自動インデックス: 全フィールドの自動インデックス作成
- スキーマレス: 柔軟なJSONドキュメント構造
- パーティション: 水平分散によるスケーラビリティ
- マルチマスター: 複数リージョンでの書き込み対応
整合性レベル
- Strong: 線形化可能性保証
- Bounded staleness: 設定可能な遅延上限
- Session: 単一セッション内での読み取り一貫性
- Consistent prefix: プレフィックス一貫性
- Eventual: 結果整合性
メリット・デメリット
メリット
- グローバルスケール: 世界規模での自動データ分散と複製
- 高パフォーマンス: 一桁ミリ秒の応答時間とオートスケーリング
- 包括的SLA: 99.999%可用性、レイテンシ、スループット保証
- マルチモデル: 複数のAPIとデータモデルを単一サービスで提供
- 運用不要: フルマネージドサービスでインフラ管理不要
- AI機能: ベクター検索、フルテキスト検索、AI統合
- 柔軟な整合性: アプリケーションニーズに応じた整合性レベル選択
- Azure統合: Azureエコシステムとの完全統合
デメリット
- 高いコスト: 従量課金制で大規模利用時は高額になる可能性
- Azure依存: Azureクラウドのみでの提供、ベンダーロックイン
- 学習コスト: 複数のAPIと設定オプションの理解が必要
- 複雑な料金体系: RU(Request Unit)ベースの課金体系
- データサイズ制限: アイテムあたり2MBの制限
- クエリ制限: 複雑なクエリや集計処理に制約
- リージョン制限: 一部リージョンでは利用不可
主要リンク
書き方の例
インストール・セットアップ
# Azure CLIでCosmos DBアカウント作成
az cosmosdb create \
--name mycosmosaccount \
--resource-group myresourcegroup \
--default-consistency-level Session \
--locations regionName=Japan East failoverPriority=0 isZoneRedundant=False
# データベースとコンテナ作成
az cosmosdb sql database create \
--account-name mycosmosaccount \
--name mydatabase \
--resource-group myresourcegroup
az cosmosdb sql container create \
--account-name mycosmosaccount \
--database-name mydatabase \
--name mycontainer \
--partition-key-path "/partitionKey" \
--throughput 400 \
--resource-group myresourcegroup
# Node.js SDK インストール
npm install @azure/cosmos
# .NET SDK インストール
dotnet add package Microsoft.Azure.Cosmos
# Python SDK インストール
pip install azure-cosmos
基本操作(CRUD)
// Node.js での接続と基本操作
const { CosmosClient } = require('@azure/cosmos');
// 接続設定
const client = new CosmosClient({
endpoint: 'https://myaccount.documents.azure.com:443/',
key: 'your-primary-key'
});
const database = client.database('mydatabase');
const container = database.container('mycontainer');
async function basicOperations() {
try {
// ドキュメント作成
const newItem = {
id: 'item-001',
partitionKey: 'electronics',
name: 'ノートパソコン',
brand: 'Sample Brand',
price: 120000,
category: 'electronics',
specifications: {
cpu: 'Intel Core i7',
memory: '16GB',
storage: '512GB SSD'
},
tags: ['コンピュータ', 'ビジネス', 'モバイル'],
createdAt: new Date().toISOString()
};
const { resource: createdItem } = await container.items.create(newItem);
console.log('作成されたアイテム:', createdItem);
// ドキュメント読み取り
const { resource: readItem } = await container.item('item-001', 'electronics').read();
console.log('読み取ったアイテム:', readItem);
// ドキュメント更新
readItem.price = 115000;
readItem.updatedAt = new Date().toISOString();
const { resource: updatedItem } = await container.item('item-001', 'electronics').replace(readItem);
console.log('更新されたアイテム:', updatedItem);
// クエリ実行
const querySpec = {
query: 'SELECT * FROM c WHERE c.category = @category AND c.price < @maxPrice',
parameters: [
{ name: '@category', value: 'electronics' },
{ name: '@maxPrice', value: 150000 }
]
};
const { resources: results } = await container.items.query(querySpec).fetchAll();
console.log('クエリ結果:', results);
// ドキュメント削除
await container.item('item-001', 'electronics').delete();
console.log('アイテムが削除されました');
} catch (error) {
console.error('操作中にエラーが発生しました:', error);
}
}
basicOperations();
データモデリング
// 複雑なドキュメント構造の例
const customerDocument = {
id: 'customer-12345',
partitionKey: 'customer-12345',
customerType: 'premium',
profile: {
firstName: '田中',
lastName: '太郎',
email: '[email protected]',
phone: '090-1234-5678',
birthDate: '1985-04-15',
address: {
zipCode: '100-0001',
prefecture: '東京都',
city: '千代田区',
street: '千代田1-1-1',
building: 'サンプルビル101'
}
},
orders: [
{
orderId: 'order-001',
orderDate: '2024-01-15T10:30:00Z',
status: 'delivered',
items: [
{
productId: 'prod-001',
name: 'ワイヤレスヘッドフォン',
quantity: 1,
unitPrice: 25000,
totalPrice: 25000
}
],
totalAmount: 25000,
deliveryAddress: {
zipCode: '100-0001',
prefecture: '東京都',
city: '千代田区',
street: '千代田1-1-1'
}
}
],
preferences: {
language: 'ja',
currency: 'JPY',
notifications: {
email: true,
sms: false,
push: true
},
categories: ['electronics', 'books', 'clothing']
},
loyaltyProgram: {
tier: 'gold',
points: 15000,
joinDate: '2023-01-01T00:00:00Z'
},
metadata: {
createdAt: '2023-01-01T00:00:00Z',
updatedAt: '2024-01-15T15:45:00Z',
version: 2,
source: 'mobile-app'
}
};
// インデックス戦略
async function indexingStrategy() {
// 複合インデックス設定例
const indexingPolicy = {
indexingMode: 'consistent',
automatic: true,
includedPaths: [
{
path: '/*'
}
],
excludedPaths: [
{
path: '/metadata/*'
}
],
compositeIndexes: [
[
{ path: '/customerType', order: 'ascending' },
{ path: '/profile/email', order: 'ascending' }
],
[
{ path: '/orders/[]/status', order: 'ascending' },
{ path: '/orders/[]/orderDate', order: 'descending' }
]
]
};
console.log('インデックス戦略:', indexingPolicy);
}
ベクター検索・AI機能
// ベクター検索の実装例
async function vectorSearchExample() {
// ベクター埋め込み付きドキュメント
const documentWithVector = {
id: 'doc-vector-001',
partitionKey: 'documents',
title: 'Azure Cosmos DB の紹介',
content: 'Azure Cosmos DB は、グローバルに分散されたマルチモデルデータベースサービスです。',
category: 'technology',
// OpenAI等で生成されたベクター埋め込み
contentVector: [0.1, 0.2, -0.3, 0.4, 0.5, /* ... 1536次元のベクター */],
createdAt: new Date().toISOString()
};
await container.items.create(documentWithVector);
// ベクター検索クエリ
const searchVector = [0.15, 0.25, -0.25, 0.35, 0.45]; // 検索用ベクター
const vectorSearchQuery = {
query: `
SELECT TOP 10 c.id, c.title, c.content,
VectorDistance(c.contentVector, @searchVector) AS similarity
FROM c
WHERE c.category = @category
ORDER BY VectorDistance(c.contentVector, @searchVector)
`,
parameters: [
{ name: '@searchVector', value: searchVector },
{ name: '@category', value: 'technology' }
]
};
const { resources: vectorResults } = await container.items.query(vectorSearchQuery).fetchAll();
console.log('ベクター検索結果:', vectorResults);
// ハイブリッド検索(フルテキスト + ベクター)
const hybridSearchQuery = {
query: `
SELECT c.id, c.title, c.content,
VectorDistance(c.contentVector, @searchVector) AS vectorSimilarity,
RANK FullTextScore(c.content, @searchTerms) AS textScore
FROM c
WHERE CONTAINS(c.content, @searchTerms)
ORDER BY (VectorDistance(c.contentVector, @searchVector) * 0.6 +
(1.0 - RANK FullTextScore(c.content, @searchTerms)) * 0.4)
`,
parameters: [
{ name: '@searchVector', value: searchVector },
{ name: '@searchTerms', value: 'データベース サービス' }
]
};
const { resources: hybridResults } = await container.items.query(hybridSearchQuery).fetchAll();
console.log('ハイブリッド検索結果:', hybridResults);
}
パフォーマンス最適化
// 高パフォーマンス操作の実装
class CosmosDBOptimizer {
constructor(container) {
this.container = container;
}
// バルクオペレーション
async bulkInsert(documents) {
const operations = documents.map(doc => ({
operationType: 'Create',
resourceBody: doc
}));
const { result, statusCode } = await this.container.items.bulk(operations);
console.log(`バルク挿入完了: ${result.length}件, ステータス: ${statusCode}`);
return result;
}
// 効率的なページネーション
async paginateItems(querySpec, pageSize = 100) {
const iterator = this.container.items.query(querySpec, {
maxItemCount: pageSize
});
const results = [];
while (iterator.hasMoreResults()) {
const { resources, continuationToken } = await iterator.fetchNext();
results.push(...resources);
console.log(`取得件数: ${resources.length}, 継続トークン: ${continuationToken}`);
if (results.length >= 1000) { // 最大1000件で停止
break;
}
}
return results;
}
// トランザクショナル操作
async transactionalBatch(partitionKey, operations) {
const transactionalBatch = this.container.items.batch(operations, partitionKey);
const { result } = await transactionalBatch.execute();
console.log('トランザクション実行結果:', result);
return result;
}
// 効率的な集計クエリ
async aggregateData() {
const aggregateQuery = {
query: `
SELECT
c.category,
COUNT(1) as itemCount,
AVG(c.price) as averagePrice,
SUM(c.price) as totalValue,
MIN(c.price) as minPrice,
MAX(c.price) as maxPrice
FROM c
GROUP BY c.category
`
};
const { resources } = await this.container.items.query(aggregateQuery).fetchAll();
return resources;
}
}
// 使用例
const optimizer = new CosmosDBOptimizer(container);
// パフォーマンス監視
async function monitorPerformance() {
const startTime = Date.now();
try {
const result = await container.items.query({
query: 'SELECT * FROM c WHERE c.category = @category',
parameters: [{ name: '@category', value: 'electronics' }]
}).fetchAll();
const endTime = Date.now();
const duration = endTime - startTime;
console.log(`クエリ実行時間: ${duration}ms`);
console.log(`結果件数: ${result.resources.length}`);
console.log(`RU消費量: ${result.requestCharge}`);
} catch (error) {
console.error('クエリエラー:', error);
}
}
セキュリティ・ベストプラクティス
// セキュリティ設定とベストプラクティス
const { DefaultAzureCredential } = require('@azure/identity');
// Azure ADによる認証
const aadClient = new CosmosClient({
endpoint: 'https://myaccount.documents.azure.com:443/',
aadCredentials: new DefaultAzureCredential()
});
// データ暗号化設定
const encryptionConfig = {
encryptionKey: {
encryptionAlgorithm: 'AEAD_AES_256_CBC_HMAC_SHA256',
wrappingAlgorithm: 'RSA_OAEP',
keyWrapMetadata: {
name: 'my-key',
type: 'AzureKeyVault',
value: 'https://my-keyvault.vault.azure.net/keys/my-key'
}
}
};
// 接続時のセキュリティ設定
const secureClient = new CosmosClient({
endpoint: 'https://myaccount.documents.azure.com:443/',
key: 'your-key',
connectionPolicy: {
enableEndpointDiscovery: false,
preferredLocations: ['Japan East'],
useMultipleWriteLocations: false
},
plugins: [
{
on: 'request',
plugin: async (context, diagNode) => {
// セキュリティヘッダーの追加
context.headers['x-ms-client-version'] = 'secure-app-v1.0';
return context;
}
}
]
});
// リソースベースアクセス制御
async function setupResourcePermissions() {
// 読み取り専用ユーザー用のリソーストークン生成例
const permissions = {
permissionMode: 'Read',
resource: 'dbs/mydatabase/colls/mycontainer',
tokenExpiryTime: new Date(Date.now() + 60 * 60 * 1000) // 1時間有効
};
console.log('リソース権限設定:', permissions);
}
// データマスキング
function maskSensitiveData(document) {
const masked = { ...document };
// 個人情報のマスキング
if (masked.profile?.email) {
masked.profile.email = masked.profile.email.replace(/(.{2}).*(@.*)/, '$1***$2');
}
if (masked.profile?.phone) {
masked.profile.phone = masked.profile.phone.replace(/(\d{3}).*(\d{4})/, '$1-****-$2');
}
return masked;
}