「DTU – Basic」是一個非常親民的入門選擇,每月僅約 $5 USD,對於初期開發或流量極小的系統非常友善。但校務建言系統(尤其是若涉及全校性活動或高峰期)在使用 Basic 級別時,可能會遇到一些明顯的瓶頸。
以下是 DTU – Basic 在開發校務建言系統時,可能「不夠用」的幾種情況:
1. 併發連線數(Max Concurrent Workers)限制
這是 Basic 最致命的短板。
- 限制:Basic 僅支援 30 個併發工作執行緒。
- 情境:如果校方發出一封全校 Email 邀請大家來建言,瞬間有 50 位同學點開網頁,資料庫會立刻排隊甚至報錯(Timeouts),導致系統看起來像掛掉了。
- 建議:若預期會有瞬間高流量(如開會期間、校園新聞發布),Basic 可能無法負荷。
2. IOPS 與資料吞吐量效能
- 限制:Basic 的 I/O 效能非常基礎。
- 情境:雖然你將附件存在 Blob Storage(這是非常正確的作法!),但資料庫仍需處理建言內容的 全文檢索 或 複雜查詢(例如:管理員要篩選「過去一年、所有關於總務處、且狀態為未處理」的建言)。當資料筆數累積到幾千筆以上時,這種分群彙整(Group By)的查詢速度會變得很慢。
3. 資料庫大小上限
- 限制:Basic 的硬碟空間上限通常只有 2 GB。
- 情境:雖然附件在 Blob,但如果建言系統包含大量文字回覆、系統 Log、或多個關聯資料表,2 GB 可能在 1-2 年內就會用滿。
4. 缺乏進階功能
- 限制:Basic 不支援 Point-in-Time Restore (PITR) 較長的天數(僅 7 天),且不支援異地備份。
- 情境:如果有人不小心刪錯資料,你只能回推到過去 7 天內。
針對「校務建言系統」的架構建議
既然你已經規劃將附件存放在 Blob Storage,這大大減輕了資料庫的壓力。以下是針對你的專案建議的部署策略:
策略 A:初期省錢法 (開發階段)
- 使用 Basic (5 DTU):在開發與內部測試(10 人以內)時完全足夠。
- 優點:成本極低,適合慢慢磨功能。
策略 B:上線初期/正式運行 (彈性調整)
- 升級至 Standard (S0 或 S1):
- S0 (10 DTU):約 $15 USD/月,支援 60 個併發連線,容量可達 250GB。
- S1 (20 DTU):約 $30 USD/月,效能明顯提升,適合應對一般校園流量。
策略 C:極致省錢法 (使用 Serverless)
如果你不希望固定每個月付 $15~$30 USD,可以考慮從 DTU 模式切換到 vCore – Serverless:
- 運作邏輯:建言系統通常「晚上沒人用」或「假日時段冷門」。Serverless 可以在沒人點擊網頁時自動暫停。
- 費用:可能比 Standard 更便宜,卻能提供比 Basic 更強大的瞬間處理能力。
總結建議
你的校務系統**「平時」用 Basic 是可以的,但「宣傳推廣期」**一定會卡頓。
我的實務建議:
先用 Basic 開發。等系統準備正式發布給學生使用的那一天,手動在 Azure Portal 將級別調整至 Standard S1(調整過程資料庫不會停機)。等宣傳熱潮過了,再降回 Basic 或 S0 節省開支。
如何在程式碼中(例如 C# 或 Python)實作「上傳附件到 Blob 並將 URL 寫回 SQL」的最佳實踐
這是一個非常標準且高效的雲端開發模式:SQL 存放結構化資料(後設資料),Blob Storage 存放非結構化的大檔案(圖片、PDF、文件)。
這種做法能讓資料庫保持輕量,避免 DTU - Basic 的空間迅速被填滿。以下是實作「校務建言系統」附件處理的最佳實踐與範例:
1. 資料庫資料表設計 (SQL Side)
不要在 SQL 中存檔案本身(Blob 類型),而是存檔案的 URL 和 唯一識別碼 (GUID)。
SQL
CREATE TABLE Suggestions (
Id INT PRIMARY KEY IDENTITY(1,1),
StudentId NVARCHAR(50),
Content NVARCHAR(MAX),
CreatedAt DATETIME DEFAULT GETDATE()
);
CREATE TABLE Attachments (
Id INT PRIMARY KEY IDENTITY(1,1),
SuggestionId INT, -- 關聯到建言
FileName NVARCHAR(255),
BlobUrl NVARCHAR(2048), -- 儲存 Blob 的完整路徑
BlobName GUID, -- 建議用 GUID 命名檔案以防重複
FOREIGN KEY (SuggestionId) REFERENCES Suggestions(Id)
);
2. 檔案上傳邏輯 (Workflow)
在開發時,建議遵循以下流程以確保效能:
- 前端上傳:使用者送出建言與檔案。
- 後端處理:
- 產生一個唯一的檔名(例如
guid.jpg)。 - 使用 Azure Storage SDK 將檔案上傳至 Blob Storage。
- 取得該檔案的 永久連結 (URL) 或路徑。
- 產生一個唯一的檔名(例如
- 寫入資料庫:將建言文字與該 URL 同步寫入 SQL Database。
3. C# 程式碼範例 (使用 Azure.Storage.Blobs)
這是最常見的實作方式,簡潔且安全:
C#
// 1. 初始化 Blob 客戶端
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient("school-attachments");
// 2. 上傳檔案
string blobName = Guid.NewGuid().ToString() + Path.GetExtension(file.FileName);
BlobClient blobClient = containerClient.GetBlobClient(blobName);
using (var stream = file.OpenReadStream())
{
await blobClient.UploadAsync(stream, true);
}
// 3. 取得 URL 並存入 SQL
string fileUrl = blobClient.Uri.ToString();
// 接下來使用 ADO.NET 或 Entity Framework 將 fileUrl 存入 Attachments 表
4. 進階安全性建議:SAS Token
由於校務系統可能涉及隱私(例如建言者不想讓別人看到他的附件),你不應該把 Blob Container 設為「公開讀取」。
- 做法:將 Container 設為 Private (私用)。
- 讀取方式:當使用者要查看附件時,後端產生一個 暫時性的簽署 URL (Shared Access Signature, SAS)。
- 優點:這個連結會在 10 分鐘或 1 小時後失效,即使連結外流,別人也無法下載。
5. 為什麼這樣做對 Basic 級別最有利?
- 節省 DTU 消耗:資料庫不需要處理大數據流(I/O),只需處理簡短的文字字串。
- 節省空間:SQL Basic 的 2GB 限制可以撐非常久,因為幾萬筆 URL 頂多才幾十 MB。
- 成本優勢:Blob Storage 每 GB 的價格(約 $0.02 USD)遠低於 SQL Database 空間。
使用 Python 實作「上傳檔案至 Blob 並將 URL 寫入 SQL」是非常直覺的。我們通常會使用 azure-storage-blob 套件處理檔案,並使用 pyodbc 或 SQLAlchemy 處理資料庫。
以下是一個完整的邏輯範例:
1. 安裝必要套件
在你的開發環境中執行:
Bash
pip install azure-storage-blob pyodbc
2. Python 實作程式碼
這個範例展示了如何接收一個檔案、上傳到 Azure,最後把資料存進 SQL Database。
Python
import os
import uuid
from azure.storage.blob import BlobServiceClient
import pyodbc
# --- 1. 設定連線資訊 ---
# Azure Blob Storage 連線字串
AZURE_STORAGE_CONNECTION_STRING = "DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;"
CONTAINER_NAME = "school-attachments"
# Azure SQL 連線資訊 (注意:Basic 級別請確保防火牆已開放 IP)
SQL_CONNECTION_STRING = (
"Driver={ODBC Driver 18 for SQL Server};"
"Server=tcp:your-server.database.windows.net,1433;"
"Database=your-db;"
"Uid=your-username;Pwd=your-password;"
"Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;"
)
def upload_suggestion_with_attachment(student_id, content, file_path):
try:
# --- 2. 上傳檔案到 Blob Storage ---
blob_service_client = BlobServiceClient.from_connection_string(AZURE_STORAGE_CONNECTION_STRING)
container_client = blob_service_client.get_container_client(CONTAINER_NAME)
# 產生唯一檔名避免重複 (例如: 550e8400-e29b.pdf)
file_extension = os.path.splitext(file_path)[1]
unique_blob_name = f"{uuid.uuid4()}{file_extension}"
blob_client = container_client.get_blob_client(unique_blob_name)
print(f"正在上傳檔案: {unique_blob_name}...")
with open(file_path, "rb") as data:
blob_client.upload_blob(data)
# 取得上傳後的 URL
blob_url = blob_client.url
print(f"上傳成功!URL: {blob_url}")
# --- 3. 將資料寫入 SQL Database ---
conn = pyodbc.connect(SQL_CONNECTION_STRING)
cursor = conn.cursor()
# 插入建言並取得 ID (SQL Server 語法)
cursor.execute(
"INSERT INTO Suggestions (StudentId, Content) OUTPUT INSERTED.Id VALUES (?, ?)",
(student_id, content)
)
suggestion_id = cursor.fetchone()[0]
# 插入附件資訊
cursor.execute(
"INSERT INTO Attachments (SuggestionId, FileName, BlobUrl, BlobName) VALUES (?, ?, ?, ?)",
(suggestion_id, os.path.basename(file_path), blob_url, unique_blob_name)
)
conn.commit()
print("資料庫寫入完成!")
except Exception as ex:
print(f"發生錯誤: {ex}")
finally:
if 'conn' in locals():
conn.close()
# 測試執行
upload_suggestion_with_attachment("S123456", "飲水機過濾網該換了", "report.pdf")
3. 進階架構優化(省錢與安全)
在實際開發校務系統時,還有兩點建議:
A. 非同步上傳 (Async)
如果你的系統是使用 FastAPI 或 Django,建議使用 azure-storage-blob 的非同步版本 (BlobServiceClient from azure.storage.blob.aio)。這樣當多個學生同時上傳大檔案時,不會卡住整個伺服器的執行緒。
B. 使用受控識別 (Managed Identity) —— 強烈建議
與其在程式碼中寫死資料庫密碼(Uid/Pwd),你可以啟用 Web App 的 Managed Identity。
- 好處:不需要在設定檔存密碼。
- 安全性:即使程式碼外流,別人也拿不到資料庫權限。
- 適用:這在跨資源群組存取時非常方便且安全。
4. 針對 DTU – Basic 的特別提醒
在使用 Python 連線時,請務必在 pyodbc 的連線字串中加入 Connection Timeout=30。
因為 Basic 級別的資源較少,有時候冷啟動或排隊會稍微慢一點,適度增加超時時間可以減少連線失敗的機率。