目录
1 什么是分布式数据库负载均衡呢?
分布式数据库为了更有效利用不同物理机节点的资源, 避免服务器性能的浪费,在数据库高负载的情况下需要尽量将压力平衡到各个物理机节点上。
2 分布式数据库读写
既然说到负载,对数据库而言,负载就是读和写。那么我们必须简单了解数据库本身的架构。下面以kwbase为例。分布式数据库通过多副本机制做到数据高可用(即一份数据不可用,另外还有完全一样数据的副本)。下面我们以最简单的3副本为例进行说明。
例如,一个表的数据会有3个副本,分别分布在3个不同的节点上。其中一个副本1会获取 lease,该副本1就是 leaseholder。系统的读写都是在leaseholder上进行的,leaseholder会将写操作复制到其他的两个副本上。
3 如何进行负载均衡呢?
我们了解了分布式数据库的读写方式后,就可以给分布式数据库设计负载均衡方案了。
-
想象一个场景1,如果数据库有90个表,假设其中有30个表的leaseholder在node1上,但这个30个表的qps非常高,占数据库访问qps的90%了。显然这样不满足负载均衡,应该怎么做呢?
-
想象一个场景2,假如集群有那么一个节点,还没有任何访问qps。我们肯定可以把数据放在上面,让它去分担一些数据库的读写压力。怎么去做呢?
-
想象一个场景3,如果某个表的的qps非常高,导致该节点压力太大。怎么去处理呢?
实际上上面说的3个场景的解决办法分别是:
1 lease(租约)均衡
2 副本均衡
3 热点数据分裂
-
均衡操作什么时候执行呢?我们能想到的是周期性检查,如果需要均衡,则做均衡操作。
周期性执行均衡操作。
实际上kwbase也是这样做的。代码如下:
可以看出kwbase在节点启动时,启动一个StoreRebalancer 线程,每个隔1分钟执行一次均衡操作, sr.rebalanceStore 均衡操作包括 lease均衡和副本均衡。
func (s *Store) Start() { // 节点启动
...
if s.replicateQueue != nil {
s.storeRebalancer = NewStoreRebalancer( // 新建 storeRebalancer
s.cfg.AmbientCtx, s.cfg.Settings, s.replicateQueue, s.replRankings)
s.storeRebalancer.Start(ctx, s.stopper) // 启动 storeRebalancer
}
}
func (sr *StoreRebalancer) Start() { // 启动 storeRebalancer
...
stopper.RunWorker(ctx, func(ctx context.Context) {
timer := timeutil.NewTimer()
defer timer.Stop()
timer.Reset(jitteredInterval(storeRebalancerTimerDuration)) // 时间间隔1分钟
for {
// Wait out the first tick before doing anything since the store is still
// starting up and we might as well wait for some qps/wps stats to
// accumulate.
select {
case <-stopper.ShouldQuiesce():
return
case <-timer.C:
timer.Read = true
timer.Reset(jitteredInterval(storeRebalancerTimerDuration))
}
mode := LBRebalancingMode(LoadBasedRebalancingMode.Get(&sr.st.SV))
if mode == LBRebalancingOff {
continue
}
storeList, _, _ := sr.rq.allocator.storePool.getStoreList(roachpb.RangeID(0), storeFilterNone)
sr.rebalanceStore(ctx, mode, storeList) // 执行均衡操作,包括lease均衡和副本均衡
}
})
}
4 Lease (租约)均衡
如3中所说的场景1
想象一个场景1,如果数据库有90个表,假设其中有30个表的leaseholder在node1上,那么对数但这个30个表的qps非常高,占数据库访问qps的90%了。显然这样不满足负载均衡,应该怎么做呢?
如图所示
答:我们可以将一些 leaseholder 转移到其他的副本上。
-
Lease 均衡前
如图所示共9个表,每个表在一个range上,每个range有3个副本,其中标蓝色的是leaseholder,读写都在此副本上执行。range1 - 1 1000,表示ragne1的1号副本,是leaseholder副本,它上面的qps是1000。可以看出3个节点的qps分别是 3000, 30, 30。 负载是不均衡的。
我们知道 range1 - 1, range1 - 2, range1 - 3 是同一个表的3个副本,他们的数据是完全一样的,只是lease 角色不同。我们可以将安全的将lease 在他们之间迁移,以做到lease均衡。
-
Lease 均衡后
如图所示我们将range2的lease 迁移到节点2,将range3的lease 迁移到节点3。现在我们可以看到3个节点的qps分别是 1000, 1030, 1030。做到了负载均衡。 因为只转移了lease,副本分布没有变化,所以叫lease 均衡。
5 副本均衡
如3中所说的场景2
想象一个场景2,假如集群有那么一个节点,还没有任何访问qps。我们肯定可以把数据放在上面,让它去分担一些数据库的读写压力。怎么去做呢?
如图所示
我们可以删除原有副本,在其他节点上重建副本。
-
副本均衡前
可以看出,副本迁移前,4个节点的qps分别是2000,3000, 3000, 0 ,负载时不均衡的。
-
副本均衡后
可以看出副本 range4 - 1 从节点2迁移到了节点4,副本range7 - 1 从节点3迁移到了节点4。4个节点副本迁移后的qps都是2000,实现了负载均衡。由于副本分布发生了变化,所以叫副本均衡。
6 热点数据分裂
如3中所说的场景2
想象一个场景3,如果某个表的的qps非常高,导致该节点压力太大。怎么去处理呢?
我们可以将要给热点表,分裂成两个表。
-
热数据分裂前
可以看出range1 的数据是热数据,3个节点的qps分别是1020, 30, 520。负载是不均衡的。
-
热数据分裂后
可以看出range1 分裂成了 range1*1 和 range1*2, range1*1 的lease 在节点1上, range1*2 的lease 在节点2上。最终3个节点的qps分别是 520, 530, 520, 实现了负载均衡。
7 总结
我们举例子简单描述了负载均衡的3个策略,lease均衡,副本均衡,热点数据分裂,接下来的章节我们结合kwbase代码,具体了解这3种均衡策略的实现细节。
https://www.modb.pro/db/84934