数据库分库分表与分布式详解

数据库分库分表与分布式详解

1. 分库分表概述

当单机数据库无法满足业务增长需求时,需要进行分库分表,将数据分散到多个数据库或表中。

2. 垂直拆分

2.1 垂直分库

按业务模块拆分:

-- 用户库
CREATE DATABASE user_db;
CREATE TABLE user_db.users (...);

-- 订单库
CREATE DATABASE order_db;
CREATE TABLE order_db.orders (...);

2.2 垂直分表

按字段拆分:

-- 主表
CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    user_id BIGINT,
    total_amount DECIMAL(10,2),
    status VARCHAR(20)
);

-- 扩展表
CREATE TABLE orders_ext (
    id BIGINT PRIMARY KEY,
    order_id BIGINT,
    shipping_address TEXT,
    billing_info TEXT
);

3. 水平拆分

3.1 按ID范围分表

func getTableByID(id int64) string {
    tableID := id / 1000000
    return fmt.Sprintf("orders_%d", tableID)
}

3.2 按Hash分表

func getTableByHash(userID int64, tableCount int) string {
    hash := userID % int64(tableCount)
    return fmt.Sprintf("orders_%d", hash)
}

3.3 按时间分表

func getTableByTime(t time.Time) string {
    return fmt.Sprintf("orders_%s", t.Format("2006_01"))
}

4. 分片中间件

4.1 ShardingSphere

schemaName: sharding_db

dataSources:
  ds_0:
    url: jdbc:mysql://localhost:3306/ds_0
    username: root
    password:
  ds_1:
    url: jdbc:mysql://localhost:3306/ds_1
    username: root
    password:

rules:
- !sharding
  tables:
    t_order:
      actualDataNodes: ds_${0..1}.t_order_${0..3}
      tableStrategy:
        standard:
          shardingColumn: order_id
          shardingAlgorithmName: t_order_inline
  bindingTables:
    - t_order

4.2 MyCAT配置

<schema name="test" checkSQLschema="false" sqlMaxLimit="100">
    <table name="orders" dataNode="dn1,dn2" rule="mod-long"/>
</schema>

<dataNode name="dn1" dataHost="dh1" database="db1"/>
<dataNode name="dn2" dataHost="dh2" database="db2"/>

5. 分布式ID

5.1 Snowflake算法

type Snowflake struct {
    mu        sync.Mutex
    timestamp int64
    nodeID    int64
    sequence  int64
}

func NewSnowflake(nodeID int64) *Snowflake {
    return &Snowflake{
        nodeID: nodeID,
    }
}

func (s *Snowflake) Generate() int64 {
    s.mu.Lock()
    defer s.mu.Unlock()

    now := time.Now().UnixNano() / 1000000

    if now == s.timestamp {
        s.sequence = (s.sequence + 1) & 4095
        if s.sequence == 0 {
            for now <= s.timestamp {
                now = time.Now().UnixNano() / 1000000
            }
        }
    } else {
        s.sequence = 0
    }

    s.timestamp = now

    return (now-1609459200000)<<22 |
        s.nodeID<<12 |
        s.sequence
}

5.2 UUID

import "github.com/google/uuid"

// 生成UUID
id := uuid.New().String()

6. 分布式事务

6.1 两阶段提交

type TwoPhaseCommit struct {
    participants []Participant
}

func (tx *TwoPhaseCommit) Prepare() error {
    for _, p := range tx.participants {
        if err := p.Prepare(); err != nil {
            tx.Rollback()
            return err
        }
    }
    return nil
}

func (tx *TwoPhaseCommit) Commit() error {
    for _, p := range tx.participants {
        if err := p.Commit(); err != nil {
            // 处理失败
        }
    }
    return nil
}

6.2 Saga模式

type SagaStep struct {
    Name     string
    Execute  func() error
    Compensate func() error
}

func ExecuteSaga(steps []SagaStep) error {
    completed := []SagaStep{}

    for _, step := range steps {
        if err := step.Execute(); err != nil {
            // 回滚已完成的步骤
            for i := len(completed) - 1; i >= 0; i-- {
                completed[i].Compensate()
            }
            return err
        }
        completed = append(completed, step)
    }

    return nil
}

7. 跨分片查询

7.1 禁止跨分片操作

// 错误示例:无法在应用层完成
SELECT * FROM orders WHERE user_id IN (1, 2, 3)
// 1和2可能在不同的分片

7.2 广播表

// 配置广播表,所有分片都有完整副本
// 适合字典表、配置表
rules:
- !sharding
  tables:
    t_config:
      broadcastTables:
        - t_config

7.3 ES替代方案

// 将数据同步到Elasticsearch
type ESService struct {
    client *elastic.Client
}

func (s *ESService) Search(query map[string]interface{}) ([]map[string]interface{}, error) {
    searchResult, err := s.client.Search().
        Index("orders").
        Query(query).
        Do(ctx)
    return searchResult.Hits.Hits, nil
}

8. 数据迁移

8.1 双写策略

func CreateOrder(order *Order) error {
    // 同时写入MySQL和ES
    if err := mysqlDB.Create(order).Error; err != nil {
        return err
    }

    if err := esClient.Index().Index("orders").BodyJson(order).Do(ctx); err != nil {
        log.Printf("ES write failed: %v", err)
    }

    return nil
}

8.2 数据同步工具

  • Canal:监听MySQL binlog同步数据
  • Debezium:支持多种数据库
  • Flink CDC:实时数据同步

9. 总结

分库分表是解决数据库扩展性的重要手段,需要根据业务特点选择合适的拆分策略。同时需要注意分布式ID、分布式事务、跨分片查询等问题,合理使用中间件和设计模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值