2020美团校招后端工程师

这篇博客主要涵盖了美团校招后端工程师面试中的核心知识点,包括如何排查线上CPU Load过高问题,MySQL联合索引的命中规则,以及分布式事务的原理和解决方案。详细讲解了利用Linux命令进行CPU问题诊断,MySQL的最左前缀匹配原则,并介绍了2PC、3PC和TCC等分布式事务解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果线上某台虚机CPU Load过高,该如何快速排查原因?只介绍思路和涉及的Linux命令即可 。

造成cpu load过高的原因: Full gc次数的增大、代码中存在Bug(例如死循环、正则的不恰当使用等)都有可能造成cpu load 增高。

  1. jps -v:查看java进程号
  2. top -Hp [java进程号]:查看当前进程下最耗费CPU的线程
  3. printf “%x\n” [步骤2中的线程号]:得到线程的16进制表示
  4. jstack [java进程号] | grep -A100 [步骤3的结果]:查看线程堆栈,定位代码行。参考:如何使用JStack分析线程状态
    top命令查看个进程cpu使用率

请简要描述MySQL数据库联合索引的命中规则,可举例说明。

  1. MySQL联合索引遵循最左前缀匹配规则,即从联合索引的最左列开始向右匹配,直到遇到匹配终止条件。例如联合索引(col1, col2, col3), where条件为col1=‘a’ AND col2='b’可命中该联合索引的(col1,col2)前缀部分, where条件为col2=‘b’ AND col3='c’不符合最左前缀匹配,不能命中该联合索引。

  2. 匹配终止条件为范围操作符(如>, <, between, like等)或函数等不能应用索引的情况。例如联合索引(col1, col2, col3), where条件为col1=‘a’ AND col2>1 AND col3=‘c’ 在col2列上为范围查询,匹配即终止,只会匹配到col1,不能匹配到(col1, col2, col3).

  3. where条件中的顺序不影响索引命中。例如联合索引(col1, col2, col3), where条件为col3=c AND col2=b AND col1=a, MySQL优化器会自行进行优化,可命中联合索引(col1, col2, col3).

什么是分布式事务,分布式事务产生的原因是什么?分布式事务的解决方案有哪些?分别有哪些优缺点?

分布式事物:将一次大的操作分为很多小的操作,这些小的操作位于各自的服务器上,分布式事物需要保证这些小的操作要么全部成功,要么全部失败。
分布式事物产生的原因:1.为了解决不同数据库操作时数据不一致的问题。2.应用SOA化。
分布式事物的解决方案:
1.2PC:两阶段提交
优点:保证数据的强一致性,适合对数据要求高的强一致性领域。
缺点:实现复杂,牺牲了可用性,性能不高,不适合高并发,高性能的场景。
2.3PC:三阶段提交
优点:相对于二阶段,它减低了阻塞的范围,解决了协调者这参与者同时挂掉的问题,即等待超时后,协调者或参与者会中断事务,避免单点问题。
缺点:数据不一致性依然存在。
3.补偿事务(TCC)
优点:1)性能提升,2)数据最终一致, 3)可靠性更高
缺点:花费高

第二个答案 更具体一点

一次大的操作由不同的小操作组成的,这些小的操作分布在不同的服务器上,分布式事务需要保证这些小操作要么全部成功,要么全部失败。从本质上来说,分布式事务是为了保证不同数据库的数据一致性。
2.1 数据库分库分表
当数据库单表数据达到千万级别,就要考虑分库分表,那么就会从原来的一个数据库变成多个数据库。例如如果一个操作即操作了01库,又操作了02库,而且又要保证数据的一致性,那么就要用到分布式事务。
2.2 应用SOA化
所谓的SOA化,就是业务的服务化。例如电商平台下单操作就会产生调用库存服务扣减库存和订单服务更新订单数据,那么就会设计到订单数据库和库存数据库,为了保证数据的一致性,就需要用到分布式事务。
归根到底是要操作多数据库,并且要保证数据的一致性,而产生的分布式事务的。
解决方案有:
1.用XA实现两阶段提交(2PC)
XA是一个分布式事务协议,由Tuxedo提出。XA中大致分为两部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如Oracle、Mysql等数据库都实现了XA接口,而事务管理器作为全局的调度者,负责各个本地资源的提交回滚。
缺点:1、同步阻塞问题,2、单点故障,3、数据不一致;
2.三阶段提交(3PC)
3PC其实在2PC的基础上增加了CanCommit阶段,是2PC的变种,并引入了超时机制。一旦事务参与者迟迟没有收到协调者的Commit请求,就会自动进行本地commit,这样相对有效的解决了协调者单点故障的问题。但是,性能和数据一致性问题没有根本解决。
3 补偿事务(TCC)
TCC(Try-Confirm-Cancel)又称补偿事务。它实际上与2PC、3PC一样,都是分布式事务的一种实现方案而已。它分为三个操作:
Try阶段:主要是对业务系统做检测及资源预留。
Confirm阶段:确认执行业务操作。
Cancel阶段:取消执行业务操作。

请描述https的请求过程。

  1. 客户端向服务器发起HTTPS请求,连接到服务器的443端口;

  2. 服务器端有一个密钥对,即公钥(即数字证书)和私钥,是用来进行非对称加密使用的,服务器端保存着私钥,不能将其泄露,公钥可以发送给任何人;

  3. 服务器将自己的公钥发送给客户端;

  4. 客户端收到服务器端的公钥之后,检查其合法性,如果发现发现公钥有问题,那么HTTPS传输就无法继续,如果公钥合格,则客户端会生成一个客户端密钥,然后用服务器的公钥对客户端密钥进行非对称加密成密文,至此,HTTPS中的第一次HTTP请求结束;

  5. 客户端发起HTTPS中的第二个HTTP请求,将加密之后的客户端密钥发送给服务器;

  6. 服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文;

  7. 然后服务器将加密后的密文发送给客户端;

  8. 客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。这样HTTPS中的第二个HTTP请求结束,整个HTTPS传输完成。

什么是事务传播行为?你知道Spring事务中都有哪些传播类型吗?如何使用/指定传播类型?

1) 事务传播用于描述当一个由事务传播行为修饰的方法被嵌套入另外一个方法时,事务如何传播。常用于定义发生事务嵌套时,如何继续执行。

2) Spring 中共定义了7中事务传播类型,明细如下表, 需答出3~4种常见类型即可:

a) PROPAGATION_REQUIRED: 当前没有事务时开启新事务,如果有则加入;

b) PROPAGATION_REQUIRES_NEW: 强制开启新事务,挂起已有事务(如有);

c) PROPAGATION_SUPPORTS: 当前有事务时加入, 没有则以非事务方式执行;

d) PROPAGATION_NOT_SUPPORTED: 以非事务方式执行, 挂起当前事务(如有);

3) 可以在注解或者XML中指定传播类型, 如 “@Transactional(Propagation=xxx)”

IO设计中Reactor 和 Proactor 区别。

1、 Reactor被动的等待指示事件的到来并作出反应,有一个等待的过程,做什么都要先放入到监听事件集合中等待handler可用时再进行操作,实现相对简单,对于耗时短的处理场景比较高效,但Reactor处理耗时长的操作会造成事件分发的阻塞,影响到后续事件的处理。

2、Proactor直接调用异步读写操作,调用完后立刻返回,实现了一个主动的事件分离和分发模型;这种设计允许多个任务并发的执行,从而提高吞吐量;并可执行耗时长的任务(各个任务间互不影响),Proactor性能更高,能够处理耗时长的并发场景,但Proactor实现逻辑复杂;依赖操作系统对异步的支持,目前实现了纯异步操作的操作系统少,实现优秀的如windows IOCP,但由于其windows系统用于服务器的局限性,目前应用范围较小;而Unix/Linux系统对纯异步的支持有限,应用事件驱动的主流还是通过select/epoll来实现。

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串(回文串是一个正读和反读都一样的字符串)。

具有不同开始位置或结束位置的回文串,即使是由相同的字符组成,也会被计为是不同的子串。

输入描述:

输入仅包含一个字符串,长度不会超过 1000。

输出描述:

输出仅包含一个非负整数, 代表输入字符串有多少个回文子串。

输入例子1:

abc

输出例子1:

3

输入例子2:

aaa

输出例子2:

6

动态规划

import java.util.*;
public class Main {
    public static void main (String[] args) {
        Scanner in = new Scanner(System.in);
        String str = in.next();
        int n = str.length();
        int res = 0;
        boolean[][] dp = new boolean[n][n];
        char[] c = str.toCharArray();
        for(int i = n - 1; i >= 0; i--) {
            for(int j = i; j < n; j++) {  
            // 如果对应字符相等  判断i~j的内的字符是否是回文字符串  是的化 那么i~j是回文字符串
            // j - i <= 2 据代表如aa 或者 a 都是一个回文字符串
                dp[i][j] = (c[i] == c[j]) && (j - i <= 2 || dp[i + 1][j - 1]);
                if (dp[i][j]) {
                    res++;
                }
            }
        }
        System.out.println(res);
    }
}

有 N 堆金币排成一排,第 i 堆中有 C[i] 块金币。每次合并都会将相邻的两堆金币合并为一堆,成本为这两堆金币块数之和。经过N-1次合并,最终将所有金币合并为一堆。请找出将金币合并为一堆的最低成本。

其中,1 <= N <= 30,1 <= C[i] <= 100

输入描述:

第一行输入一个数字 N 表示有 N 堆金币
第二行输入 N 个数字表示每堆金币的数量 C[i]

输出描述:

输出一个数字 S 表示最小的合并成一堆的成本

输入例子1:

4
3 2 4 1

输出例子1:

20

输入例子2:

30
10 20 30 40 50 60 70 80 90 100 99 89 79 69 59 49 39 29 19 9 2 12 22 32 42 52 62 72 82 92

输出例子2:

7307

//转移方程就是因为合成一堆有不同的组合方法
//如2 3 6 1
//当len=2时 ,组合有2 3 3 6 6 1
//获取当前组合的最小值
//当len=3
//组合有 一堆是2 3 6 组合为2 3 6 组合为2 3 6
//比如组合2 3 6 就要获取2 3的最优解 3 6的最优解
//当Len=4
//组合有2 3 6 1 2 3 6 1 2 3 6 1
//因为之前已经得到了2 3 6 的最优解 和 3 6 1的最优解
//继续比较两者到底谁小

    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[][] dp = new int[n][n];
        int[] arr = new int[n + 1];
        for(int i = 1; i <= n; i++) {
                arr[i] = arr[i - 1] + in.nextInt();
        }
        for(int k = 2; k <= n; k++) {
            for (int i = 0; i <= n - k; i++) { // 起点  一堆有多少组成  i = 2 表示两个一堆
                int end = i + k - 1; // 终点  
                int sum = arr[end + 1] - arr[i]; //  end + 1 因为 arr[0] 为0 是空出来的  最后一定会相加的值  如 2 3 6 堆  最后一定合成一定加上11
                dp[i][end] = Integer.MAX_VALUE;
                for(int j = i; j < end; j++) {
                    dp[i][end] = Math.min(dp[i][end], dp[i][j] + dp[j + 1][end] + sum);  
                }
            }
        }
        System.out.println(dp[0][n - 1]);
    }

给定一组个字符串,为每个字符串找出能够唯一识别该字符串的最小前缀。

输入描述:

第一行输入一个整数 n 表示字符串个数
后面n行,每行一个字符串,一共n串互不相同的字符串。(2 <= n <= 100,字符串长度不超过100)

输出描述:

输出n行,每行一个字符串,依次是每个字符串的最小可唯一识别前缀

输入例子1:

5
meituanapp
meituanwaimai
dianpingliren
dianpingjiehun
mt

输出例子1:

meituana
meituanw
dianpingl
dianpingj
mt

前缀树

package com.luyi;

import java.util.*;
public class Main {
    public Node root;
    public List<Integer> temp;
    public static void main (String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        List<String> list = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            list.add(in.next());
        }
        Main m = new Main();
        for (int i = 0; i < n; i++) {
            m.init(list.get(i), i);
        }
        m.getRes(list);
    }
    public  void init(String s, int g) {
        if (s.length() == 0) {
            return;
        }
        char[] ch;
        if (this.root == null) {
            ch = s.toCharArray();
            Node node = new Node(ch[0], 0);
            node.count = 0;
            this.root = node;
        }
             ch = s.toCharArray();
            for (int i = 0; i < ch.length; i++) {
                Node node = new Node(ch[i], i);
                node.group = g;
                node.count = 1;
                insetNode(this.root, node);
        }
    }

    public void getRes(List<String> list) {
        for (int i = 0; i < list.size(); i++) {
            temp = new ArrayList<>();
            search(this.root, i);
            int min = 65535;
            for (int j = 0; j < temp.size(); j++) {
                min = Math.min(min, temp.get(j));
            }
            if (min == 65535) {
                System.out.println(list.get(i));
            } else {
                System.out.println(list.get(i).substring(0, min + 1));
            }
        }
    }

    public void search(Node node,int k) {
        if (node.group == k && node.count == 1) {
            temp.add(node.index);
        }
        if (node.left != null) {
            search(node.left, k);
        }
        if (node.right != null) {
            search(node.right, k);
        }
    }

    public void  insetNode (Node node1,Node node) {
        if (node1.ch > node.ch) {
            if (node1.left != null) {
                insetNode(node1.left, node);
            } else {
                node1.left = node;
            }
        } else if (node1.ch < node.ch){
            if (node1.right != null) {
                insetNode(node1.right, node);
            } else {
                node1.right = node;
            }
        } else {
            if (node.index == node1.index) {
                node1.count++;
            } else  {
                if (node1.right != null) {
                    insetNode(node1.right, node);
                } else {
                    node1.right = node;
                }
            }

        }
    }
    class Node {
        char ch;
        int index;
        int count;
        int group;
        Node left = null;
        Node right = null;
        Node ( char ch, int index) {
            this.ch = ch;
            this.index = index;
        }
    }
}

// 5
//meituanapp
//meituanwaimai
//dianpingliren
//dianpingjiehun
//mt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值