關聯式資料庫統治資料儲存 30 多年,但無綱要(NoSQL)資料庫的流行顯示變革正在發生。RDBMS 雖為傳統 Client-Server 架構提供堅實基礎,卻難以廉價、輕鬆地擴展至多節點。在 Facebook、Twitter 等高擴展性 Web 應用時代,這是致命弱點。早期替代方案(如 OODB)未解決核心問題,而 NoSQL(Google Bigtable、Amazon SimpleDB)作為對 Web 高擴展性需求的直接回應而崛起,或許是 Web 2.0 演進下的「殺手級應用」。
開發者常以為使用 NoSQL 需徹底改變思維。實際上取決於初始資料建模方法:
非關聯資料儲存無 Join、無主鍵/外鍵概念(僅以鬆散形式存在)。若嘗試以關聯建模為基礎在 NoSQL 建模,可能以失敗告終。從領域模型出發能簡化遷移,無綱要結構的彈性更能重燃開發活力。

無綱要儲存賦予先用對象設計領域模型的彈性(Grails 等現代框架自動支援)。下一步將領域映射到底層儲存,Google App Engine (GAE) 使此過程極簡單。
Gaelyk 利用 Google 的 Entity 物件進行物件持久化:
def ticket = new Entity("ticket")
ticket.officer = params.officer
ticket.license = params.plate
ticket.issuseDate = offensedate
ticket.location = params.location
ticket.notes = params.notes
ticket.offense = params.offense
若頻繁使用實體會顯得繁瑣,更自然的選擇是建模領域對象(如 Ticket 類別)。
Sharding 將表結構複寫至多節點,邏輯上分割資料。但跨 Shard Join 是棘手難題。無綱要儲存優勢在於無需預知一切,比關聯結構更易適應變更。不預先定義領域對象屬性,利用 Groovy 動態特性建立 GAE Entity 的領域對象代理,專注於如何查詢對象與處理關聯——這是 NoSQL 框架尚未內建的功能。
abstract class Model {
def entity
static def datastore = DatastoreServiceFactory.datastoreService
public Model(params) {
this.@entity = new Entity(this.getClass().simpleName)
params.each { key, val -> this.setProperty(key, val) }
}
public Model(Entity entity) { this.@entity = entity }
def getProperty(String name) {
if (name == "id") return entity.key.id
else return entity."${name}"
}
void setProperty(String name, value) { entity."${name}" = value }
def save() { entity.save() }
def getEntity(entityType, id) {
def key = KeyFactory.createKey(entityType, id)
return datastore.get(key)
}
}
關鍵技巧:@entity 繞過存取器;getClass().simpleName 設定 Entity 種類;getProperty 智能處理 id。
class Race extends Model {
public Race(params) { super(params) }
def addRunner(runner) {
if (this.@entity.runners) this.@entity.runners << runner.id
else this.@entity.runners = [runner.id]
}
def getRunners() {
return this.@entity.runners.collect { new Runner(getEntity(Runner.class.simpleName, it)) }
}
}
class Runner extends Model {
public Runner(params) { super(params) }
def addRace(race) {
if (this.@entity.races) this.@entity.races << race.id
else this.@entity.races = [race.id]
}
def getRaces() {
return this.@entity.races.collect { new Race(getEntity(Race.class.simpleName, it)) }
}
}
// 建立 Race
def iparams = ["name": "Charlottesville Marathon", "date": rdate, "distance": 26.2]
def race = new Race(iparams)
race.save()
// 查詢方法
static def findByName(name) { ... }
static def findAllByName(name, pagination=10) { ... }

def runner = new Runner([fname:"Chris", lname:"Smith", age:34])
runner.save()
race.addRunner(runner); race.save()
runner.addRace(race); runner.save()

無綱要儲存彈性:屬性按需自動新增,無需更新結構部署。
| 優勢 | 劣勢 |
|---|---|
| 極高彈性:新增屬性無需遷移,舊實例自動為 null | 犧牲一致性與完整性:無級聯刪除,需自行實作完整性檢查 |
| 讀取速度極快,適合百萬用戶級應用 | 需嚴格紀律:屬性命名不一致將帶來維護噩夢 |
| 領域視角建模,而非關聯視角 | 缺乏成熟持久層(如 Hibernate)支援 |
NoSQL 不會取代 RDBMS,但會補充它。無數成功工具建立於關聯資料庫之上,RDBMS 尚無過時風險。NoSQL 為物件-關聯模型提供及時替代方案,證明某些場景下不僅可行甚至更優。最適用於需高速資料檢索與擴展性的多節點 Web 應用,極佳副作用是讓開發者從領域視角而非關聯視角進行資料建模。