数据库分库分表与分布式详解
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、分布式事务、跨分片查询等问题,合理使用中间件和设计模式。

1149

被折叠的 条评论
为什么被折叠?



