Google Cloud Spanner 學(xué)習(xí)報(bào)告

Google Cloud Spanner 學(xué)習(xí)報(bào)告

  • 17 Jan 2019

本人介紹,未使用過(guò) Google Cloud Spanner,不是 DBA。

簡(jiǎn)介

Google 的 AdWords 曾經(jīng)是靠 MySQL+手工Sharding來(lái)支撐,但在擴(kuò)展和可靠性上不能滿足要求。于是開發(fā)了全局一致性和自動(dòng) Sharding 的數(shù)據(jù)庫(kù) Spanner。

Spanner 帶動(dòng)了一波云原生數(shù)據(jù)庫(kù)的熱潮,云原生數(shù)據(jù)庫(kù)的特點(diǎn)是,計(jì)算和存儲(chǔ)分離,增加 node 就可以增加存儲(chǔ)能力和性能;自動(dòng)分片;支持分布式事務(wù)。

國(guó)內(nèi)的云原生數(shù)據(jù)有阿里云的 云數(shù)據(jù)庫(kù)POLARDB,騰訊云的
云數(shù)據(jù)庫(kù) CynosDB,這兩個(gè)數(shù)據(jù)庫(kù)的特點(diǎn)還有完全兼容 MySQL,其中 CynosDB 還可以支持 PostgreSQL 10(參考時(shí)間 2019年1月17日),現(xiàn)有應(yīng)用無(wú)需更改 SDK,減少數(shù)據(jù)庫(kù)遷移的成本。

TrueTime

Spanner 數(shù)據(jù)庫(kù)告訴了人們,當(dāng)數(shù)據(jù)容量增加,分片數(shù)量增加,依然可以享受高性能和事務(wù)全局一致性,依靠的最驚艷的技術(shù)就是 TrueTime。

注意,Spanner 的可序列化(serializability)依靠的還是 Lock,但是外部一致性(external consistency)依靠的是 TrueTime

事務(wù)依靠全局一致性的順序 id 來(lái)保證事務(wù)執(zhí)行順序的正確性,Spanner 使用的是時(shí)間戳,而就算使用原子時(shí)鐘,時(shí)間還是會(huì)有亂序的存在。TrueTime.now() 返回了 [earliest, latest] 間隔,犧牲了少許等待時(shí)間換來(lái)更高的一致性。下面 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ù)庫(kù)間做數(shù)據(jù)遷移,都要理解兩個(gè)數(shù)據(jù)庫(kù)各自的優(yōu)點(diǎn),才能更好的用上新數(shù)據(jù)庫(kù)。例如從 MySQL 遷移到 HBase,如果表設(shè)計(jì)一模一樣的話,簡(jiǎn)直就是浪費(fèi)了 Hbase 的特性,而且因?yàn)?Hbase 沒有二級(jí)索引(雖然可以簡(jiǎn)單設(shè)計(jì)一下)可能業(yè)務(wù)性能會(huì)下降。

先簡(jiǎn)單說(shuō)一下 Spanner 的存儲(chǔ)架構(gòu)

Spanner 的數(shù)據(jù)都是存放在節(jié)點(diǎn)機(jī)器,下面簡(jiǎn)稱 node,而 node 存放分片(split),數(shù)據(jù)在 split 的訪問順序是有序的,參考過(guò)論文,數(shù)據(jù)在 split 中使用 KV 結(jié)構(gòu)存儲(chǔ)。node 之間的數(shù)據(jù)也是有序的,可以這樣理解:node1{spli1, split2}, node2{split3}, node3{split4, split5, slit 6}。

第一個(gè)問題,順序?qū)懭?HotSpot

數(shù)據(jù)主鍵如果是有序的,例如用自增 id 或者 MongoDB 的 ObjectId,新數(shù)據(jù)很大可能會(huì)寫入到第一個(gè)或者最后一個(gè)split,造成單個(gè) node 的負(fù)載超高,而其他 node 很空閑,這樣對(duì)于寫性能不能做到增加 node 就增加性能。

Spanner 產(chǎn)品文檔給出了幾種解決辦法:

1. 調(diào)換主鍵順序

因?yàn)?spanner 是可以定義多個(gè)列來(lái)組合稱主鍵,登錄日志表如原主鍵是 (time, user_id), 因?yàn)?time 字段是自增的,可以用 (user_id, time) 來(lái)做主鍵。登錄的 user_id 是無(wú)序的所以可以把數(shù)據(jù)寫入到盡量多的 split

2. 增加 shard_id 字段

通過(guò)保存原主鍵的 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 計(jì)算 md5,因?yàn)檫@樣的話每個(gè)用戶下的訂單都可能放在同一個(gè) split,加快用戶端的訂單查找速度。而根據(jù) shop_id 查找訂單可能是低頻的,能接受的延時(shí)稍高。Spanner 文檔是使用 hash() % N 來(lái)存儲(chǔ)數(shù)字型 shard_id,我這里只是給出另一種實(shí)現(xiàn),不一定是最好。

3. 使用 UUID v4

新表設(shè)計(jì)可以使用 UUID,但是如果從舊數(shù)據(jù)庫(kù)轉(zhuǎn)移過(guò)來(lái),例如 MongoDB,直接把 ObjectId 替換成 UUID 的話,一般都是新增一個(gè)主鍵然,保留舊主鍵,并且為舊主鍵增加索引。但是更好的辦法應(yīng)該是使用前一種方法,增加 shard_id。不過(guò),具體是否更好還是要看業(yè)務(wù),增加 shard_id 的好處是服務(wù)使用方還是可以直接舊 id 來(lái)訪問單條數(shù)據(jù)。

4. 按位反轉(zhuǎn)

// 隨手打的不要介意 
// 不是所有語(yǔ)言都能很好處理 64 位整數(shù)和二進(jìn)制運(yùn)算, 下面用 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;
}

還有一種做法做法其實(shí)跟 shard_id 差不多,就是把最高位的第 2 到 N + 1 位用來(lái)保存hash,原 id 保存在低 (63 - N) 位

出現(xiàn) HotSpot 的其他因素

1. 索引

Spanner 的索引也是數(shù)據(jù)表,也就是數(shù)索引的設(shè)計(jì)也會(huì)影響寫入性能

Table 主鍵設(shè)計(jì)好了,但是因?yàn)闃I(yè)務(wù)需要需要增加二級(jí)索引,而二級(jí)索引的字段的數(shù)據(jù)是自增的話,索引表的新數(shù)據(jù)都會(huì)寫到同一個(gè) split,從而造成 HotSpot。

2. 父子關(guān)系表設(shè)計(jì)

您可以在一個(gè)數(shù)據(jù)庫(kù)中定義多個(gè)表,而且,如果希望 Cloud Spanner 以物理方式協(xié)同定位表的行,從而實(shí)現(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,這樣同一個(gè) shop_id 下的所有訂單都會(huì)放在同一個(gè) split,就算 shop_orders 表加入了 shard_id 都沒沒有用的,當(dāng)同一個(gè)店鋪高并發(fā)寫入訂單的時(shí)候,會(huì)造成存放該 shop_id 數(shù)據(jù)的 split 出現(xiàn) HotSpot。

父子表適合字表數(shù)據(jù)相對(duì)少的情況,例如一個(gè)訂單一張發(fā)票,那么發(fā)票表可以是訂單表的子表。

數(shù)據(jù)庫(kù)選型考慮

由于 Google Cloud Spanner 是 GCP 托管的服務(wù),存儲(chǔ)費(fèi)對(duì)于新舊數(shù)據(jù)都是一致的,一個(gè)電子商務(wù)網(wǎng)站,顧客一般不會(huì)經(jīng)???1 年前的訂單,這時(shí)候的訂單數(shù)據(jù)如何可以存放在成本相對(duì)小的服務(wù)器,可以大大減少云服務(wù)費(fèi)用。

另外一個(gè)考慮就是,不是所有應(yīng)用都一定要用到 Spanner,Google 給出另一個(gè)選擇合適數(shù)據(jù)庫(kù)的方法:

https://cloud.google.com/storage-options/

image.png

圖片來(lái)自 Google Cloud

參考資料

閱讀原文

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 32,311評(píng)論 2 89
  • 緣起 最近研究Spanner,發(fā)現(xiàn)國(guó)內(nèi)對(duì)Spanner論文的翻譯很多,但是美中不足的是,每個(gè)人都在做論文的搬運(yùn)工和...
    呂信閱讀 20,244評(píng)論 4 36
  • 本文由廈門大學(xué)計(jì)算機(jī)系教師林子雨翻譯,翻譯質(zhì)量很高,本人只對(duì)極少數(shù)翻譯得不太恰當(dāng)?shù)牡胤竭M(jìn)行了修改。 【摘要】:Sp...
    Jeffbond閱讀 4,093評(píng)論 1 42
  • 上一篇說(shuō)到了Google Spanner--全球級(jí)分布式數(shù)據(jù)庫(kù),但我覺得Spanner不能稱作NewSQL,因?yàn)镾...
    GvVvos閱讀 6,113評(píng)論 0 10

友情鏈接更多精彩內(nèi)容