目录
目录
一、负载均衡随机算法
1.普通随机算法:
假设有十台服务器
public class ServersIp {
public static final List<String> list = Arrays.asList(
"192.168.000.1",
"192.168.000.2",
"192.168.000.3",
"192.168.000.4",
"192.168.000.5",
"192.168.000.6",
"192.168.000.7",
"192.168.000.8",
"192.168.000.9",
"192.168.000.10"
);
}
模拟客户端随机获取ip
public class Random {
public static String getServerIp(){
java.util.Random random = new java.util.Random();
int nextInt = random.nextInt(ServersIp.list.size());
return ServersIp.list.get(nextInt);
}
public static void main(String[] args) {
for (int i =0;i<10;i++){
System.out.println(getServerIp());
}
}
}
缺点:服务器性能不同,配置高的服务器无法发挥性能
解决方法:增加权重。
优化后代码如下
public static final Map<String,Integer> weight_list = new LinkedHashMap<String,Integer>();
static {
// 权重55
weight_list.put("192.168.000.1",1);
weight_list.put("192.168.000.2",2);
weight_list.put("192.168.000.3",3);
weight_list.put("192.168.000.4",4);
weight_list.put("192.168.000.5",5);
weight_list.put("192.168.000.6",6);
weight_list.put("192.168.000.7",7);
weight_list.put("192.168.000.8",8);
weight_list.put("192.168.000.9",9);
weight_list.put("192.168.000.10",10);
}
取IP的方法:
// 根据权重将ip多次放入新list中,例如这个55大小的list中会有10个权重为10的同一IP,再根据list大小生成随机数,这样权重大的概率会大
public static String getServerIpForWeight() {
List<String> ips = new ArrayList<String>();
for (String ip: ServersIp.weight_list.keySet()) {
// 根据IP取权重
Integer weight = ServersIp.weight_list.get(ip);
//根据权重累加ip
for (int i = 0; i < weight; i++) {
ips.add(ip);
}
}
java.util.Random random = new java.util.Random();
int nextInt = random.nextInt(ips.size());
return ips.get(nextInt);
}
缺点:如果权重为100,如果ip过多,那么会导致list越大,这样占用内存越大。
解决方法:将权重视为一个坐标轴,类似下图,ABC代表服务器

优化后代码:
//权重随机改版
public static String getServerIpForWeightV2() {
// 首先算出总权重。
int totalweight = 0;
for (Integer weight : ServersIp.weight_list.values()
) {
totalweight += weight;
}
java.util.Random random = new java.util.Random();
int nextInt = random.nextInt(totalweight);
for (String ip : ServersIp.weight_list.keySet()
) {
Integer weight = ServersIp.weight_list.get(ip);
// 如果权重大于随机数就返回IP,如果没有就继续随机
if (nextInt < weight) {
return ip;
}
nextInt = nextInt - weight;
}
return "";
}
2.轮询算法
// 简单轮询
public String getServerIP() {
if (pos >= ServersIp.list.size()) {
pos=0
}
String ip = ServersIp.list.get(pos);
pos++;
return ip;
}
缺点:同样是权重问题。
优化算法思路如同随机第二种,不再写。
具体轮询实现:
自增id
public class RequestId {
public static Integer id =0;
public static Integer getId(){
return id++;
}
}
// 权重轮询
public String getServerIPForWeight(){
int totalWeight=0;
for (Integer weight: ServersIp.weight_list.values()
) {
totalWeight += weight;
}
// 如果id比较大的情况下,可以取余,得到余数进行判断
Integer id = RequestId.getId();
int pos = id % totalWeight;
for (String ip: ServersIp.weight_list.keySet()
) {
Integer weight = ServersIp.weight_list.get(ip);
if (pos<weight){
return ip;
}
pos = pos-weight;
}
return "";
}
缺点:如果A的服务器权重特别高,如果这样相当于连续的请求都会压在一台服务器上。
解决方法,除了IP和静态权重,还需要动态权重。
平滑加成的轮询算法
已知有三个服务器ABC,静态权重如下。
第一个请求过来时,将静态权重加上动态权重,并赋予动态权重,则权重为5,1,1。取最大动态权重,权重5的服务器是A,然后将最大的权重减去权重和,则ABC权重为-2,1,1
第二个请求过来是,将静态权重加上动态权重,并赋予动态权重,则权重为3,2,2. 取最大动态权重,即服务器A ,然后然后将最大的权重减去权重和,-4,2,2
......

代码实现:
public static Weight getServerIPForWeightV2() {
// 计算总权重
int totalWeight = 0;
for (Integer weight : ServersIp.weight_list.values()
) {
totalWeight += weight;
}
if (weightMap.isEmpty()) {
// java8语法,将IP放入weightMap中并赋予初始值
ServersIp.weight_list.forEach((ip, weight) -> {
weightMap.put(ip, new Weight(ip, weight, 0));
});
}
// 遍历map,处理静态权重和动态权重
for (Weight weight:weightMap.values()
) {
// 动态权重为静态权重加动态权重
weight.setCurrentWeight(weight.getCurrentWeight() +weight.getWeight());
}
Weight maxCurrentWeight =null;
for (Weight weight:weightMap.values()
) {
// 如果maxCurrentWeight为空,或者是当前的weight大于maxCurrentWeight,就把当前weight赋予maxCurrentWeight
if (null==maxCurrentWeight||weight.getCurrentWeight()>maxCurrentWeight.getCurrentWeight()){
maxCurrentWeight=weight;
}
}
// 在返回值前,将动态权重减去总权重
maxCurrentWeight.setCurrentWeight(maxCurrentWeight.getCurrentWeight()-totalWeight);
return maxCurrentWeight;
}
二、哈希算法
生产级hash算法关键点:1.哈希环,2.虚拟节点,3、treeMap。
// 定义treeMap作为hash环
private static TreeMap<Integer, String> virtualNodes = new TreeMap<Integer, String>();
// 虚拟节点数目
private static final int VIRTUAL_NODES = 200;
// 为每个真实节点添加虚拟节点,并进行散列
static {
for (String ip : ServersIp.list
) {
for (int i = 0; i < VIRTUAL_NODES; i++) {
// gethash只是个算法不再写
int hash = getHash(ip + "VN" + i);
virtualNodes.put(hash, ip);
}
}
}
//hash算法
public static String getServerIP(String clientInfo) {
int hash = getHash(clientInfo);
SortedMap<Integer, String> subMap = virtualNodes.tailMap(hash);//根据传入的hash返回个大于此hash的红黑树
// 如果为空,说明该hash就是最大的,则返回最初map的最小值
if (subMap == null) {
return virtualNodes.get(virtualNodes.firstKey());
}
Integer firstKey = subMap.firstKey();//获取第一个元素,即最小的
return virtualNodes.get(firstKey);//返回对应IP
}
三、分布式数据库自增IP
1.各种id自增缺陷
1.uuid,1.非自增,2.uuid过长,使用索引不方便。
2.数据库自增主键:
分表时不做处理自增id会重复.
解决方案,新的数据库,新建一个表,用该表的id做主键,对应的表和查询语句。
CREATE TABLE seqid.sequence_id(
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
stub CHAR(10) NOT NULL DEFAULT '',
PRIMARY KEY (id),
UNIQUE KEY stub(stub)
)ENGINE=MYISAM;
BEGIN:
REPLACE INTO sequence_id(stub) VALUES ('anyword');--select DELETE INSERT 插入前查询,如果有就delete再insert,如果没有就insert
SELECT LAST_INSERT_ID();
COMMIT ;
但是这个方式并不高可用,如果该库挂了,就不行了。解决方法就是数据库主从。但是主从之间会有延迟,即主库更新到8,从库可能只同步到7,所以可能会重复。所以可以多个主数据库生成id,然后通过步长和开始数字来解决重复 ,这样满足了高可用,和不重复。
SET @@AUTO_INCREMENT_offset =1 ;--从一开始
SET @@AUTO_INCREMENT_INCREMENT=2;--步长为2
但也有缺陷,如果新加一台,这种方式需要停掉所有数据库去加。而且规律很容易出错。
3.号段模式
基于数据库自增要请求一次数据库。号段模式实际上是把数据库的id一次性拿出多个然后放入内存中使用。.
4.tinyID
https://blog.youkuaiyun.com/xinzhifu1/article/details/105213395
九种id,很好博客
https://blog.youkuaiyun.com/xinzhifu1/article/details/104297243
本文详细介绍了分布式环境下的负载均衡算法,包括普通随机算法和轮询算法及其优化,并探讨了分布式数据库自增IP的挑战与解决方案,如哈希算法、号段模式和tinyID等。这些内容对于理解分布式系统的面试核心知识点非常有帮助。
794





