分布式面试核心知识点

本文详细介绍了分布式环境下的负载均衡算法,包括普通随机算法和轮询算法及其优化,并探讨了分布式数据库自增IP的挑战与解决方案,如哈希算法、号段模式和tinyID等。这些内容对于理解分布式系统的面试核心知识点非常有帮助。

目录

 

一、负载均衡随机算法

1.普通随机算法:

2.轮询算法

二、哈希算法

三、分布式数据库自增IP

1.各种id自增缺陷

2.数据库自增主键:

目录

 

一、负载均衡随机算法

1.普通随机算法:

2.轮询算法

二、哈希算法

三、分布式数据库自增IP

1.各种id自增缺陷

2.数据库自增主键:

3.号段模式

4.tinyID

九种id,很好博客


 


 

一、负载均衡随机算法

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

 

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值