Azure Cosmos DB 建立與使用
什麼是非關聯式資料庫?
最近這幾年,非關聯式資料庫開始成為許多大型系統的架構考慮之一,原因有很多,但歸根究底核心原因我覺得是『全球化』。
過去企業(即便是大型的全球化企業)在建立資訊系統的時候,大部分都是以集中式為主的架構,也就是說,核心的資料庫只會有一份,頂多加上異地備援機制。
在傳統的企業營運需求上,這樣的設計完全沒問題,而且關聯式資料庫的特性就是ACID,有著結構清晰的資料表,經過正規化,存入的資料欄位都是固定的,這確保了資料的高可靠性與正確性,對企業應用系統來說,恰是所需。
但對於全球化的網際網路需求,可就不一定了。
當前的關聯式資料庫儲存資料有幾種類型,像是底下這樣:
你發現,這些資料的結構跟過去方方正正的資料表很是不同,現實生活中,有很多類似這樣的資料,例如:
若我們想表達一位用戶的朋友、資源、行事曆、檔案…等資訊,結構上就很可能不是傳統關聯式資料表所能表達的,這時候非關連式資料庫就開始派上用場。
除此之外,另外一個主因是 --『同步一致性』。
資料的同步問題
在企業內,資料的一致性似乎很重要,但一致性並非沒有代價,假設資料庫採分散式架構,在全球3-5個點都有資料庫,彼此之間進行同步抄寫。
這樣的架構底下,要維持一致性是很困難的,這也是大部分企業應用都採集中式架構的原因,但集中式架構最大的缺點是,如果總部斷線,就意味著全球各地區都別運作了。
但真實世界中,我們真的需要在全球有數個彼此同步抄寫的資料庫嗎? 過去沒有,但現在可能到處都是,最明顯的例子就是像FB之類的社群網站。
社群網站每秒中有數以萬計的留言對話,可能同時需要存入資料庫,如果採用傳統的關聯式資料庫,又是集中式設計,很可能根本無法運作,因為資料庫的loading太大了,光寫入資料就沒時間了,更何況還要承擔全球性的讀取。
所以非常有可能採用全球數個資料庫彼此抄寫的狀況,但這時候,『資料同步』將是個問題。
我鍵入一筆留言,在台灣的資料中心可以即時看到並回應,但在地球另一端的資料庫中卻可能還沒出現,因此我在國外的友人會隔一段時間才看到(需要等資料抄寫過去),然後他才能回應,同樣的當國外的友人回應,在台灣的我可能也不會立刻看到,而會延遲一段時間。
這就是分散式資料庫可能會碰到的問題。
這對於開發人員來說,是兩難。因為集中式效率會變低,分散式會導致資料的精確性或即時性降低。
所幸,目前大部分需要分散式資料庫的場景中,對於精確性或即時性的要求並不高,這時NoSQL Database正好符合需求。
而Azure 上的Cosmos DB,正是這樣的資料庫。
當你建立好Cosmos DB後,可以設定多個自動抄寫的全球同步:
如此一來,你的資料庫可在一個地點寫入,多個區域讀出,以達到最佳的使用效能。而這仰賴高效的一致性抄寫,Cosmos DB具備五種內建的自動化一致性設定,也可以從後台進行設定:
透過Cosmos DB,我們可以在資訊系統設計時,同時兼顧效率與資訊的即時性,以因應全球化高併發流量的需求。
使用Cosmos DB
您可以透過Azure Portal建立Cosmic DB,我們先以SQL API為範例:
操作的過程可參考底下影片:
完成後,我們可以先建立資料庫與container(collection):
上面影片中,展示了如何建立一個ToDoList DB,並建立三筆以JSON為格式的資料庫,接著透過 SQL API進行搜尋。
你會發現,如果採用SQL API,資料庫的使用與搜尋,其實和過去傳統的關聯式資料庫很接近。
透過SDK操作Cosmos DB
我們當然也可以透過C# SDK,對 Cosmos DB 進行 database、record item的建立,並且透過程式碼呼叫SQL API進行查詢。
使用到的套件是 Microsoft.Azure.Cosmos,您可以在專案中透過 CLI 安裝:
dotnet add package Microsoft.Azure.Cosmos
我在github上,撰寫了一個AZ-204課程中,給學員的範例程式碼,位於:
https://github.com/isdaviddong/az204-cosmicdemo.git
您可以透過 git clone 指令下載:
git clone https://github.com/isdaviddong/az204-cosmicdemo.git
你會看到該程式是 console app的形式,建立資料庫與Container的程式碼如下:
private async Task CreateDatabaseAsync()
{
// Create a new database
this.database = await this.cosmosClient.CreateDatabaseIfNotExistsAsync(databaseId);
Console.WriteLine("建立 Database: {0}\n", this.database.Id);
}
private async Task CreateContainerAsync()
{
// Create a new container
this.container = await this.database.CreateContainerIfNotExistsAsync(containerId, "/LastName");
Console.WriteLine("建立 Container: {0}\n", this.container.Id);
}
底下這段程式新增JSON data record(個人通訊錄),每一筆資料錄都是透過呼叫網路上的假資料產生器 getFakeData() 建立的:
private async Task InsertData()
{
Console.Write("\n請輸入要動態產生的資料筆數(ex. 30): ");
var tmp=Console.ReadLine();
var n=int.Parse(tmp);
for (int i = 0; i < n; i++)
{
var person = getFakeData();
var name = person.name.ToString().Split(" ");
var lastName = name[name.Length - 1];
var firstName = name[name.Length - 2];
var rec = new DataRecord()
{
id = Guid.NewGuid().ToString(),
FirstName = firstName,
LastName = lastName,
Address = person.address,
FullName = person.name.ToString()
};
var item = await this.container.CreateItemAsync<DataRecord>(rec, new PartitionKey(lastName));
Console.WriteLine("Created item {0}: \n{1}\n", i, rec.FirstName );
}
Console.WriteLine($"\n{n} items has been added...");
}
建立好資料之後,您可以從 cosmos db 的 explorer 中看到每一筆資料:
底下這段程式碼則是查詢的部分:
private async Task QueryData()
{
Console.Write("\n請輸入查詢關鍵字(例如. ab): ");
var key=Console.ReadLine();
//查詢
var sqlQueryText = "SELECT * FROM c WHERE CONTAINS(c.FullName,'"+key+"') ";
Console.WriteLine("執行查詢: {0}\n", sqlQueryText);
QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText);
FeedIterator<DataRecord> queryResultSetIterator =
this.container.GetItemQueryIterator<DataRecord>(queryDefinition);
while (queryResultSetIterator.HasMoreResults)
{
FeedResponse<DataRecord> currentResultSet = queryResultSetIterator.ReadNextAsync().Result;
foreach (DataRecord DataRecord in currentResultSet)
{
Console.WriteLine("\n符合的項目 : {0}", DataRecord.FullName);
}
}
}
底下這段影片,展示了整個操作過程:
留言