golang面试经验答案总结(一)分享一篇B站后端面经 哔哩哔哩

问题来源于分享一篇B站后端面经 哔哩哔哩,自学习使用,答案搜索来自于网络,若有不对的地方欢迎指正。

1 go的gmp

GMP,自己总结的算是较为详尽的内容。

2 cpu特别高如何定位

答案来源
问题定位的步骤如下:

  • 找到CPU使用率较高的进程
  • 找到CPU使用率最高的线程
  • 查看堆栈信息,定位到相关代码

步骤一、找到耗CPU的进程

执行top -d 1,显示进程运行信息列表。
在这里插入图片描述如上图 ,最耗CPU的进程PID为:11205

步骤二、找到耗CPU的线程

通过上一步,我们确定了是pid=11205的cava_smu进程cpu过高,那么可以通过top -d 1 -p 11205 -H来确认cpu过载的线程tid,如下图所示:

在这里插入图片描述通过以上操作,可以确认tid=11208,11212,11213三个线程的cpu过高。

步骤三、查看堆栈信息,定位相关代码

首先,如果生产环境没有dlv,则可以拷贝对应的dlv到/usr/local/bin下。
接着 dlv attach 11205,确认tid=11208的goroutine 序号,如下图所示:
在这里插入图片描述
在dlv中切换到对应高cpu协程,并查看堆栈
在这里插入图片描述
通过以上操作,可以确认业务底层的栈帧是第6→5帧,business.go:18行的disPatchTask ->business.go:168 行的dispatchIdleTeu方法相关,查看对应版本代码如下:
代码执行到下图中,dispatchIdleTeu返回了错误qferror.ErrNoTeu。
在这里插入图片描述
代码执行到下图中,189行dispatchIdleTeu返回了错误qferror.ErrNoTeu,所以189 if的执行语句192~212无法进入进行,而外层是一个for死循环,则会造成该协程一直占用cpu,导致cpu过载。

3 mysql引擎

内容来源
MYSQL常用2个引擎分别是MyISAMInnoDB。没啥特殊的话请使用innodb。 myisam已被放弃。

读操作多用 MyISAM
写操作多用 InnoDB
1、myisam查询效率更高,查询效率差myisam6-7倍。
2、innodb支持事务,行锁,外键。myisam不支持。
如果数据表涉及的存储数据多、查询多,用myisam,如文章表。
如果数据表涉及业务逻辑多,增删改操作多,就用innodb,如订单表。

MyISAM适合:

  1. 做很多count 的计算;
  2. 插入不频繁,查询非常频繁;
  3. 没有事务。

InnoDB适合:

  1. 要求事务;
  2. 表更新和查询频繁
  3. 大尺寸的数据集趋向于选择InnoDB引擎,因为它支持事务处理和故障恢复。数据库的大小决定了故障恢复的时间长短,InnoDB可以利用事务日志进行数据恢复,这比较快。

区别

  1. InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;

  2. InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;

  3. InnoDB是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而MyISAM是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。

  4. ```InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数``,执行上述语句时只需要读出该变量即可,速度很快;

  5. InnoDB支持行锁

  6. InnoDB:5.6以后才有全文索引;MyISAM:支持全文索引;不支持事务;它是表级锁;会保存表的具体行数.

4 复合索引生效问题

mysql联合索引原理及失效条件

当创建(col1,col2,col3)联合索引时,相当于创建了(col)单列索引(clo1,clo2)联合索引以及(col1,col2,col3)联合索引。想要索引生效,只能使用(col1)(col1,col2)(col1,col2,col3)三种组合。当然,col1,col3组合也可以,但实际上只用到了col1的索引,col3并没有用到。

通俗理解
复合索引的结构与电话簿类似,人名由姓和名构成,电话簿首先按姓氏对进行排序,然后按名字对有相同姓氏的人进行排序。如果您知道姓,电话簿将非常有用;如果您知道姓和名,电话簿则更为有用,但如果您只知道名不姓,电话簿将没有用处。

所以说创建复合索引时,应该仔细考虑列的顺序。对索引中的所有列执行搜索或仅对前几列执行搜索时,复合索引非常有用;仅对后面的任意列执行搜索时,复合索引则没有用处。

mysql联合索引注意事项

命名规则:表名_字段名

  1. 需要加索引的字段,要在where条件中
  2. 数据量少的字段不需要加索引
  3. 如果where条件中是OR关系,加索引不起作用
  4. 符合最左原则

5 sql执行慢的原因?如何看是什么问题?

执行SQL响应比较慢,你有哪些排查思路?
如果执行SQL响应比较慢,我觉得可能有以下4个原因:

  • 第1个原因:没有索引或者导致索引失效。
  • 第2个原因:单表数据量数据过多,导致查询瓶颈。
  • 第3个原因:网络原因或者机器负载过高。
  • 第4个原因:热点数据导致单点负载不均衡。

解决方案

第1种情况:索引失效或者没有没有索引的情况
首先,可以打开MySQL的慢查询日志,收集一段时间的慢查询日志内容,然后找出耗时最长的SQL语句,对这些SQL语句进行分析。
**加粗样式**
比如可以利用执行计划explain去查看SQL是否有命中索引。如果发现慢查询的SQL没有命中索引,可以尝试去优化这些SQL语句,保证SQL走索引执行。如果SQL结构没有办法优化的话,可以考虑在表上再添加对应的索引。我们在优化SQL或者是添加索引的时候,都需要符合最左匹配原则。

第2种情况:单表数据量数据过多,导致查询瓶颈的情况。
即使SQL语句走了索引,表现性能也不会特别好。这个时候我们需要考虑对表进行```切分``。表切分规则一般分为两种,一种是水平切分,一种是垂直切分。

  • 水平切分的意思是把一张数据行数达到千万级别的大表,按照业务主键切分为多张小表,这些小表可能达到100张甚至1000张。
  • 垂直切分的意思是,将一张单表中的多个列,按照业务逻辑把关联性比较大的列放到同一张表中去。
  • 分库:比如我们已经拆分完1000表,然后,把后缀为0-100的表放到同一个数据库实例中,然后,100-200的表放到另一个数据库实例中,依此类推把1000表存放到10个数据库实例中。这样的话,我们就可以根据业务主键把请求路由到不同数据库实例,从而让每一个数据库实例承担的流量比较小,达到提高数据库性能的目的。

第3种情况:网络原因或者机器负载过高的情况,我们可以进行读写分离
比如MySQL支持一主多从的分布式部署,我们可以将主库只用来处理写数据的操作,而多个从库只用来处理读操作。在流量比较大的场景中,可以增加从库来提高数据库的负载能力,从而提升数据库的总体性能。

第4种情况:热点数据导致单点负载不均衡的情况。
这种情况下,除了对数据库本身的调整以外,还可以增加缓存。将查询比较频繁的热点数据预存到缓存当中,比如Redis、MongoDB、ES等,以此来缓解数据的压力,从而提高数据库的响应速度。

6 redis zset实现

redis跳表——zset的底层实现
redis中有序集合的实现采用了一种数据结构——跳跃表。跳跃表是有序单链表的一种改进,其查询、插入、删除也是O(logN)的时间复杂度。

zset的两种实现方式

  • ziplist(压缩链表):满足以下两个条件的时候

    • 元素数量少于128的时候
    • 每个元素的长度小于64字节
  • skiplist(跳跃链表):不满足上述两个条件就会使用跳表,具体来说是组合了map和skiplist

    • map用来存储member到score的映射,这样就可以在O(1)时间内找到member对应的分数
    • skiplist按从小到大的顺序存储分数
    • skiplist每个元素的值都是[score,value]对

skiplist原理

在这里插入图片描述
上图用a,b,c,d,e五种有序链表说明了跳跃表的motivation.

  1. [a]单链表:查询时间复杂度O(n)7
  2. [b]level-2单链表:每隔一个节点为一个level-2节点,每个level-2节点有2个后继指针,分别指向单链表中的下一个节点和下一个level-2节点。查询时间复杂度为O(n/2)
  3. [c]level-3单链表:每隔一个节点为一个level-2节点,每隔4个节点为一个level-3节点,查询时间复杂度O(n/4)
  4. [d]指数式单链表:每隔一个节点为一个level-2节点,每隔4个节点为一个level-3节点,每隔8个节点为一个level-4节点(每2^i个节点的level为i+1),查询时间复杂度为O(log2N)
  5. [e]跳跃表:各个level的节点个数同指数式单链表,但出现的位置随机,查询复杂度是O(logN)

redis选择跳跃表而非红黑树作为有序集合实现方式的原因并非是基于并发上的考虑,因为redis是单线程的,选用跳跃表的原因仅仅是因为跳跃表的实现相较于红黑树更加简洁。

7 go 一致性hash

Golang实现一致性Hash算法

实现原理

实现原理:将key映射到 2^32 的空间中,将这个数字的首尾相连,形成一个环

  • 计算节点(使用节点名称、编号、IP地址)的hash值,放置在环上
  • 计算key的hash值,放置在环上,顺时针寻找到的第一个节点,就是应选取的节点

    例如:p2、p4、p6三个节点,key11、key2、key27按照顺序映射到p2、p4、p6上面,假设新增一个节点p8在p6节点之后,这个时候只需要将key27从p6调整到p8就可以了也就是说,每次新增删除节点时,只需要重新定位该节点附近的一小部分数据

数据倾斜问题

如果服务器的节点过少,容易引起key的倾斜。例如上面的例子中p2、p4、p6分布在环的上半部分,下半部分是空的。那么映射到下半部分的key都会被分配给p2,key过度倾斜到了p2缓存间节点负载不均衡;
为了解决这个问题,引入了虚拟节点的概念,一个真实的节点对应多个虚拟的节点
假设1个真实的节点对应3个虚拟节点,那么p1对应的就是p1-1、p1-2、p1-3

  • 计算虚拟节点的Hash值,放置在环上
  • 计算key的Hash值,在环上顺时针寻找到对应选取的虚拟节点,例如:p2-1,对应真实的节点p2
    虚拟节点扩充了节点的数量,解决了节点较少的情况下数据倾斜的问题,而且代价非常小,只需要新增一个字典(Map)维护真实的节点与虚拟节点的映射关系就可以了。

8 rpc

推荐入门
RPC代指 远程过程调用(Remote Procedure Call)

优点

  • 简单
  • 高效

9 tcp和udp区别

  • TCP 面向连接(如打电话要先拨号建立连接)提供可靠的服务,UDP 是无连接的,即发送数据之前不需要建立连接,UDP 尽最大努力交付,即不保证可靠交付。
  • UDP 具有较好的实时性,工作效率比 TCP 高,适用于对高速传输和实时性有较高的通信或广播通信。
  • 每一条 TCP 连接只能是一对一的,UDP 支持一对一,一对多,多对一和多对多的交互通信。
  • UDP 分组首部开销小,TCP 首部开销 20 字节,UDP 的首部开销小,只有 8 个字节。
  • TCP 面向字节流,实际上是 TCP 把数据看成一连串无结构的字节流,UDP 是面向报文的一次交付一个完整的报文,报文不可分割,报文是 UDP 数据报处理的最小单位。
    UDP 适合一次性传输较小数据的网络应用,如 DNS,SNMP 等。

10 线程与进程

  • 本质区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。
  • 包含关系:一个进程至少有一个线程,线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
  • 资源开销:每个进程都有独立的地址空间,进程之间的切换会有较大的开销;线程可以看做轻量级的进程,同一个进程内的线程共享进程的地址空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小。
  • 影响关系:一个进程崩溃后,在保护模式下其他进程不会被影响,但是一个线程崩溃可能导致整个进程被操作系统杀掉,所以多进程要比多线程健壮。

11死锁以及预防

死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

死锁产生原因

  1. 系统资源的竞争:系统资源的竞争导致系统资源不足,以及资源分配不当,导致死锁。

  2. 进程运行推进顺序不合适:进程在运行过程中,请求和释放资源的顺序不当,会导致死锁。

死锁的四个必要条件

  • 互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
  • 请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
  • 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
  • 循环等待条件: 若干进程间形成首尾相接循环等待资源的关系。

这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

预防死锁的方法主要有以下几种:

  • 避免使用多个锁:如果可能的话,尽量避免使用多个锁,而是使用一种更高级别的同步机制,例如条件变量、信号量等。
  • 统一加锁顺序:如果必须使用多个锁,那么就要保证所有线程都按照相同的顺序加锁,这样可以避免死锁的发生。
  • 使用超时机制:在加锁的时候,可以设置一个超时时间,如果在规定时间内无法获取锁,就放弃锁并释放已经占用的锁,避免死锁的发生。
  • 避免持有锁的同时等待其他锁:如果一个线程已经持有了一个锁,那么就不应该再去等待其他锁,否则容易导致死锁的发生。
  • 尽量减小锁的粒度:如果可能的话,尽量将锁的粒度减小,这样可以减少锁的竞争,从而降低死锁的概率。
  • 使用死锁检测工具:在开发过程中,可以使用一些死锁检测工具来检测程序中是否存在死锁的情况,及时发现并解决问题。

http状态码

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

12 设计索引

MySQL性能优化之索引设计
通常情况下,字段类型的选择是需要根据业务来判断的,通常需要遵循以下几点。

  • 确定合适的大类型:数字、字符串、日期和时间、二进制等。
  • 确定具体的类型:有无符号、取值范围、变长定长等。
  • 尽量选择更小的数据类型,因为它们通常有更好的性能,占用更少的硬件资源。
  • 尽量把字段定义为NOT NULL,避免使用NULL

数值类型

  • 如果整型数据没有负数,如id号,建议指定为UNSIGNED无符号类型,容量可以扩大一倍。
  • 整数通常是最佳的数据类型,因为它速度快,并且能使用AUTO_INCREMENT。
    建议使用TINYINT代替ENUM、BITENUM、SET。
  • 建议使用整型类型来运算和存储实数,一种方法是实数乘以相应的倍数后再操作;另外一种方法是使用两个字段来分别存储整数位和小数位。
  • DECIMAL最适合保存准确度要求高并且用于计算的数据,比如价格、金额等,但是在使用DECIMAL类型的时候注意长度设置。
  • 避免使用整数的显示宽度,也就是说不要用INT(5)类似的方法指定字段显示宽度,直接用INT。

字符串类型

  • 当字符串短,并且所有值都固定一个长度或者接近一个长度时使用CHAR,当然要是如果没有完全可以使用整型来存储;字符串长度相差较大时使用VARCHAR。
  • CHAR和VARCHAR适合长度不超过255个字符唱的的任意字母和数字组合,例如人名、电话号码、编码等。用来计算的数字不要用VARCHAR类型保存,因为可能会导致一些与计算相关的问题,同时可能影响到计算的准确性和完整性。
  • VARCHAR(255)在建立索引时会占用比较多的存储空间,在不要求保证数据完全精确的境况下可以使用前缀索引。例如idx_name_age_position(name(20), age, position),取前20个字符作为索引,但是这种情况下因为是不完全字段,所以order by name asc或者group by name 排序过程无法使用索引排序。当然需要保证数据的精确性和查找速度,最优的方案就是使用全文搜索引擎ES了。
  • 尽量不用BLOB和TEXT,如果实在要用可以考虑将BLOB和TEXT字段单独存一张表,使用主键id来关联。
  • BLOB和TEXT都不能有默认值。BLOB系列存储二进制字符串,与字符集无关;TEXT系列存储非二进制字符串,与字符集相关。

时间类型

  • MySQL能存储的最小时间粒度为秒。当然要是需要精确到毫秒级的话,当然也是有办法的,新加一列在另外一列保留毫秒值即可。
  • 建议使用MySQL的内建类型DATE、TIME、DATETIME来存储时间,而不是使用字符串或者保存时间戳,这样的话可以通过MySQL的日期函数处理相关逻辑。
    当不需要具体时间时,建议用DATE数据类型来保存日期,MySQL中默认的日期格式是YYYY-MM-DD。
  • **当数据格式为TIMESTAMP和DATETIME时,可以用CURRENT_TIMESTAMP作为(MySQL5.6以后),MySQL会自动返回记录插入的当前确切时间。**不过需要注意的是校准MySQL运行环境的时间和时区,比如Linux时间或者docker容器的时间和时区。
  • TIMESTAMP是UTC时间戳,与时区相关;DATETIME的存储格式是一个YYYYMMDD HH:MM:SS的整数,与时区无关,存的什么读出来就是什么。
  • 一般的短期项目或者小公司项目小二建议使用TIMESTAMP,因为这种项目生命往往活不到2038年,DATETIME还更节约空间。但是如果是腾讯、阿里、京东一般会用DATETIME,因为不用考虑TIMESTAMP将来的时间上限问题。

13 给你场景,你来选择tcp还是udp

TCP

相对于 UDP,TCP 实现了数据传输过程中的各种控制,可以进行丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。在对可靠性要求较高的情况下,可以使用 TCP,即不考虑 UDP 的时候,都可以选择 TCP。
需要可靠连接,比如付费、加密数据等等方向都需要依靠TCP

UDP

UDP不提供复杂的控制机制,利用IP提供面向无连接的通信服务,随时都可以发送数据,处理简单且高效。
以主要使用在以下场景:

  • 包总量较小的通信(DNS、SNMP)
  • 视频、音频等多媒体通信(即时通信)
  • QQ就是使用的UDP协议。
  • 广播通信
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值