architecture
Реализация шардирования
Практическая реализация шардирования в приложениях
•
#scaling
#sharding
#databases
#implementation
Реализация шардирования
Практическое руководство по реализации шардирования в реальных приложениях.
Shard Manager
class ShardManager {
private shards: Map<string, Shard> = new Map();
private strategy: ShardingStrategy;
constructor(config: ShardConfig) {
this.initializeShards(config.shards);
this.strategy = this.createStrategy(config.strategy);
}
async query(shardKey: string, sql: string, params: any[]): Promise<any> {
const shard = this.strategy.getShard(shardKey);
const connection = await this.getConnection(shard);
return connection.query(sql, params);
}
async queryAll(sql: string, params: any[]): Promise<any[]> {
const promises = Array.from(this.shards.values()).map(async (shard) => {
const connection = await this.getConnection(shard);
return connection.query(sql, params);
});
return Promise.all(promises);
}
}
Repository Pattern
abstract class ShardedRepository<T> {
constructor(protected shardManager: ShardManager) {}
protected abstract getShardKey(entity: T): string;
async findById(id: string, shardKey: string): Promise<T | null> {
const sql = `SELECT * FROM ${this.getTableName()} WHERE id = ?`;
const result = await this.shardManager.query(shardKey, sql, [id]);
return result.length > 0 ? this.mapToEntity(result[0]) : null;
}
async save(entity: T): Promise<T> {
const shardKey = this.getShardKey(entity);
const data = this.mapToData(entity);
const columns = Object.keys(data);
const values = Object.values(data);
const sql = `INSERT INTO ${this.getTableName()}
(${columns.join(', ')}) VALUES (${columns.map(() => '?').join(', ')})`;
await this.shardManager.query(shardKey, sql, values);
return entity;
}
}
Migration
class MigrationManager {
async migrate(tableName: string): Promise<void> {
const totalRecords = await this.getTotalRecords(tableName);
const batchSize = 1000;
let processed = 0;
for (const shard of this.shardManager.getShards()) {
let offset = 0;
while (true) {
const batch = await this.readBatch(shard, tableName, offset, batchSize);
if (batch.length === 0) break;
for (const record of batch) {
await this.migrateRecord(tableName, record);
}
processed += batch.length;
offset += batchSize;
}
}
}
}
Мониторинг
class ShardingMetrics {
recordQuery(shardId: string, duration: number, type: 'read' | 'write'): void {
const metric = this.getOrCreateMetric(shardId);
metric.totalQueries++;
metric.totalDuration += duration;
}
getMetrics(shardId: string): ShardMetric {
return this.getOrCreateMetric(shardId);
}
}
Best Practices
- Connection Pooling — используйте пулы соединений
- Retry Logic — реализуйте повторные попытки
- Monitoring — отслеживайте метрики шардов
- Graceful Degradation — обрабатывайте отказы шардов
- Testing — тестируйте на нескольких шардах
Заключение
Реализация шардирования требует:
- Правильного выбора shard key
- Эффективной маршрутизации запросов
- Мониторинга и метрик
- Стратегии миграции данных