1. 分庫分表后如何設(shè)計索引?全局索引、二級索引

        共 5664字,需瀏覽 12分鐘

         ·

        2022-09-08 11:48

        大家好,我是小富~

        文末送書了?。。。ㄟ@本書挺貴)

        分布式數(shù)據(jù)庫架構(gòu)下,索引的設(shè)計也需要做調(diào)整,否則無法充分發(fā)揮分布式架構(gòu)線性可擴展的優(yōu)勢。今天我們就來聊聊 “在分布式數(shù)據(jù)庫架構(gòu)下,如何正確的設(shè)計索引?”

        主鍵選擇

        對主鍵來說,要保證在所有分片中都唯一,它本質(zhì)上就是一個全局唯一的索引。如果用大部分同學(xué)喜歡的自增作為主鍵,就會發(fā)現(xiàn)存在很大的問題。

        因為自增并不能在插入前就獲得值,而是要通過填 NULL 值,然后再通過函數(shù) last_insert_id()獲得自增的值。所以,如果在每個分片上通過自增去實現(xiàn)主鍵,可能會出現(xiàn)同樣的自增值存在于不同的分片上。

        比如,對于電商的訂單表 orders,其表結(jié)構(gòu)如下(分片鍵是o_custkey,表的主鍵是o_orderkey):

              CREATE?TABLE?`orders`?(
        ??`O_ORDERKEY`?int?NOT?NULL?auto_increment,
        ??`O_CUSTKEY`?int?NOT?NULL,
        ??`O_ORDERSTATUS`?char(1)?NOT?NULL,
        ??`O_TOTALPRICE`?decimal(15,2)?NOT?NULL,
        ??`O_ORDERDATE`?date?NOT?NULL,
        ??`O_ORDERPRIORITY`?char(15)?NOT?NULL,
        ??`O_CLERK`?char(15)?NOT?NULL,
        ??`O_SHIPPRIORITY`?int?NOT?NULL,
        ??`O_COMMENT`?varchar(79)?NOT?NULL,
        ??PRIMARY?KEY?(`O_ORDERKEY`),
        ??KEY?(`O_CUSTKEY`)
        ??......
        )?ENGINE=InnoDB

        如果把 o_orderkey 設(shè)計成上圖所示的自增,那么很可能 o_orderkey 同為 1 的記錄在不同的分片出現(xiàn),如下圖所示:

        6de3cee52534b75568af66f86db5e101.webp

        所以,在分布式數(shù)據(jù)庫架構(gòu)下,盡量不要用自增作為表的主鍵:自增性能很差、安全性不高、不適用于分布式架構(gòu)。

        講到這兒,我們已經(jīng)說明白了“自增主鍵”的所有問題,那么該如何設(shè)計主鍵呢?依然還是用全局唯一的鍵作為主鍵,比如 MySQL 自動生成的有序 UUID;業(yè)務(wù)生成的全局唯一鍵(比如發(fā)號器);或者是開源的 UUID 生成算法,比如雪花算法(但是存在時間回溯的問題)。

        總之,用有序的全局唯一替代自增,是這個時代數(shù)據(jù)庫主鍵的主流設(shè)計標(biāo)準(zhǔn),如果你還停留在用自增做主鍵,或許代表你已經(jīng)落后于時代發(fā)展了。

        索引設(shè)計

        通過分片鍵可以把 SQL 查詢路由到指定的分片,但是在現(xiàn)實的生產(chǎn)環(huán)境中,業(yè)務(wù)還要通過其他的索引訪問表。

        還是以前面的表 orders 為例,如果業(yè)務(wù)還要根據(jù) o_orderkey 字段進行查詢,比如查詢訂單 ID 為 1 的訂單詳情:

              SELECT?*?FROM?orders?WHERE?o_orderkey?=?1

        我們可以看到,由于分片規(guī)則不是分片鍵,所以需要查詢 4 個分片才能得到最終的結(jié)果,如果下面有 1000 個分片,那么就需要執(zhí)行 1000 次這樣的 SQL,這時性能就比較差了。

        但是,我們知道 o_orderkey 是主鍵,應(yīng)該只有一條返回記錄,也就是說,o_orderkey 只存在于一個分片中。這時,可以有以下兩種設(shè)計:

        • 同一份數(shù)據(jù),表 orders 根據(jù) o_orderkey 為分片鍵,再做一個分庫分表的實現(xiàn);

        • 在索引中額外添加分片鍵的信息。

        這兩種設(shè)計的本質(zhì)都是通過冗余實現(xiàn)空間換時間的效果,否則就需要掃描所有的分片,當(dāng)分片數(shù)據(jù)非常多,效率就會變得極差。

        而第一種做法通過對表進行冗余,對于 o_orderkey 的查詢,只需要在 o_orderkey = 1 的分片中直接查詢就行,效率最高,但是設(shè)計的缺點又在于冗余數(shù)據(jù)量太大。

        所以,改進的做法之一是實現(xiàn)一個索引表,表中只包含 o_orderkey 和分片鍵 o_custkey,如:

              CREATE?TABLE?idx_orderkey_custkey?(
        ??o_orderkey?INT
        ??o_custkey?INT,
        ??PRIMARY?KEY?(o_orderkey)
        )

        如果這張索引表很大,也可以將其分庫分表,但是它的分片鍵是 o_orderkey,如果這時再根據(jù)字段 o_orderkey 進行查詢,可以進行類似二級索引的回表實現(xiàn):先通過查詢索引表得到記錄 o_orderkey = 1 對應(yīng)的分片鍵 o_custkey 的值,接著再根據(jù) o_custkey 進行查詢,最終定位到想要的數(shù)據(jù),如:

              SELECT?*?FROM?orders?WHERE?o_orderkey?=?1

        =>

        #?step?1
        SELECT?o_custkey?FROM?idx_orderkey_custkey?
        WHERE?o_orderkey?=?1

        #?step?2
        SELECT?*?FROM?orders?
        WHERE?o_custkey?=???AND?o_orderkey?=?1

        這個例子是將一條 SQL 語句拆分成 2 條 SQL 語句,但是拆分后的 2 條 SQL 都可以通過分片鍵進行查詢,這樣能保證只需要在單個分片中完成查詢操作。不論有多少個分片,也只需要查詢 2個分片的信息,這樣 SQL 的查詢性能可以得到極大的提升。

        通過索引表的方式,雖然存儲上較冗余全表容量小了很多,但是要根據(jù)另一個分片鍵進行數(shù)據(jù)的存儲,依然顯得不夠優(yōu)雅。

        因此,最優(yōu)的設(shè)計,不是創(chuàng)建一個索引表,而是將分片鍵的信息保存在想要查詢的列中,這樣通過查詢的列就能直接知道所在的分片信息。

        如果我們將訂單表 orders 的主鍵設(shè)計為一個字符串,這個字符串中最后一部分包含分片鍵的信息,如:

              o_orderkey?=?string(o_orderkey?+?o_custkey)

        那么這時如果根據(jù) o_orderkey 進行查詢:

              SELECT?*?FROM?Orders
        WHERE?o_orderkey?=?'1000-1';

        由于字段 o_orderkey 的設(shè)計中直接包含了分片鍵信息,所以我們可以直接知道這個訂單在分片1 中,直接查詢分片 1 就行。

        同樣地,在插入時,由于可以知道插入時 o_custkey 對應(yīng)的值,所以只要在業(yè)務(wù)層做一次字符的拼接,然后再插入數(shù)據(jù)庫就行了。

        這樣的實現(xiàn)方式較冗余表和索引表的設(shè)計來說,效率更高,查詢可以提前知道數(shù)據(jù)對應(yīng)的分片信息,只需 1 次查詢就能獲取想要的結(jié)果。

        這樣實現(xiàn)的缺點是,主鍵值會變大一些,存儲也會相應(yīng)變大。但只要主鍵值是有序的,插入的性能就不會變差。而通過在主鍵值中保存分片信息,卻可以大大提升后續(xù)的查詢效率,這樣空間換時間的設(shè)計,總體上看是非常值得的。

        當(dāng)然,這里我們談的設(shè)計都是針對于唯一索引的設(shè)計,如果是非唯一的二級索引查詢,那么非??上?,依然需要掃描所有的分片才能得到最終的結(jié)果,如:

              SELECT?*?FROM?Orders
        WHERE?o_orderate?>=???o_orderdate?<??

        因此,再次提醒你,分布式數(shù)據(jù)庫架構(gòu)設(shè)計的要求是業(yè)務(wù)的絕大部分請求能夠根據(jù)分片鍵定位到 1 個分片上。

        如果業(yè)務(wù)大部分請求都需要掃描所有分片信息才能獲得最終結(jié)果,那么就不適合進行分布式架構(gòu)的改造或設(shè)計。

        最后,我們再來回顧下淘寶用戶訂單表的設(shè)計:

        73fc031e311c0697351be5a577313dbd.webp

        上圖是我的淘寶訂單信息,可以看到,訂單號的最后 6 位都是 308113,所以可以大概率推測出:

        • 淘寶訂單表的分片鍵是用戶 ID;

        • 淘寶訂單表,訂單表的主鍵包含用戶 ID,也就是分片信息。這樣通過訂單號進行查詢,可以獲得分片信息,從而查詢 1 個分片就能得到最終的結(jié)果。

        全局表

        在分布式數(shù)據(jù)庫中,有時會有一些無法提供分片鍵的表,但這些表又非常小,一般用于保存一些全局信息,平時更新也較少,絕大多數(shù)場景僅用于查詢操作。

        例如 tpch 庫中的表 nation,用于存儲國家信息,但是在我們前面的 SQL 關(guān)聯(lián)查詢中,又經(jīng)常會使用到這張表,對于這種全局表,可以在每個分片中存儲,這樣就不用跨分片地進行查詢了。如下面的設(shè)計:

        16e1f672460487d8b20e8fb049353c51.webp

        唯一索引

        最后我們來談?wù)勎ㄒ凰饕脑O(shè)計,與主鍵一樣,如果只是通過數(shù)據(jù)庫表本身唯一約束創(chuàng)建的索引,則無法保證在所有分片中都是唯一的。

        所以,在分布式數(shù)據(jù)庫中,唯一索引一樣要通過類似主鍵的 UUID 的機制實現(xiàn),用全局唯一去替代局部唯一,但實際上,即便是單機的 MySQL 數(shù)據(jù)庫架構(gòu),我們也推薦使用全局唯一的設(shè)計。因為你不知道,什么時候,你的業(yè)務(wù)就會升級到全局唯一的要求了。

        總結(jié)

        今天介紹了非常重要的分布式數(shù)據(jù)庫索引設(shè)計,內(nèi)容非常干貨,是分布式架構(gòu)設(shè)計的重中之重,建議反復(fù)閱讀,抓住本文的重點,總結(jié)來說:

        • 分布式數(shù)據(jù)庫主鍵設(shè)計使用有序 UUID,全局唯一;

        • 分布式數(shù)據(jù)庫唯一索引設(shè)計使用 UUID 的全局唯一設(shè)計,避免局部索引導(dǎo)致的唯一問題;

        • 分布式數(shù)據(jù)庫唯一索引若不是分片鍵,則可以在設(shè)計時保存分片信息,這樣查詢直接路由到一個分片即可;

        • 對于分布式數(shù)據(jù)庫中的全局表,可以采用冗余機制,在每個分片上進行保存。這樣能避免查詢時跨分片的查詢。

        文末送書

        為了感謝一路支持小富的小伙們,今天特地給大家一點小福利。規(guī)則非常簡單:在本文留言,按點贊數(shù)量排名,點贊數(shù)量最多的前3位,每人獲取1本書。(你可以發(fā)朋友圈集贊,或者發(fā)微信群集贊。但如果發(fā)現(xiàn)有人用機器刷點贊數(shù),立即取消資格,并且拉黑

        后面我會朋友圈公布中獎名單(記得提前加我好友,提前加我好友,提前加我好友要不然領(lǐng)不到書),給你免費包郵到家!這些書是由電子工業(yè)出版社提供的,感謝贊助。

        f7f01db02fd637464afe4dacfeaa8d83.webp

        深入理解Kafka與Pulsar:消息流平臺的實踐與剖析

        梁國斌?著

        本書詳細介紹了Kafka與Pulsar的使用方式,并深入分析了它們的實現(xiàn)機制。通過閱讀本書,讀者可以快速入門和使用Kafka與Pulsar,并深入理解它們的實現(xiàn)原理。

        本書通過大量實踐示例介紹了Kafka與Pulsar的使用方式,包括管理腳本與客戶端(生產(chǎn)者、消費者)的使用方式、關(guān)鍵的配置項、ACK提交方式等基礎(chǔ)應(yīng)用,以及安全機制、跨地域復(fù)制機制、連接器/流計算引擎、常用監(jiān)控管理平臺等高級應(yīng)用。這些內(nèi)容可以幫助讀者深入掌握Kafka與Pulsar的使用方式,并完成日常管理工作。另外,本書深入分析了Kafka與Pulsar的實現(xiàn)原理,包括客戶端(生產(chǎn)者、消費者)的設(shè)計與實現(xiàn)、Broker網(wǎng)絡(luò)模型、主題(分區(qū))分配與負載均衡機制,以及磁盤存儲與性能優(yōu)化方案、數(shù)據(jù)同步機制、擴容與故障轉(zhuǎn)移機制。最后,本書介紹了Kafka與Pulsar的事務(wù)機制,并深入分析了Kafka事務(wù)的實現(xiàn)及Kafka的分布式協(xié)作組件KRaft模塊。這部分內(nèi)容可以幫助讀者輕松理解Kafka與Pulsar的架構(gòu)設(shè)計與實現(xiàn)原理。


        在看 、 點贊 、 轉(zhuǎn)發(fā) ,是對我最大的鼓勵 。


        整理了些技術(shù)書籍,有需要的同學(xué)公眾號內(nèi)回復(fù)[? pdf ?]自取。


        面試筆記、springcloud進階實戰(zhàn)PDF,公眾號內(nèi)回復(fù)[? 1222 ?]自取。


        技術(shù)群快滿了 ,想進的同學(xué)可以加我好友,和大佬們一起吹吹技術(shù)。


        ? ? ? ?
        瀏覽 91
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 国产69一区二区三区 | 51国产视频 | 伊人蕉久 | 91视频下载污 | 囯产精品久久久久久久 |