深入解析分庫分錶:原理、主鍵產生、分頁查詢、分散式事務及高可用實踐

1. 引言

在大規模互聯網應用中,隨著資料量的不斷增長,單庫單表的架構無法滿足高並發和大數據儲存的需求。分庫分錶是一種常見的資料庫架構最佳化方案,它可以提高資料庫的吞吐量、減少單表資料量並提升查詢效率。然而,分庫分錶也帶來了許多複雜的問題,例如主鍵產生、分頁查詢、分散式事務、跨表查詢、高可用性等。

本文將詳細講解分庫分錶的核心概念、關鍵技術及其實作方式,並結合範例程式碼,幫助開發者理解如何在實際專案中應用分庫分錶。


2. 分庫分錶概述

2.1 什麼是分庫分錶?

分庫分錶是將原本儲存在一個資料庫表中的資料分割到多個資料庫或多個資料表中的技術,主要有以下兩種方式:

  1. 水平分庫分錶(Sharding):按照某個欄位(如 user_id)的範圍或哈希值,將資料拆分到不同的資料庫實例和表中。例如:
    • 使用者ID 1~1000 存在 db1.user_table_11001~2000 存在 db1.user_table_2
    • 使用者ID 2001~3000 存在 db2.user_table_13001~4000 存在 db2.user_table_2
  2. 垂直分庫分錶(Vertical Partitioning):按照業務模組拆分,如 使用者表訂單表 儲存在不同的資料庫中,適用於不同業務系統的資料隔離。

2.2 為什麼要使用分庫分錶?

  • 解決單庫效能瓶頸:單一資料庫無法承載大量並發請求,拆分資料庫可以均衡流量壓力。
  • 提高讀寫效率:減少單表的資料量,提高查詢和插入速度。
  • 降低儲存成本:可以使用多個較低配置的資料庫執行個體來取代高配資料庫。
  • 提升系統高可用性:多個資料庫實例可以相互獨立,避免單點故障。

3. 分庫分錶主鍵生成

在分庫分錶後,全域唯一的主鍵產生成為一個關鍵問題。常見的主鍵產生方式包括:

  1. UUID:全域唯一,但長度較長,儲存開銷大,不利於索引查詢。
  2. 資料庫自增ID:單機可用,但無法跨資料庫唯一。
  3. Snowflake(雪花演算法)

    • Twitter 提出的分散式ID 產生演算法,基於時間戳記+機器ID+序號
    • Go 實作範例:

      package main
      
      import (
       "fmt"
       "sync"
       "time"
      )
      
      const (
       workerIDBits  = 5
       datacenterIDBits = 5
       sequenceBits  = 12
      
       maxWorkerID  = -1 ^ (-1 << workerIDBits)
       maxDatacenterID = -1 ^ (-1 << datacenterIDBits)
       maxSequence  = -1 ^ (-1 << sequenceBits)
      )
      
      type Snowflake struct {
       mu           sync.Mutex
       lastTimestamp int64
       workerID     int64
       datacenterID int64
       sequence     int64
      }
      
      func NewSnowflake(workerID, datacenterID int64) *Snowflake {
       return &Snowflake{
           workerID:     workerID,
           datacenterID: datacenterID,
       }
      }
      
      func (s *Snowflake) NextID() int64 {
       s.mu.Lock()
       defer s.mu.Unlock()
      
       timestamp := time.Now().UnixNano() / 1e6
       if timestamp == s.lastTimestamp {
           s.sequence = (s.sequence + 1) & maxSequence
           if s.sequence == 0 {
               for timestamp <= s.lastTimestamp {
                   timestamp = time.Now().UnixNano() / 1e6
               }
           }
       } else {
           s.sequence = 0
       }
       s.lastTimestamp = timestamp
       return ((timestamp << (workerIDBits + datacenterIDBits + sequenceBits)) |
           (s.datacenterID << (workerIDBits + sequenceBits)) |
           (s.workerID << sequenceBits) |
           s.sequence)
      }
      
      func main() {
       sf := NewSnowflake(1, 1)
       fmt.Println(sf.NextID())
      }
  4. 資料庫分段號段方式
    • 預分配ID 段,如 db1 產生 1~10000db2 產生 10001~20000,適用於少量資料庫的情況。

4. 分庫分錶分頁查詢

分頁查詢在分庫分錶後變得複雜,因為資料分散在多個表和庫中,常見的解決方案:

  1. 全庫查詢+合併排序
    SELECT * FROM (
       SELECT id, name, create_time FROM db1.user_table_1
       UNION ALL
       SELECT id, name, create_time FROM db1.user_table_2
    ) AS temp
    ORDER BY create_time DESC
    LIMIT 10 OFFSET 0;
  2. 利用快取:先查詢主鍵ID,再分庫查詢詳細數據,減少跨庫查詢的開銷。

5. 分庫分錶後的分散式事務

分散式事務的核心問題是多個資料庫執行個體之間的交易一致性,常見解決方案:

  1. TCC(Try-Confirm-Cancel)模式
  2. 本地訊息表+可靠訊息佇列
  3. Seata 分散式事務框架

範例:使用 golangTP + gorm 解決分散式事務問題:

func createOrder(db1, db2 *gorm.DB) error {
    return db1.Transaction(func(tx *gorm.DB) error {
        if err := tx.Exec("UPDATE users SET balance = balance - 100 WHERE id = ?", 1).Error; err != nil {
            return err
        }
        if err := db2.Exec("INSERT INTO orders (user_id, amount) VALUES (?, ?)", 1, 100).Error; err != nil {
            return err
        }
        return nil
    })
}

9. 總結

本文詳細介紹了分庫分錶的原理、主鍵產生、分頁查詢、分散式事務、無分錶鍵查詢、容量預估及高可用方案,並結合程式碼範例說明如何解決關鍵問題。

🔗 參考資料:

暫無評論

發送評論 編輯評論

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ°Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
顏文字
Emoji
小恐龍
花!
上一篇