Google Cloud Spanner 學(xué)習(xí)報告
17 Jan 2019
本人介紹,未使用過 Google Cloud Spanner,不是 DBA本股。
簡介
Google 的 AdWords 曾經(jīng)是靠 MySQL+手工Sharding來支撐,但在擴展和可靠性上不能滿足要求。于是開發(fā)了全局一致性和自動 Sharding 的數(shù)據(jù)庫 Spanner夹纫。
Spanner 帶動了一波云原生數(shù)據(jù)庫的熱潮,云原生數(shù)據(jù)庫的特點是设凹,計算和存儲分離舰讹,增加 node 就可以增加存儲能力和性能;自動分片闪朱;支持分布式事務(wù)月匣。
國內(nèi)的云原生數(shù)據(jù)有阿里云的 云數(shù)據(jù)庫POLARDB,騰訊云的
云數(shù)據(jù)庫 CynosDB奋姿,這兩個數(shù)據(jù)庫的特點還有完全兼容 MySQL锄开,其中 CynosDB 還可以支持 PostgreSQL 10(參考時間 2019年1月17日),現(xiàn)有應(yīng)用無需更改 SDK称诗,減少數(shù)據(jù)庫遷移的成本萍悴。
TrueTime
Spanner 數(shù)據(jù)庫告訴了人們,當(dāng)數(shù)據(jù)容量增加粪狼,分片數(shù)量增加退腥,依然可以享受高性能和事務(wù)全局一致性,依靠的最驚艷的技術(shù)就是 TrueTime再榄。
注意狡刘,Spanner 的可序列化(serializability)依靠的還是 Lock,但是外部一致性(external consistency)依靠的是 TrueTime
事務(wù)依靠全局一致性的順序 id 來保證事務(wù)執(zhí)行順序的正確性困鸥,Spanner 使用的是時間戳嗅蔬,而就算使用原子時鐘,時間還是會有亂序的存在疾就。TrueTime.now() 返回了 [earliest, latest] 間隔澜术,犧牲了少許等待時間換來更高的一致性。下面 quote 一下 Google 的文章
Thus, if two intervals do not overlap, then we know calls were definitely ordered in real time. If the intervals overlap, we do not know the actual order.
Schema Design
不同的數(shù)據(jù)庫間做數(shù)據(jù)遷移猬腰,都要理解兩個數(shù)據(jù)庫各自的優(yōu)點鸟废,才能更好的用上新數(shù)據(jù)庫。例如從 MySQL 遷移到 HBase姑荷,如果表設(shè)計一模一樣的話盒延,簡直就是浪費了 Hbase 的特性缩擂,而且因為 Hbase 沒有二級索引(雖然可以簡單設(shè)計一下)可能業(yè)務(wù)性能會下降。
先簡單說一下 Spanner 的存儲架構(gòu)
Spanner 的數(shù)據(jù)都是存放在節(jié)點機器添寺,下面簡稱 node胯盯,而 node 存放分片(split),數(shù)據(jù)在 split 的訪問順序是有序的计露,參考過論文博脑,數(shù)據(jù)在 split 中使用 KV 結(jié)構(gòu)存儲。node 之間的數(shù)據(jù)也是有序的票罐,可以這樣理解:node1{spli1, split2}, node2{split3}, node3{split4, split5, slit 6}
叉趣。
第一個問題,順序?qū)懭?HotSpot
數(shù)據(jù)主鍵如果是有序的胶坠,例如用自增 id 或者 MongoDB 的 ObjectId君账,新數(shù)據(jù)很大可能會寫入到第一個或者最后一個split,造成單個 node 的負載超高沈善,而其他 node 很空閑乡数,這樣對于寫性能不能做到增加 node 就增加性能。
Spanner 產(chǎn)品文檔給出了幾種解決辦法:
1. 調(diào)換主鍵順序
因為 spanner 是可以定義多個列來組合稱主鍵闻牡,登錄日志表如原主鍵是 (time, user_id)
, 因為 time 字段是自增的净赴,可以用 (user_id, time)
來做主鍵。登錄的 user_id 是無序的所以可以把數(shù)據(jù)寫入到盡量多的 split
2. 增加 shard_id 字段
通過保存原主鍵的 hash 或者只是保存 hash罩润。例如原主鍵是 (shop_id, user_id, order_id)
玖翅,可以增加 shard_id
字段 shard_id = md5(shop_id + user_id + order_id)[0:3]
,新主鍵為 (shard_id, shop_id, user_id)
割以,這里還可以考慮到只需要 shop_id 和 user_id 計算 md5金度,因為這樣的話每個用戶下的訂單都可能放在同一個 split,加快用戶端的訂單查找速度严沥。而根據(jù) shop_id 查找訂單可能是低頻的猜极,能接受的延時稍高。Spanner 文檔是使用 hash() % N 來存儲數(shù)字型 shard_id消玄,我這里只是給出另一種實現(xiàn)跟伏,不一定是最好。
3. 使用 UUID v4
新表設(shè)計可以使用 UUID翩瓜,但是如果從舊數(shù)據(jù)庫轉(zhuǎn)移過來受扳,例如 MongoDB,直接把 ObjectId 替換成 UUID 的話兔跌,一般都是新增一個主鍵然勘高,保留舊主鍵,并且為舊主鍵增加索引。但是更好的辦法應(yīng)該是使用前一種方法华望,增加 shard_id层亿。不過,具體是否更好還是要看業(yè)務(wù)立美,增加 shard_id 的好處是服務(wù)使用方還是可以直接舊 id 來訪問單條數(shù)據(jù)。
4. 按位反轉(zhuǎn)
// 隨手打的不要介意
// 不是所有語言都能很好處理 64 位整數(shù)和二進制運算, 下面用 javascript 做例子
function getNewId(oldId) {
let newId = 0;
const MAX_BITS = 51;
for (let i = 0; i < MAX_BITS; i++) {
newId *= 2; // newId <<= 1;
if (oldId & 1) {
newId |= 1;
}
oldId = Math.floor(oldId / 2);
}
return newId;
}
還有一種做法做法其實跟 shard_id 差不多方灾,就是把最高位的第 2 到 N + 1 位用來保存hash建蹄,原 id 保存在低 (63 - N) 位
出現(xiàn) HotSpot 的其他因素
1. 索引
Spanner 的索引也是數(shù)據(jù)表,也就是數(shù)索引的設(shè)計也會影響寫入性能
Table 主鍵設(shè)計好了裕偿,但是因為業(yè)務(wù)需要需要增加二級索引洞慎,而二級索引的字段的數(shù)據(jù)是自增的話,索引表的新數(shù)據(jù)都會寫到同一個 split嘿棘,從而造成 HotSpot劲腿。
2. 父子關(guān)系表設(shè)計
您可以在一個數(shù)據(jù)庫中定義多個表,而且鸟妙,如果希望 Cloud Spanner 以物理方式協(xié)同定位表的行焦人,從而實現(xiàn)高效檢索,您還可以選擇定義表之間的父子關(guān)系重父。 https://cloud.google.com/spanner/docs/schema-and-data-model?hl=zh-CN
假設(shè) root table(父表) shops(shop_id, shop_name)
, 字表 shop_orders(shop_id, order_id) INTERLEAVE IN PARENT shops
花椭,這樣同一個 shop_id 下的所有訂單都會放在同一個 split,就算 shop_orders
表加入了 shard_id 都沒沒有用的房午,當(dāng)同一個店鋪高并發(fā)寫入訂單的時候矿辽,會造成存放該 shop_id
數(shù)據(jù)的 split 出現(xiàn) HotSpot。
父子表適合字表數(shù)據(jù)相對少的情況郭厌,例如一個訂單一張發(fā)票袋倔,那么發(fā)票表可以是訂單表的子表。
數(shù)據(jù)庫選型考慮
由于 Google Cloud Spanner 是 GCP 托管的服務(wù)折柠,存儲費對于新舊數(shù)據(jù)都是一致的宾娜,一個電子商務(wù)網(wǎng)站,顧客一般不會經(jīng)骋鹤撸看 1 年前的訂單碳默,這時候的訂單數(shù)據(jù)如何可以存放在成本相對小的服務(wù)器,可以大大減少云服務(wù)費用缘眶。
另外一個考慮就是嘱根,不是所有應(yīng)用都一定要用到 Spanner,Google 給出另一個選擇合適數(shù)據(jù)庫的方法:
https://cloud.google.com/storage-options/
圖片來自 Google Cloud
參考資料
- Spanner 101, https://www.youtube.com/watch?v=IfsTINNCooY
- Spanner 201, https://www.youtube.com/watch?v=Tzhe7sUNDbg
- What DBAs need to know about Cloud Spanner, part 1: Keys and indexes, https://cloud.google.com/blog/products/gcp/what-dbas-need-to-know-about-cloud-spanner-part-1-keys-and-indexes
- Sharding of timestamp-ordered data in Cloud Spanner, https://chinagdg.org/2018/05/sharding-of-timestamp-ordered-data-in-cloud-spanner/
- Optimizing Applications, Schemas, and Query Design on Cloud Spanner (Cloud Next '18), https://www.youtube.com/watch?v=DxrdatA_ULk
- Cloud Spanner Documentation, https://cloud.google.com/spanner/docs/
- Spanner 論文, https://www.usenix.org/system/files/conference/osdi12/osdi12-final-16.pdf
- Spanner 論文視頻, https://www.usenix.org/conference/osdi12/technical-sessions/presentation/corbett
- Spanner, TrueTime & The CAP Theorem, https://storage.googleapis.com/pub-tools-public-publication-data/pdf/45855.pdf
- Spanner 白皮書集合, https://cloud.google.com/spanner/docs/whitepapers