2020年最新阿里C/C++ Linux后台开发面试题及答案

1、Mysql有哪些存储引擎?请详细列举其区别?
InnoDB: 事务型存储引擎, 并且有较高的并发读取频率

MEMORY: 存储引擎,存放在内存中,数据量小, 速度快

Merge:

ARCHIVE: 归档, 有很好的压缩机制

2、设计高并发系统数据库层面该如何设计? 数据库锁有哪些类型?如何实现?

  1. 分库分表: 同样量的数据平均存储在不同数据库相同表(或不同表)中,减轻单表压力,如果还是很大,就可以每个库在分多张表,根据hash取值或者其他逻辑判断将数据存储在哪张表中

  2. 读写分离: 数据库原本就有主从数据库之分,查询在从服务器,增删改在主服务器,

  3. 归档和操作表区分: 建一张归档表,将历史数据放入,需要操作的表数据单独存储

  4. 索引啊之类的创建,对于数据量很大,百万级别以上的单表,如果增删改操作不频繁的话, 可以创建bitMap索引,速度要快得多

  5. 共享锁:要等第一个人操作完,释放锁,才能操作

  6. 更新锁:解决死锁,别人可以读,但不能操作

  7. 排他锁:读写都被禁用

  8. 意向锁(xlock): 对表中部分数据加锁,查询时,可以跳过

  9. 计划锁: 操作时,别的表连接不了这张表

3、讲讲分布式唯一ID?
确定ID存储用64位,1个64位二进制1是这样的00000000…1100…0101,切割64位,某段二进制表示成1个约束条件,前41位为毫秒时间,后紧接9位为IP,IP之后为自增的二进制,记录当前面位数相同情况下是第几个id,如现在有10台机器,这个id生成器生成id极限是同台机器1ms内生成2的14次方个ID。

分布式唯一ID = 时间戳 << 41位, int类型服务器编号 << 10,序列自增sequence。每个时间戳内只能生成固定数量如(10万)个自增号,达到最大值则同步等待下个时间戳,自增从0开始。将毫秒数放在最高位,保证生成的ID是趋势递增的,每个业务线、每个机房、每个机器生成的ID都是不同的。如39bit毫秒数|4bit业务线|2bit机房|预留|7bit序列号。高位取2016年1月1日1到现在的毫秒数,系统运行10年,至少需要10年x365天x24小时x3600秒x1000毫秒=320x10~9,差不多39bit给毫秒数,每秒单机高峰并发小于100,差不多7bit给每毫秒的自增号,5年内机房小于100台机器,预留2bit给机房,每个机房小于100台机器,预留7bit给每个机房,业务线小于10个,预留4bit给业务线标识。

64bit分布式ID(42bit毫秒+5bit机器ID+12位自增)等

生成分布式ID的方式:A,2个自增表,步长相互隔开 B,时间的毫秒或者纳秒 C,UUID D,64位约束条件(如上)

4、Redis内存数据上升到一定大小会执行数据淘汰策略,Redis提供了哪6种数据淘汰策略?
LRU:从已设置过期时间的数据集合中挑选最近最少使用的数据淘汰

random:从已设置过期时间的数据中挑选任意数据淘汰

ttl:从已设置过期时间的数据集合中挑选将要过期的数据淘汰。

notenvision:禁止驱逐数据

如mysql中有2千万数据,redis只存储20万的热门数据。LRU或者TTL都满足热点数据读取较多,不太可能超时特点。

redis特点:速度块,O(1),丰富的数据类型,支持事物原子性,可用于缓存,比memecache速度块,可以持久化数据。

常见问题和解决:Master最好不做持久化如RDB快照和AOF日志文件;如果数据比较重要,某分slave开启AOF备份数据,策略为每秒1次,为了主从复制速度及稳定,MS主从在同一局域网内;主从复制不要用图状结构,用单向链表更为稳定 M-S-S-S-S。。。。;redis过期采用懒汉+定期,懒汉即get/set时候检查key是否过期,过期则删除key,定期遍历每个DB,检查制定个数个key;结合服务器性能调节并发情况。

过期淘汰,数据写入redis会附带1个有效时间,这个有效时间内该数据被认为是正确的并不关心真实情况,例如对支付等业务采用版本号实现,redis中每一份数据都维持1个版本号,DB中也维持1份,只有当redis的与DB中的版本一致时,才会认为redis为有效的,不过仍然每次都要访问DB,只需要查询version版本字段即可。

5、ZooKeeper分布式是如何做到高可用?
ZooKeeper 运行期间,集群中至少有过半的机器保存了最新数据。集群超过半数的机器能够正常工作,集群就能够对外提供服务。

zookeeper可以选出N台机器作主机,它可以实现M:N的备份;keepalive只能选出1台机器作主机,所以keepalive只能实现M:1的备份。

通常有以下两种部署方案:双机房部署(一个稳定性更好、设备更可靠的机房,这个机房就是主要机房,而另外一个机房则更加廉价一些,例如,对于一个由 7 台机器组成的 ZooKeeper 集群,通常在主要机房中部署 4 台机器,剩下的 3 台机器部署到另外一个机房中);三机房部署(无论哪个机房发生了故障,剩下两个机房的机器数量都超过半数。在三个机房中都部署若干个机器来组成一个 ZooKeeper 集群。假设机器总数为 N,各机房机器数:N1 = (N-1)/2 ,N2=1~(N-N1)/2 ,N3 = N - N1 - N2 )。

水平扩容就是向集群中添加更多机器,Zookeeper2种方式(不完美),一种是集群整体重启,另外一种是逐台进行服务器的重启。

6、如何将数据分布在redis第几个库?
redis 本身支持16个数据库,通过 数据库id 设置,默认为0。

例如jedis客户端设置。一:JedisPool(org.apache.commons.pool.impl.GenericObjectPool.Config poolConfig, String host, int port, int timeout, String password, int database);

第一种通过指定构造函数database字段选择库,不设置则默认0库。二:jedis.select(index);调用jedis的select方法指定。

7、幂等的处理方式?
一、查询与删除操作是天然幂等

二、唯一索引,防止新增脏数据

三、token机制,防止页面重复提交

四、悲观锁 for update

五、乐观锁(通过版本号/时间戳实现, 通过条件限制where avai_amount-#subAmount# >= 0)

六、分布式锁

七、状态机幂等(如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。)

八、select + insert(并发不高的后台系统,或者一些任务JOB,为了支持幂等,支持重复执行)

8、Https工作流程?
a、客户端发送自己支持的加密规则给服务器,代表告诉服务器要进行连接了

b、服务器从中选出一套加密算法和hash算法以及自己的身份信息(地址等)以证书的形式发送给浏览器,证书中包含服务器信息,加密公钥,证书的办法机构

c、客户端收到网站的证书之后要做下面的事情:

c1、验证证书的合法性

c2、如果验证通过证书,浏览器会生成一串随机数作为密钥K,并用证书中的公钥进行加密

c3、用约定好的hash算法计算握手消息,然后用生成的密钥K进行加密,然后一起发送给服务器

d、服务器接收到客户端传送来的信息,要求下面的事情:

d1、用私钥解析出密码,用密码解析握手消息,验证hash值是否和浏览器发来的一致

d2、使用密钥加密消息,回送

如果计算法hash值一致,握手成功

9、RabbitMQ消息堆积怎么处理?
增加消费者的处理能力(例如优化代码),或减少发布频率

单纯升级硬件不是办法,只能起到一时的作用

考虑使用队列最大长度限制,RabbitMQ 3.1支持

给消息设置年龄,超时就丢弃

默认情况下,rabbitmq消费者为单线程串行消费,设置并发消费两个关键属性concurrentConsumers和prefetchCount,concurrentConsumers设置的是对每个listener在初始化的时候设置的并发消费者的个数,prefetchCount是每次一次性从broker里面取的待消费的消息的个数

建立新的queue,消费者同时订阅新旧queue

生产者端缓存数据,在mq被消费完后再发送到mq

打破发送循环条件,设置合适的qos值,当qos值被用光,而新的ack没有被mq接收时,就可以跳出发送循环,去接收新的消息;消费者主动block接收进程,消费者感受到接收消息过快时主动block,利用block和unblock方法调节接收速率,当接收线程被block时,跳出发送循环。

新建一个topic,partition是原来的10倍;然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue;接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据;等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息;

10、一个线程池正在处理服务如果忽然断电该怎么办?
队列实现持久化储存,下次启动自动载入。

但是实际需要看情况,大体思路是这样。

添加标志位,未处理 0,处理中 1,已处理 2。每次启动的时候,把所有状态为 1 的, 置为 0。或者定时器处理

关键性的应用就给电脑配个 UPS。

11、红黑树的特点?
(1)每个节点或者是黑色,或者是红色。

(2)根节点是黑色。

(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL) 的叶子节点!]

(4)如果一个节点是红色的,则它的子节点必须是黑色的。

(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。[这里指到 叶子节点的路径]

12、分布式服务调用的特点?
分布式服务调用可以实现跟踪系统,可以在业务日志中添加调用链ID,各个环节RPC 均添加调用时延,QPS等。

非业务组件应该少加入业务代码,服务调用采用买点,也会采用配置采样率方式,买点 即当前节点的上下文信息,包含TraceId,RPCId,开始结束时间,类型,协议,调用 方IP,端口,服务名等,以及其他异常信息,报文等扩展,日志采用离线+实时的如flume 结合kafka等,应按照TraceId汇总日志后按RPCId顺序整理。

13、如何编写高质量C++代码的建议?

  1. extern C的作用是当程序被C++编译器编译时,让后续的链接器以C方式来寻找函数,方便C++程序调用C程序。

  2. C++风格注释形如:// …,推荐使用这样的注释。但是,头文件说明和函数默认参数的注释,还是用C风格(/**/)的较好。

  3. 不要写与编译器依赖紧密的代码

例如:printf(“a %d %d”, p(), q()),p和 q函数的执行前后顺序与编译器实现相关,应当避免此类代码。类似的还有 c = p() * q() * r()。

  1. 尽量用const,enum,inline代替#define

inline关键字用在函数调用展开,在类声明中定义并且实现的函数自动为内联函数。如果需要将其他函数定义为内联函数,则需要在函数实现头声明此关键字,才让编译器尝试去内联,至于具体是否内联,还要求函数体满足一定条件才行,总体原则是短小精悍。

在使用define的场合,注意用()来保护宏函数。例如:#define MAX(a, b) ((a) > (b) ? (a) : (b))

  1. struct在C和C++下的异同,C语言的struct不允许定义函数程序,而C++语言下的struct可以。

  2. 所有数据成员一律为private类型。如果派生类需要用到,那么在用到的时候再将其改为protect类型,否则,一律声明为private类型,对外隐藏。在具体声明时,可以按类型来多段声明,比如私有控件,来一个private,私有数据来另外一个private。

  3. 当类中至少包含一个虚函数时,才需要将其析构函数设置为虚函数。不要在构造/析构函数中调用虚函数。

  4. 以行为为中心的类设计,对外的public函数放在前面,需要继承的protect虚函数紧随其后,再后面是private的虚函数、普通函数以及成员变量。

  5. 语法的背后含义是语义,接口设计要有明确的语义,不可模棱两可、职责不清。

  6. 如底层发生异常,则需要逐级上报,直到有能力处理此异常的层级来处理。如果程序都没处理,则会被C++系统捕获并终止程序运行。异常可以将发生错误和处理错误分离。

  7. 一般以传值来抛出异常,以 const 引用来捕获异常,不涉及到异常对象的清理工作,无对象切割问题,如本层级处理后还需要继续抛出异常,可调用throw来。

  8. 优先使用shared_ptr,它内部工作原理是引用计数,线程安全,支持扩展,推荐使用。

由于篇幅有限,今天就分享到这里,需要更多2020最新面试题(阿里系、腾讯系、百度系)可以加q裙812855908,免费分享

在这里插入图片描述
2020年最新阿里面试题及答案
在这里插入图片描述
2020年最新百度面试题及答案
在这里插入图片描述
2020年最新腾讯面试题及答案

猜你喜欢

转载自blog.csdn.net/qq_40989769/article/details/104820434