ORMX 分布式数据库功能,学习分片策略、读写分离和分布式事务。 关键字:ORMX 分布式,分片策略,读写分离,分布式事务
分布式数据库
什么是分布式数据库
分布式数据库是指将数据分散存储在多个物理节点上的数据库系统。通过数据分片(Sharding)和读写分离(Read-Write Separation)等技术,分布式数据库能够提供更好的性能、可扩展性和可用性。
社会主流观点
随着数据量的爆炸式增长,单体数据库已经难以满足现代应用的需求。主流观点认为,分布式数据库是解决大数据量、高并发访问的有效方案。然而,传统分布式数据库的复杂性往往让开发者望而却步。
核心观点
ORMX 的分布式功能旨在降低分布式数据库的使用门槛,通过简洁的 API 设计和智能的路由策略,让开发者能够轻松实现数据分片和读写分离,而无需深入了解底层实现细节。ORMX 在保持高性能的同时,提供了完善的故障转移和分布式事务支持,使分布式数据库的构建变得前所未有的简单。
ORMX 分布式功能概述
ORMX 提供了完整的分布式数据库解决方案,包括以下核心功能:
基本用法
ORMX 的分布式功能通过 DistributedDbProvider 类提供,使用简单直观的 API。
核心特点:
- 直接通过构造函数创建分布式提供者
- 支持自定义分片键属性
- 提供
GetTable<T>()方法获取分布式表操作对象 - 需要引用
JCode.ORMX.Distributed.Core.Providers命名空间
示例:
using JCode.ORMX.DbProvider;
using JCode.ORMX.Distributed.Core.Providers;
using JCode.ORMX.Distributed.Core.Sharding;
// 创建分片数据库列表
var shards = new List<IDbProvider>();
// 创建分片策略
var strategy = new HashShardStrategy();
// 创建分布式提供者(指定分片键属性)
var distributedProvider = new DistributedDbProvider(
shards: shards,
shardStrategy: strategy,
shardKeyProperty: "Username" // 使用 Username 属性作为分片键
);
// 获取分布式表对象
var distributedTable = distributedProvider.GetTable<User>();
重要提示:
- 分片键属性可以是实体的任意属性,默认为 "Id"
GetTable<T>()方法返回分布式表操作对象- 所有示例代码都已包含必要的 using 语句
数据分片(Sharding)
将数据按照一定规则分散到多个数据库节点,提高数据存储容量和查询性能。
using JCode.ORMX.DbProvider;
using JCode.ORMX.Distributed.Core.Providers;
using JCode.ORMX.Distributed.Core.Sharding;
using System.Collections.Generic;
// 创建分片数据库列表
var shards = new List<IDbProvider>
{
new SqliteDatabaseProvider("Data Source=shard1.db"),
new SqliteDatabaseProvider("Data Source=shard2.db"),
new SqliteDatabaseProvider("Data Source=shard3.db")
};
// 创建哈希分片策略
var strategy = new HashShardStrategy();
// 创建分布式提供者(指定分片键属性)
var distributedProvider = new DistributedDbProvider(
shards: shards,
shardStrategy: strategy,
shardKeyProperty: "UserId" // 使用 UserId 属性作为分片键
);
// 获取分布式表对象
var userTable = distributedProvider.GetTable<User>();
读写分离(Read-Write Separation)
将读操作路由到从库,写操作路由到主库,提高系统并发能力。
using JCode.ORMX.DbProvider;
using JCode.ORMX.Distributed.Core.Providers;
using JCode.ORMX.Distributed.Core.Router;
using JCode.ORMX.Distributed.Core.Sharding;
// 创建分片数据库列表(用于写操作)
var shards = new List<IDbProvider>
{
new SqliteDatabaseProvider("Data Source=shard1.db"),
new SqliteDatabaseProvider("Data Source=shard2.db"),
new SqliteDatabaseProvider("Data Source=shard3.db")
};
// 创建从库列表(用于读操作)
var slaveDbs = new List<IDbProvider>
{
new SqliteDatabaseProvider("Data Source=slave1.db"),
new SqliteDatabaseProvider("Data Source=slave2.db")
};
// 创建读写路由器
var readWriteRouter = new ReadWriteRouter(
master: shards.First(), // 主库使用第一个分片
slaves: slaveDbs
);
// 创建分布式提供者(带读写分离)
var distributedProvider = new DistributedDbProvider(
shards: shards,
shardStrategy: new HashShardStrategy(),
readWriteRouter: readWriteRouter,
shardKeyProperty: "UserId"
);
分布式事务(Distributed Transaction)
支持跨多个分片的事务操作,确保数据一致性。
using var transaction = distributedProvider.BeginDistributedTransaction();
transaction.Begin(IsolationLevel.ReadCommitted);
try
{
// 执行跨分片操作
userTable.Insert(user1); // 分片 1
userTable.Insert(user2); // 分片 2
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
}
分片策略
ORMX 提供了多种分片策略,满足不同的业务场景需求。
哈希分片(Hash Sharding)
根据分片键的哈希值将数据均匀分布到各个分片。
using JCode.ORMX.DbProvider;
using JCode.ORMX.Distributed.Core.Providers;
using JCode.ORMX.Distributed.Core.Sharding;
// 创建分片数据库列表
var shards = new List<IDbProvider>();
// 创建哈希分片策略
var strategy = new HashShardStrategy();
// 创建分布式提供者
var distributedProvider = new DistributedDbProvider(
shards: shards,
shardStrategy: strategy,
shardKeyProperty: "UserId"
);
适用场景:
- 数据分布均匀
- 查询主要基于分片键
- 需要快速定位数据
优点:
- 数据分布均匀
- 查询性能稳定
- 扩展简单
范围分片(Range Sharding)
根据分片键的范围将数据分配到不同的分片。
using JCode.ORMX.DbProvider;
using JCode.ORMX.Distributed.Core.Providers;
using JCode.ORMX.Distributed.Core.Sharding;
// 创建分片数据库列表
var shards = new List<IDbProvider>();
// 创建范围分片策略
var strategy = new RangeShardStrategy();
// 创建分布式提供者
var distributedProvider = new DistributedDbProvider(
shards: shards,
shardStrategy: strategy,
shardKeyProperty: "CreatedAt"
);
适用场景:
- 数据有时间或数值范围特征
- 经常需要范围查询
- 数据访问有明显的局部性
优点:
- 范围查询效率高
- 数据迁移方便
- 易于理解和管理
一致性哈希分片(Consistent Hash Sharding)
使用一致性哈希算法,减少分片变化时的数据迁移量。
using JCode.ORMX.DbProvider;
using JCode.ORMX.Distributed.Core.Providers;
using JCode.ORMX.Distributed.Core.Sharding;
// 创建分片数据库列表
var shards = new List<IDbProvider>();
// 创建一致性哈希分片策略(指定虚拟节点数)
var strategy = new ConsistentHashShardStrategy(virtualNodes: 150);
// 创建分布式提供者
var distributedProvider = new DistributedDbProvider(
shards: shards,
shardStrategy: strategy,
shardKeyProperty: "UserId"
);
适用场景:
- 分片数量频繁变化
- 需要最小化数据迁移
- 对数据可用性要求高
优点:
- 分片变化时数据迁移量小
- 负载均衡效果好
- 系统稳定性高
自定义分片策略
通过实现 IShardStrategy 接口,可以创建自定义的分片策略。
using JCode.ORMX.Distributed.Core.Sharding;
public class CustomShardStrategy : IShardStrategy
{
private readonly Func<object, int> _shardSelector;
public CustomShardStrategy(Func<object, int> shardSelector)
{
_shardSelector = shardSelector;
}
public int GetShardIndex(object shardKey, int shardCount)
{
return _shardSelector(shardKey) % shardCount;
}
}
// 使用自定义分片策略
var strategy = new CustomShardStrategy(key =>
{
// 自定义分片逻辑
return key.GetHashCode();
});
var distributedProvider = new DistributedDbProvider(
shards: shards,
shardStrategy: strategy,
shardKeyProperty: "UserId"
);
读写分离
读写分离是提高数据库并发能力的重要手段,ORMX 提供了简单易用的读写分离功能。
架构说明
ORMX 的读写分离采用以下架构:
分片列表(shards):
- Shard1.db ← 存储 userId % 3 == 0 的数据(写操作)
- Shard2.db ← 存储 userId % 3 == 1 的数据(写操作)
- Shard3.db ← 存储 userId % 3 == 2 的数据(写操作)
从库列表(slaveDbs):
- Slave1.db ← 用于读操作(负载均衡)
- Slave2.db ← 用于读操作(负载均衡)
- Slave3.db ← 用于读操作(负载均衡)
工作原理:
- 写操作:根据分片键路由到
shards中的对应分片 - 读操作:如果启用了读写分离,使用
slaveDbs中的从库(轮询负载均衡);否则使用shards中的分片 - 数据同步:需要外部机制将
shards中的数据同步到slaveDbs
重要说明:主从复制 vs 读写分离
ORMX 负责的是读写分离(路由决策),而不是主从复制(数据同步)!
- 主从复制(Data Replication):这是数据库层面的功能,由数据库自己实现(如 MySQL 的 binlog 复制、PostgreSQL 的流复制)。ORMX 不负责数据同步。
- 读写分离(Read-Write Splitting):这是应用层面的功能,ORMX 的
ReadWriteRouter负责智能地选择使用主库还是从库。工作流程:
应用层 (ORMX) ├─ 写操作 → GetMaster() → 主库 └─ 读操作 → GetSlave() → 从库 ↓ 数据库层 (MySQL/PostgreSQL) └─ 主库 --binlog/流复制--> 从库 (自动同步)生产环境配置:
- 使用 MySQL/PostgreSQL 等支持主从复制的数据库
- 在数据库层面配置主从复制(binlog、relay-log 等)
- ORMX 自动利用数据库的主从架构进行读写分离
测试环境说明:
- SQLite 不支持主从复制,所以测试中需要手动同步数据
- 实际生产环境中,数据同步由数据库自动完成,无需手动处理
基本配置
using JCode.ORMX.DbProvider;
using JCode.ORMX.Distributed.Core.Providers;
using JCode.ORMX.Distributed.Core.Router;
using JCode.ORMX.Distributed.Core.Sharding;
// 创建分片数据库列表(用于写操作)
var shards = new List<IDbProvider>
{
new SqliteDatabaseProvider("Data Source=shard1.db"),
new SqliteDatabaseProvider("Data Source=shard2.db"),
new SqliteDatabaseProvider("Data Source=shard3.db")
};
// 创建从库列表(用于读操作)
var slaveDbs = new List<IDbProvider>
{
new SqliteDatabaseProvider("Data Source=slave1.db"),
new SqliteDatabaseProvider("Data Source=slave2.db"),
new SqliteDatabaseProvider("Data Source=slave3.db")
};
// 创建读写路由器
var readWriteRouter = new ReadWriteRouter(
master: shards.First(), // 主库使用第一个分片
slaves: slaveDbs
);
// 创建分布式提供者(带读写分离)
var distributedProvider = new DistributedDbProvider(
shards: shards,
shardStrategy: new HashShardStrategy(),
readWriteRouter: readWriteRouter,
shardKeyProperty: "UserId"
);
重要说明:
shards参数是分片列表,用于数据分片和写操作readWriteRouter参数提供读写分离功能slaveDbs参数是从库列表,用于读操作- 需要外部机制将
shards中的数据同步到slaveDbs
读写路由
ORMX 会自动将写操作路由到主库,读操作路由到从库。
var userTable = distributedProvider.GetTable<User>();
// 写操作 - 自动路由到主库
userTable.Insert(new User { Name = "张三", Email = "zhangsan@example.com" });
// 读操作 - 自动路由到从库
var users = userTable.Where(u => u.Age > 25).GetList();
自定义读写路由
通过实现 IReadWriteRouter 接口,可以自定义读写路由策略。
public class CustomReadWriteRouter : IReadWriteRouter
{
private readonly IDbProvider _master;
private readonly List<IDbProvider> _slaves;
private int _slaveIndex;
public CustomReadWriteRouter(IDbProvider master, IEnumerable<IDbProvider> slaves, bool readWriteSeparationEnabled = true)
{
_master = master ?? throw new ArgumentNullException(nameof(master));
_slaves = slaves?.ToList() ?? new List<IDbProvider>();
_slaveIndex = 0;
}
public IDbProvider GetMaster()
{
return _master;
}
public IDbProvider GetSlave()
{
if (_slaves.Count == 0)
{
return _master;
}
// 自定义从库选择策略(例如:加权轮询)
_slaveIndex = (_slaveIndex + 1) % _slaves.Count;
return _slaves[_slaveIndex];
}
public IEnumerable<IDbProvider> GetAllSlaves()
{
return _slaves;
}
public bool IsReadWriteSeparationEnabled => _slaves.Count > 0;
}
读写分离的最佳实践
1. 理解 ORM 与数据库的职责分工
核心概念:
- ORMX(应用层):负责读写分离,即决定哪个操作使用哪个数据库连接(路由决策)
- 数据库(数据库层):负责主从复制,即保证主库和从库的数据一致性(数据同步)
ORMX 不做什么:
- ❌ 不负责将数据从主库同步到从库
- ❌ 不监控数据库的主从复制状态
- ❌ 不处理数据库层面的复制延迟
ORMX 做什么:
- ✅ 智能路由:写操作→主库,读操作→从库
- ✅ 负载均衡:轮询选择从库
- ✅ 故障转移:从库不可用时自动切换到主库
2. 数据同步
使用数据库的主从复制机制(不是 ORMX 的功能):
-- MySQL 主从复制配置示例
-- 主库配置
server-id = 1
log-bin = mysql-bin
binlog-format = ROW
-- 从库配置
server-id = 2
relay-log = mysql-relay-bin
read-only = 1
3. 从库数量
根据读操作负载调整从库数量:
// 合理配置从库数量
var slaveCount = Math.Max(2, Environment.ProcessorCount / 2);
var slaveDbs = new List<IDbProvider>();
for (int i = 0; i < slaveCount; i++)
{
slaveDbs.Add(new SqliteDatabaseProvider(<div class="latex">$"Data Source=slave{i}.db"));
}
4. 故障处理
当从库不可用时,系统会自动回退到主库:
// ORMX 会自动处理从库故障
// 如果从库不可用,读操作会自动使用主库
var users = userTable.Where(u => u.Age > 25).GetList();
分布式事务
分布式事务确保跨多个分片的操作能够原子性地执行,保证数据一致性。
基本用法
using var transaction = distributedProvider.BeginDistributedTransaction();
transaction.Begin(IsolationLevel.ReadCommitted);
try
{
var userTable = distributedProvider.GetTable<User>();
var orderTable = distributedProvider.GetTable<Order>();
// 执行跨分片操作
userTable.Insert(new User { Name = "张三", Email = "zhangsan@example.com" });
orderTable.Insert(new Order { UserId = 1, Amount = 100 });
transaction.Commit();
Console.WriteLine("事务提交成功");
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine($</div>"事务回滚:{ex.Message}");
}
事务监控
var transactionMonitor = new TransactionMonitor();
var distributedProvider = new DistributedDbProvider(
shards,
strategy,
transactionMonitor: transactionMonitor
);
// 订阅事务事件
transactionMonitor.TransactionEventOccurred += (sender, e) =>
{
Console.WriteLine(<div class="latex">$"事务事件:{e.EventType}");
Console.WriteLine($</div>"事务 ID: {e.TransactionId}");
Console.WriteLine(<div class="latex">$"新状态:{e.NewStatus}");
if (e.Error != null)
{
Console.WriteLine($</div>"错误:{e.Error.Message}");
}
};
事务状态查询
// 获取所有事务信息
var transactions = transactionMonitor.GetAllTransactions();
foreach (var transaction in transactions)
{
Console.WriteLine(<div class="latex">$"事务 ID: {transaction.Key}");
Console.WriteLine($</div>"状态:{transaction.Value.Status}");
Console.WriteLine(<div class="latex">$"开始时间:{transaction.Value.StartTime}");
Console.WriteLine($</div>"执行时长:{transaction.Value.Duration}");
Console.WriteLine(<div class="latex">$"事件数量:{transaction.Value.Events?.Count}");
}
超时事务处理
// 设置事务超时时间
var timeoutThreshold = TimeSpan.FromMinutes(5);
// 检查超时事务
transactionMonitor.CheckTimeoutTransactions(timeoutThreshold);
完整示例
下面是一个完整的分布式数据库使用示例:
using JCode.ORMX.DbProvider;
using JCode.ORMX.Distributed.Core.Providers;
using JCode.ORMX.Distributed.Core.Router;
using JCode.ORMX.Distributed.Core.Sharding;
using JCode.ORMX.Attributes;
// 定义实体类
[Table(Name = "Users")]
public class User
{
[Column(IsPrimaryKey = true, IsAutoIncrement = true)]
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
// 创建分片数据库(用于写操作)
var shards = new List<IDbProvider>
{
new SqliteDatabaseProvider("Data Source=shard1.db"),
new SqliteDatabaseProvider("Data Source=shard2.db"),
new SqliteDatabaseProvider("Data Source=shard3.db")
};
// 创建从库(用于读操作)
var slaveDbs = new List<IDbProvider>
{
new SqliteDatabaseProvider("Data Source=slave1.db"),
new SqliteDatabaseProvider("Data Source=slave2.db")
};
// 创建读写路由器
var readWriteRouter = new ReadWriteRouter(
master: shards.First(),
slaves: slaveDbs
);
// 创建分布式提供者(使用 Username 作为分片键)
var distributedProvider = new DistributedDbProvider(
shards: shards,
shardStrategy: new HashShardStrategy(),
readWriteRouter: readWriteRouter,
shardKeyProperty: "Username"
);
// 获取表对象
var userTable = distributedProvider.GetTable<User>();
// 在每个分片上创建表
foreach (var shard in shards)
{
var tableManager = shard.GetTableManager();
tableManager.Create(typeof(User));
}
// 插入数据(自动路由到对应分片)
for (int i = 1; i <= 100; i++)
{
userTable.Insert(new User
{
Username = $</div>"用户{i}",
Email = <div class="latex">$"user{i}@example.com",
Age = 20 + (i % 30)
});
}
// 查询数据
var users = userTable.GetList(u => u.Age > 25);
Console.WriteLine($</div>"查询到 {users.Count()} 个用户");
// 分布式事务示例
using var transaction = distributedProvider.BeginDistributedTransaction();
transaction.Begin(IsolationLevel.ReadCommitted);
try
{
// 执行跨分片操作
userTable.Insert(new User { Username = "新用户", Email = "new@example.com", Age = 30 });
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
}
总结
ORMX 的分布式数据库功能提供了:
- ✅ 简单易用:直观的 API,无需深入了解分布式系统的复杂性
- ✅ 灵活扩展:支持多种分片策略和自定义策略
- ✅ 高性能:通过数据分片和读写分离提升系统性能
- ✅ 数据一致性:完善的分布式事务支持
- ✅ 生产就绪:故障转移、负载均衡等企业级特性
通过 ORMX,你可以轻松构建可扩展的分布式数据库应用,而无需被底层实现细节所困扰。