<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          分庫(kù)分表后如何設(shè)計(jì)索引?全局索引、二級(jí)索引

          共 5932字,需瀏覽 12分鐘

           ·

          2023-08-16 15:06

          胖虎和朋友原創(chuàng)的視頻教程有興趣的可以看看


          (文末附課程大綱)


          ??2023 最新,Java成神之路,架構(gòu)視頻(點(diǎn)擊查看)


          ??超全技術(shù)棧的Java入門(mén)+進(jìn)階+實(shí)戰(zhàn)!(點(diǎn)擊查看)

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

          主鍵選擇

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

          因?yàn)樽栽霾⒉荒茉诓迦肭熬瞳@得值,而是要通過(guò)填 NULL 值,然后再通過(guò)函數(shù) last_insert_id()獲得自增的值。所以,如果在每個(gè)分片上通過(guò)自增去實(shí)現(xiàn)主鍵,可能會(huì)出現(xiàn)同樣的自增值存在于不同的分片上。

          比如,對(duì)于電商的訂單表 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(1NOT NULL,
            `O_TOTALPRICE` decimal(15,2NOT NULL,
            `O_ORDERDATE` date NOT NULL,
            `O_ORDERPRIORITY` char(15NOT NULL,
            `O_CLERK` char(15NOT NULL,
            `O_SHIPPRIORITY` int NOT NULL,
            `O_COMMENT` varchar(79NOT NULL,
            PRIMARY KEY (`O_ORDERKEY`),
            KEY (`O_CUSTKEY`)
            ......
          ENGINE=InnoDB

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

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

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

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

          索引設(shè)計(jì)

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

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

          SELECT * FROM orders WHERE o_orderkey = 1

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

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

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

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

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

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

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

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

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

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

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

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

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

          o_orderkey = string(o_orderkey + o_custkey)

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

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

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

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

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

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

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

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

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

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

          最后,我們?cè)賮?lái)回顧下淘寶用戶訂單表的設(shè)計(jì):

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

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

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

          全局表

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

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


          唯一索引

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

          所以,在分布式數(shù)據(jù)庫(kù)中,唯一索引一樣要通過(guò)類似主鍵的 UUID 的機(jī)制實(shí)現(xiàn),用全局唯一去替代局部唯一,但實(shí)際上,即便是單機(jī)的 MySQL 數(shù)據(jù)庫(kù)架構(gòu),我們也推薦使用全局唯一的設(shè)計(jì)。因?yàn)槟悴恢溃裁磿r(shí)候,你的業(yè)務(wù)就會(huì)升級(jí)到全局唯一的要求了。

          總結(jié)

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

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

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

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

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

          來(lái)源:程序員小富
                

          胖虎聯(lián)合兩位大佬朋友,一位是知名培訓(xùn)機(jī)構(gòu)講師和科大訊飛架構(gòu),聯(lián)合打造了《Java架構(gòu)師成長(zhǎng)之路》的視頻教程。完全對(duì)標(biāo)外面2萬(wàn)左右的培訓(xùn)課程。

          除了基本的視頻教程之外,還提供了超詳細(xì)的課堂筆記,以及源碼等資料包..


          課程階段:

          1. Java核心 提升閱讀源碼的內(nèi)功心法
          2. 深入講解企業(yè)開(kāi)發(fā)必備技術(shù)棧,夯實(shí)基礎(chǔ),為跳槽加薪增加籌碼
          3. 分布式架構(gòu)設(shè)計(jì)方法論。為學(xué)習(xí)分布式微服務(wù)做鋪墊
          4. 學(xué)習(xí)NetFilx公司產(chǎn)品,如Eureka、Hystrix、Zuul、Feign、Ribbon等,以及學(xué)習(xí)Spring Cloud Alibabba體系
          5. 微服務(wù)架構(gòu)下的性能優(yōu)化
          6. 中間件源碼剖析
          7. 元原生以及虛擬化技術(shù)
          8. 從0開(kāi)始,項(xiàng)目實(shí)戰(zhàn) SpringCloud Alibaba電商項(xiàng)目

          點(diǎn)擊下方超鏈接查看詳情(或者點(diǎn)擊文末閱讀原文):

          (點(diǎn)擊查看)  2023年,最新Java架構(gòu)師成長(zhǎng)之路 視頻教程!

          以下是課程大綱,大家可以雙擊打開(kāi)原圖查看


          瀏覽 1011
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  男女大香蕉| 欧美丁香五月天 | 国产精品日韩在线 | 夜夜干夜夜操 | 小h片免费 |