平衡二叉树口诀

平衡二叉树口诀2022年最新C/C++后台开发60道大厂面试题(含答案)(总结一)1. 使用mysql索引都有哪些原则?索引什么数据结构?B+tree 和 B tree 什么区别?1、 对于查询频率高的字段创建索引;2、 对排序

2022年最新C/C++后台开发60道大厂面试题(含答案)(总结一)   1. 使用mysql索引都有哪些原则?索引什么数据结构?B+tree 和 B tree 什么区别?   1、 对于查询频率高的字段创建索引;   2、 对排序、分组、联合查询频率高的字段创建索引;   3、 索引的数目不宜太多   原因: a、每创建一个索引都会占用相应的物理控件;   b、过多的索引会导致insert、update、delete语句的执行效率降低;   4、若在实际中,需要将多个列设置索引时,可以采用多列索引   如:某个表(假设表名为Student),存在多个字段(StudentNo, StudentName, Sex, Address, Phone,BirthDate),其中需要对StudentNo,StudentName字段进行查询,对Sex字段进行分组,对BirthDate 字段进行排序,此时可以创建多列索引index index_name (StudentNo, StudentName, Sex, BirthDate);#index_name为索引名   在上面的语句中只创建了一个索引,但是对4个字段都赋予了索引的功能。   创建多列索引,需要遵循BTree类型,即第一列使用时,才启用索引。   在上面的创建语句中,只有mysql语句在使用到StudentNo字段时,索引才会被启用。   如: select * from Student where StudentNo = 1000; #使用到了StudentNo字段,索引被启用。   以使用explain检测索引是否被启用如:explain select * from Student where StudentNo = 1000;   5、选择唯一性索引   唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。例如,学生表中学号是具有唯 一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息。如果使用姓名的话,可能存 在同名现象,从而降低查询速度。   6、尽量使用数据量少的索引   如果索引的值很长,那么查询的速度会受到影响。例如,对一个CHAR(100)类型的字段进行全文检索 需要的时间肯定要比对CHAR(10)类型的字段需要的时间要多。   7、尽量使用前缀来索引   如果索引字段的值很长,最好使用值的前缀来索引。例如,TEXT和BLOG类型的字段,进行全文检 索会很浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度。   8、删除不再使用或者很少使用的索引.   表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理 员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响B+ tree树索引, B tree,散列.   2. Mysql有哪些存储引擎?请详细列举其区别?   InnoDB: 事务型存储引擎, 并且有较高的并发读取频率   MEMORY: 存储引擎,存放在内存中,数据量小, 速度快   Merge:   ARCHIVE: 归档, 有很好的压缩机制   总结归纳了2022年十家大厂50道面试题总共500+道需要的伙伴自取:CC++Linux后端服务器10家大厂开发面试题全集   3. 设计高并发系统数据库层面该如何设计? 数据库锁有哪些类型?如何实现?   1. 分库分表: 同样量的数据平均存储在不同数据库相同表(或不同表)中,减轻单表压力,如果还是很大,就可以每个库在分多张表,根据hash取值或者其他逻辑判断将数据存储在哪张表中   2. 读写分离: 数据库原本就有主从数据库之分,查询在从服务器,增删改在主服务器,   3. 归档和操作表区分: 建一张归档表,将历史数据放入,需要操作的表数据单独存储   4. 索引啊之类的创建,对于数据量很大,百万级别以上的单表,如果增删改操作不频繁的话, 可以创建bitMap索引,速度要快得多   1. 共享锁:要等第一个人操作完,释放锁,才能操作   2. 更新锁:解决死锁,别人可以读,但不能操作   3. 排他锁:读写都被禁用   4. 意向锁(xlock): 对表中部分数据加锁,查询时,可以跳过   5. 计划锁: 操作时,别的表连接不了这张表   4. 数据库事务有哪些?   原子性: 所有操作要么全部成功,要么全部失败   一致性: 例如转账,一个事务执行前和执行后必须一致   隔离性: 防止脏读, 重复读问题   持久性: 永久性提交数据库   5. Oracle常用函数有哪些?   Concat: 字符串拼接, 或者 ||   MConcat: 字符串拼接, 或者 ||   Instr: 指定字符串位置   Length: 长度   Trim: 去空格   Lower: 小写   Upper:大写   Nvl: 判断空   Replace: 替换   Substr: 截取   Floor: 向下取整   To_number:   To_char:   To_date:   Decode: 判断函数等等   6. Sql中哪些情况可能不会走索引?   1. 查询谓词没有使用索引的主要边界,换句话说就是select *,可能会导致不走索引   2. 单键值的b树索引列上存在null值,导致COUNT(*)不能走索引。索引列存在空值   3. 索引列上有函数运算,导致不走索引   4. 隐式类型转换导致不走索引。   5. 表的数据库小或者需要选择大部分数据,不走索引   6. !=或者<>(不等于),可能导致不走索引   7. 表字段的属性导致不走索引,字符型的索引列会导致优化器认为需要扫描索引大部分数据且聚簇因子很大,最终导致弃用索引扫描而改用全表扫描方式,   8. 使用like, in 等, 可能导致不走索引   7. 讲讲分布式唯一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毫秒=320×10~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位约束条件(如上)   8. NIO和IO的区别?   第一点,NIO少了1次从内核空间到用户空间的拷贝。   ByteBuffer.allocateDirect()分配的内存使用的是本机内存而不是Java堆上的内存,和网络或者磁盘交互都在操作系统的内核空间中发生。allocateDirect()的区别在于这块内存不由java堆管理, 但仍然在同一用户进程内。   第二点,NIO以块处理数据,IO以流处理数据   第三点,非阻塞,NIO1个线程可以管理多个输入输出通道   9. 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版本字段即可。   10. 请描述MyISM和InnoDB?   MyISM采用表级锁,对Myism表读不会阻塞读,会阻塞同表写,对Myism写则会阻塞读和写,即一个线程获得1个表的写锁后,只有持有锁的线程可以对表更新操作,其他线程的读和写都会等待。   InnoDB,采用行级锁,支持事务,例如只对a列加索引,如果update …where a=1 and b=2其实也会锁整个表, select 使用共享锁,update insert delete采用排它锁,commit会把锁取消,当然select by id for update也可以制定排它锁。   11. 请描述实时队列?   实时队列采用双队列模式,生产者将行为记录写入Queue1,worker服务从Queue1消费新鲜数据,如果异常则写入Queue2(主要保存异常数据),RetryWorker会监听Queue2,消费异常数据,如果还未处理成功按照一定的策略等待或者将异常数据再写入Queue2,如果数据发生积压可以调整worker的消费游标,从最新数据重新开始消费,保证了最新data得到处理,中间未处理的一段则可以启动backupWorker指定起止游标在消费完指定区间的数据后,backupWorker会自动停止。   DB降级开关后,可直接写入redis(storm),同时将数据写入一份到Retry队列,在开启DB降级开关后消费Retry队列中的数据,从而把数据写入到mysql中,达到最终一致性。MYSQL切分为分片为2的N次方,例如原来分为两个库d0和d1均放在s0服务器上,s0同时有备机s1,扩容只要几步骤:确保s0到s1服务器同步顺利,没有明显延迟;s0暂时关闭读写权限;确保s1已经完全同步到s0更新;s1开放读写权限;d1的dns由s0切换到s1;s0开放读写权限。   12. DB的特性和隔离级别?   4大特性:原子性,一致性,分离性,持久性   隔离级别:   读提交:写事务禁止读   读未提交:写事务允许读   可重复读:写事务禁止读事务,读禁止写   序列化:全部禁止   详细说明:读提交1个事务开始写则全部禁止其他事务访问该行。读未提交1个事务开始写则不允许其他事务同时写,但可以读。可重复读 读事务会禁止写事务,写事物则禁止其他任何事务。序列化性能最低,全部禁止,串行执行。 MYSQL默认的是可重复读。   13. ICMP是什么协议,处于哪一层?   Internet控制报文协议,处于网络层(IP层)   14. 讲一下NIO和网络传输.   NIO Reactor反应器模式,例如汽车是乘客访问的实体reactor,乘客上车后到售票员处Acceptor登记,之后乘客 便可休息睡觉了,到达乘客目的地后,售票员Aceptor将其唤醒即可。持久TCP长链接每个client和server之间有存在一 个持久连接,当CCU(用户并发数量)上升,阻塞server无法为每个连接运行1个线程,自己开发1个二进制协议,将 message压缩至3-6倍,传输双向且消息频率高,假设server链接了2000个client,每个client平均每分钟传输1-10个 message,1个messaged的大小为几百字节/几千字节,而server也要向client广播其他玩家的当前信息,需要高速处 理消息的能力。Buffer,网络字节存放传输的地方,从channel中读写,从buffer作为中间存储格式,channel是网络连 接与buffer间数据通道,像之前的socket的stream。   15. 内存泄漏   未对作废数据内存单置为null,尽早释放无用对象的引用,使用临时变量时,让引用变量在推出活动域后自动设置为null,暗示垃圾收集器收集;程序避免用String拼接,用StringBuffer,因为每个String会占用内存一块区域;尽量少用静态变量(全局不会回收);不要集中创建对象尤其大对象,可以使用流操作;尽量使用对象池,不再循环中创建对象,优化配置;创建对象到单例getInstance中,对象无法回收被单例引用;服务器session时间设置过长也会引起内存泄漏。   16. 请描述平衡二叉树.   平衡二叉树,左右高度之差不超过1,Add/delete可能造成高度>1,此时要旋转,维持平衡状态,避免二叉树退化为链表,让Add/Delete时间复杂度但控制在O(log2N),旋转算法2个方法,1是求树的高度,2是求2个高度最大值,1个空树高度为-1,只有1个根节点的树的高度为0,以后每一层+1,平衡树任意节点最多有2个儿子,因此高度不平衡时,此节点的2棵子树高度差为2。例如单旋转,双旋转,插入等。   红黑树放弃完全平衡,追求大致平衡,保证每次插入最多要3次旋转就能平衡。   17. 请问溢出的原因?   是否递归的调用;大量循环;全局变量是否过多;数组,List,Map数据是否过大;用DDMS工具检查地方。   内存溢出的原因   过多使用了static;static最好只用int和string等基本类型;大量的递归或者死循环;大数据项的查询,如返回表的所有记录,应该采用分页查询。检查是否有数组、List、map中存放的是对象的引用而不是对象,这些引用会让对应对象不能被释放。   栈过大会导致内存占用过多,频繁页交换阻碍效率。   32,说一下http/2   Http/2采用二进制格式而不是文本   Http/2是完全多路复用的,而非有序并阻塞的。   Http/2使用报头压缩   Http/2让服务器可以将响应主动推送到客户端缓存中。   18. 单例模式的7种写法.   懒汉2种,枚举,饿汉2种,静态内部类,双重校验锁(推荐)。   19. lucence倒排索引.   三个文件:字典文件,频率文件,位置文件。词典文件不仅保存有每个关键词,还保留了指向频率文件和位置文件的指针,通过指针可以找到该关键字的频率信息和位置信息。   field的概念,用于表达信息所在位置(如标题中,文章中,url中),在建索引中,该field信息也记录在词典文件中,每个关键词都有一个field信息(因为每个关键字一定属于一个或多个field)。   关键字是按字符顺序排列的(lucene没有使用B树结构),因此lucene可以用二搜索算法快速定位关键词。   假设要查询单词 “live”,lucene先对词典二查找、找到该词,通过指向频率文件的指针读出所有文章号,然后返回结果。词典通常非常小,因而,整个过程的时间是毫秒级的。   对词典文件中的关键词进行了压缩,关键词压缩为<前缀长度,后缀>,例如:当前词为“阿拉伯语”,上一个词为“阿拉伯”,那么“阿拉伯语”压缩为<3,语>。对数字的压缩,数字只保存与上一个值的差值。   20. 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种方式(不完美),一种是集群整体重启,另外一种是逐台进行服务器的重启。   21. 如何将数据分布在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方法指定。   22. kafka高性能的原因?   A,Broker NIO异步消息处理,实现了IO线程与业务线程分离;   B,磁盘顺序写;   C, 零拷贝(跳过用户缓冲区的拷贝,建立一个磁盘空间和内存的直接映射,数据不再复制到用户态缓冲区);   D,分区/分段(每次文件操作都是对一个小文件的操作,非常轻便,同时也增加了并行处理能力);   F,批量发送 (可以指定缓存的消息达到某个量的时候就发出去,或者缓存了固定的时间后就发送出去,大大减少服务端的I/O次数)   E,数据压缩   23. 幂等的处理方式?   一、查询与删除操作是天然幂等   二、唯一索引,防止新增脏数据   三、token机制,防止页面重复提交   四、悲观锁 for update   五、乐观锁(通过版本号/时间戳实现, 通过条件限制where avai_amount-#subAmount# >= 0)   六、分布式锁   七、状态机幂等(如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。)   八、select + insert(并发不高的后台系统,或者一些任务JOB,为了支持幂等,支持重复执行)   24. Https工作流程?   a、客户端发送自己支持的加密规则给服务器,代表告诉服务器要进行连接了   b、服务器从中选出一套加密算法和hash算法以及自己的身份信息(地址等)以证书的形式发送给浏览器,证书中包含服务器信息,加密公钥,证书的办法机构   c、客户端收到网站的证书之后要做下面的事情:   c1、验证证书的合法性   c2、如果验证通过证书,浏览器会生成一串随机数作为密钥K,并用证书中的公钥进行加密   c3、用约定好的hash算法计算握手消息,然后用生成的密钥K进行加密,然后一起发送给服务器   d、服务器接收到客户端传送来的信息,要求下面的事情:   d1、用私钥解析出密码,用密码解析握手消息,验证hash值是否和浏览器发来的一致   d2、使用密钥加密消息,回送   如果计算法hash值一致,握手成功   25. 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机器来消费消息;   26. RabbitMQ的消息丢失解决方案?   消息持久化:Exchange 设置持久化:durable:true;Queue 设置持久化;Message持久化发送。   ACK确认机制:消息发送确认;消息接收确认。   27. 负载均衡算法?   常见6种负载均衡算法:轮询,随机,源地址哈希,加权轮询,加权随机,最小连接数。   nginx5种负载均衡算法:轮询,weight,ip_hash,fair(响应时间),url_hash   dubbo负载均衡算法:随机,轮询,最少活跃调用数,一致性Hash   28. 一个线程池正在处理服务如果忽然断电该怎么办?   队列实现持久化储存,下次启动自动载入。   但是实际需要看情况,大体思路是这样。   添加标志位,未处理 0,处理中 1,已处理 2。每次启动的时候,把所有状态为 1 的, 置为 0。或者定时器处理   关键性的应用就给电脑配个 UPS。   29. DoS,DDoS,DRDoS攻击分别是什么?   DoS是Denial of Service的简写就是拒绝服务。   DDoS就是Distributed Denial of Service的简写就是分布式拒绝服务。   DRDoS就是Distributed Reflection Denial of Service的简写,分布反射式拒绝服务。   DoS、DDos以及DRDoS攻击手段和防范措施   30. 服务限流的方式?   漏桶:水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速 度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求。   令牌桶算法:系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入 Token,如果桶已经满了就不再加了.新请求来临时,会各自拿走一个Token,如果没有 Token就拒绝服务。   基于redis实现的限流:假设每分钟访问次数不能超过10次,在Redis中创建一个键, 过期60秒,对此服务接口的访问就把键值加1,在60秒内增加到10的时候,禁止访 问服务接口。   计数器,滑动窗口   31. Quartz实现原理?   A、scheduler是一个计划调度器容器(总部),容器里面可以盛放众多的JobDetail 和trigger,当容器启动后,里面的每个JobDetail都会根据trigger按部就班自动去 执行。   B、JobDetail是一个可执行的工作,它本身可能是有状态的。   C、Trigger代表一个调度参数的配置,什么时候去调。   D、当JobDetail和Trigger在scheduler容器上注册后,形成了装配好的作业 (JobDetail和Trigger所组成的一对儿),就可以伴随容器启动而调度执行了。   E、scheduler是个容器,容器中有一个线程池,用来并行调度执行每个作业,这样可 以提高容器效率。   32. 数据库的锁?   行锁(共享锁和排他锁),表锁,页级锁,页级锁,意向锁,读锁,写锁,悲观锁,乐观锁等   33. 聚集索引和非聚集索引的区别?   聚集索引:   索引中键值的逻辑顺序决定了表中相应行的物理顺序(索引中的数据物理存放地址和索引的顺序是一致的),可以这么理解:只要是索引是连续的,那么数据在存储介质上的存储位置也是连续的。   比方说:想要到字典上查找一个字,我们可以根据字典前面的拼音找到该字,注意拼音的排列时有顺序的。   聚集索引就像我们根据拼音的顺序查字典一样,可以大大的提高效率。在经常搜索一定范围的值时,通过索引找到第一条数据,根据物理地址连续存储的特点,然后检索相邻的数据,直到到达条件截至项。   非聚集索引   索引的逻辑顺序与磁盘上的物理存储顺序不同。非聚集索引的键值在逻辑上也是连续的,但是表中的数据在存储介质上的物理顺序是不一致的,即记录的逻辑顺序和实际存储的物理顺序没有任何联系。索引的记录节点有一个数据指针指向真正的数据存储位置。   总结如下:   如果一个主键被定义了,那么这个主键就是作为聚集索引   如果没有主键被定义,那么该表的第一个唯一非空索引被作为聚集索引   如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键是一个6个字节的列,改列的值会随着数据的插入自增。   InnoDB引擎会为每张表都加一个聚集索引,而聚集索引指向的的数据又是以物理磁盘顺序来存储的,自增的主键会把数据自动向后插入,避免了插入过程中的聚集索引排序问题。如果对聚集索引进行排序,这会带来磁盘IO性能损耗是非常大的。   34. mysql数据库锁表怎么解决?   查询锁表信息   当前运行的所有事务   select * from information_schema.innodb_trx   当前出现的锁   select * from information_schema.innodb_locks   锁等待的对应关系   select * from information_schema.innodb_lock_waits   通过 select * from information_schema.innodb_trx 查询 trx_mysql_thread_id然 后执行 kill 线程ID   KILL 8807;//后面的数字即时进程的ID   35. 红黑树的特点?   (1)每个节点或者是黑色,或者是红色。   (2)根节点是黑色。   (3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL) 的叶子节点!]   (4)如果一个节点是红色的,则它的子节点必须是黑色的。   (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。[这里指到 叶子节点的路径]   36. kafka消息会不会丢失?   Kafka消息发送分同步(sync)、异步(async)两种方式。默认是使用同步方式,可通过 producer.type属性进行配置;Kafka保证消息被安全生产,有三个选项分别是0,1,-1。   通过request.required.acks属性进行配置:   0代表:不进行消息接收是否成功的确认(默认值);   1代表:当Leader副本接收成功后,返回接收成功确认信息;   -1代表:当Leader和Follower副本都接收成功后,返回接收成功确认信息;   网络异常   acks设置为0时,不和Kafka集群进行消息接受确认,当网络发生异常等情况时,存 在消息丢失的可能;   客户端异常   异步发送时,消息并没有直接发送至Kafka集群,而是在Client端按一定规则缓存并 批量发送。在这期间,如果客户端发生死机等情况,都会导致消息的丢失;   缓冲区满了   异步发送时,Client端缓存的消息超出了缓冲池的大小,也存在消息丢失的可能;   Leader副本异常   acks设置为1时,Leader副本接收成功,Kafka集群就返回成功确认信息,而Follower 副本可能还在同步。这时Leader副本突然出现异常,新Leader副本(原Follower副本) 未能和其保持一致,就会出现消息丢失的情况;   以上就是消息丢失的几种情况,在日常应用中,我们需要结合自身的应用场景来选择不 同的配置。   想要更高的吞吐量就设置:异步、ack=0;想要不丢失消息数据就选:同步、ack=-1策 略   37. kafka的leader副本选举?   如果某个分区patition的Leader挂了,那么其它跟随者将会进行选举产生一个新的 leader,之后所有的读写就会转移到这个新的Leader上,在kafka中,其不是采用常见的 多数选举的方式进行副本的Leader选举,而是会在Zookeeper上针对每个Topic维护一 个称为ISR(in-sync replica,已同步的副本)的集合,显然还有一些副本没有来得及 同步。只有这个ISR列表里面的才有资格成为leader(先使用ISR里面的第一个,如果 不行依次类推,因为ISR里面的是同步副本,消息是最完整且各个节点都是一样的)。   通过ISR,kafka需要的冗余度较低,可以容忍的失败数比较高。假设某个topic有f+1 个副本,kafka可以容忍f个不可用,当然,如果全部ISR里面的副本都不可用,也可以 选择其他可用的副本,只是存在数据的不一致。   38. kafka消息的检索?   其实很简单主要是用二分查找算法,比如我们要查找一条offest=10000的文件,kafka 首先会在对应分区下的log文件里采用二分查看定位到某个记录该offest   =10000这条消息的log,然后从相应的index文件定位其偏移量,然后拿着偏移量到log 里面直接。这样就完成了一个消息的检索过程。   39. RabbitMQ 集群方式?   1)普通集群:   以两个节点(rabbit01、rabbit02)为例来进行说明。   rabbit01和rabbit02两个节点仅有相同的数据,即队列的结构,但消息实体只存在 于其中一个节点rabbit01(或者rabbit02)中。   当消息进入rabbit01节点的Queue后,consumer从rabbit02节点消费时,RabbitMQ 会临时在rabbit01、rabbit02间进行消息传输,把A中的消息实体取出并经过B发送 给consumer。所以consumer应尽量连接每一个节点,从中取消息。即对于同一个逻辑 队列,要在多个节点建立物理Queue。否则无论consumer连rabbit01或rabbit02,出 口总在rabbit01,会产生瓶颈。当rabbit01节点故障后,rabbit02节点无法取到 rabbit01节点中还未消费的消息实体。如果做了消息持久化,那么得等rabbit01节点 恢复,然后才可被消费;如果没有持久化的话,就会产生消息丢失的现象。   2)镜像集群:   在普通集群的基础上,把需要的队列做成镜像队列,消息实体会主动在镜像节点间同步, 而不是在客户端取数据时临时拉取,也就是说多少节点消息就会备份多少份。该模式带 来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息 进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉。所以在对可靠性要求较高 的场合中适用   由于镜像队列之间消息自动同步,且内部有选举master机制,即使master节点宕机也 不会影响整个集群的使用,达到去中心化的目的,从而有效的防止消息丢失及服务不可 用等问题   40. ElasticSearch如何解决深度分页的问题?   使用scroll(有状态)和search after(无状态)的游标方式。   41. 单点登录原理与简单实现?   相比于单系统登录,sso需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,sso认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同。这个过程,也就是单点登录的原理.   单点登录自然也要单点注销,在一个子系统中注销,所有子系统的会话都将被销毁.   42. MQ如何保证消息的一致性?   MQ做数据同步也会造成不一致,又需要引入监控,实时计算2个集群的数据同步,做一致性同步。大部分来说,同步es和solr不要在代码中去同步,同步失败无法保证事务,而且业务耦合。可以使用Databug和cancel等工具去做代码解耦,MQ支持重试,存储失败后抛出异常下次再处理。数据做异构,对外服务时任意拼装,MYSQL在半同步复制上做了一些优化,保证了一致性,引入了诸如paxos等主流算法保证强一致性问题。   当DB(监听从库),binlog有变化,cancel监听到时候解析过滤发送MQ(表名字,主键等)到变化的实时从库中查询数据同步到ES聚合表,MQ可以重试,系统解耦。事务log挖掘县城会对DB的事务log监听,并把这些事件发布到消息代理。   43. 分布式服务调用的特点?   分布式服务调用可以实现跟踪系统,可以在业务日志中添加调用链ID,各个环节RPC 均添加调用时延,QPS等。   非业务组件应该少加入业务代码,服务调用采用买点,也会采用配置采样率方式,买点 即当前节点的上下文信息,包含TraceId,RPCId,开始结束时间,类型,协议,调用 方IP,端口,服务名等,以及其他异常信息,报文等扩展,日志采用离线+实时的如flume 结合kafka等,应按照TraceId汇总日志后按RPCId顺序整理。   44. Sentinel 工作原理?   (1)每个 Sentinel 以每秒钟一次的频率向它所知的 Master,Slave 以及其他 Sentinel 实例发送一个 PING 命令;   (2)如果一个实例(instance)距离最后一次有效sigusoft PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观 下线;   (3)如果一个 Master 被标记为主观下线,则正在监视这个 Master 的所有 Sentinel 要以每秒一次的频率确认 Master 的确进入了主观下线状态;   (4)当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确 认 Master 的确进入了主观下线状态,则 Master 会被标记为客观下线;   (5)在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有 Master,Slave 发送 INFO 命令;   当 Master 被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次;   (6)若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态 就会被移除;   (7)若 Master 重新向 Sentinel 的 PING 命令返回有效sigusoft, Master 的主观下线 状态就会被移除。   监控( Monitoring ): Redis Sentinel 实时监控主服务器和从服务器运行状态;   自动故障转移:如果一个 master 不正常运行了,哨兵可以启动一个故障转移进程,将 一个 slave 升级成为 master,其他的 slave 被重新配置使用新的 master,并且应用 程序使用 Redis 服务端通知的新地址;   45. 高性能统计UV的方式?   (1)使用redis的set集合   (2)使用redis的bitmap(注意内存消耗)   46. Elasticsearch分片使用优化?   (1)拆分集群   对于存在明显分界线的业务,可以按照业务、地域使用不同集群,这种拆分集群的思路 是非常靠谱的。对于我们的场景,已经按照地域拆分了集群,且同一地域的子业务间分 界线不明显,拆分过多的集群维护成本较高。   (2)调整滚动周期   根据保留时长调整index滚动周期是最简单有效的思路。例如保留3天的数据按天滚动, 保留31天的数据按周滚动,保留一年的数据按月滚动。合理的滚动周期,可以在存储 成本增加不大的情况下,大幅降低分片数量。   对于我们的场景,大部分数据保留31天,在按周滚动的情况下,集群的总分片数可以 下降到6.5w~个。   (3)合理设置分片数和副本数   除个别子业务压力较高外,大部分业务压力较小,合理设置单Index的分片数效果也不 错。我们的经验是单个分片的大小在10GB~30GB之间比较合适,对于压力非常小的业务 可以直接分配1个分片。其他用户可结合具体场景考虑,同时注意单分片的记录条数不 要超过上限2,147,483,519。   在平衡我们的业务场景对数据可靠性的要求 及 不同副本数对存储成本的开销 两个因 素之后,我们选择使用一主一从的副本策略。   目前我们集群单Index的平均分配数为3,集群的总分片数下降到3w~个。   (4)分片分配流程优化   默认情况下,ES在分配分片时会考虑分片relocation对磁盘空间的影响。在分片数较 少时,这个优化处理的副作用不明显。但随着单机分片数量的上升,这个优化处理涉及 的多层循环嵌套过程耗时愈发明显。可通过 cluster.routing.allocation.disk.include_relocations: false关闭此功能,这对 磁盘均衡程度影响不明显。   (5)预创建Index   对于单集群3w分片的场景,集中在每周某天0点创建Index,对集群的压力还是较大, 且存储空间存在波动。考虑到集群的持续扩展能力和可靠性,我们采用预创建方式提前 创建分片,并把按Index的创建时间均匀打散到每周的每一天。   (6)持续调整分片数   对于集群分片的调整,通常不是一蹴而就的。随着业务的发展,不断新增的子业务 或 原 有子业务规模发生突变,都需要持续调整分片数量。   默认情况下,新增的子业务会有默认的分片数量,如果不足,会在测试阶段及上线初期 及时发现。随着业务发展,系统会考虑Index近期的数据量、写入速度、集群规模等因 素,动态调整分片数量。   47. 如何初始化一个指针数组。   首先明确一个概念,就是指向数组的指针,和存放指针的数组。 指向数组的指针:char (*array)[5];含义是一个指向存放5个字符的数组的指针。 存放指针的数组:char *array[5];含义是一个数组中存放了5个指向字符型数据的指针。 按照题意,我理解为初始化一个存放指针的数组,char *array[2]={“China”,”Beijing”};其含义是初始化了一个有两个指向字符型数据的指针的数组,这两个指针分别指向字符串”China”和”Beijing”。   48. 关键字const是什么含意?   我只要一听到被面试者说:“const意味着常数”,我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可 以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)如果应试者能正确回答这 个问题,我将问他一个附加的问题:下面的声明都是什么意思?   const int a;   int const a;   const int *a;   int * const a;   int const * a const;   前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整 型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型 数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由: 1). 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理 其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。) 2). 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 3). 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。   const关键字至少有下列n个作用:   (1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;   (2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;   (3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;   (4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;   (5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。例如:   const classA operator*(const classA& a1,const classA& a2);   operator*的返回结果必须是一个const对象。如果不是,这样的变态代码也不会编译出错:   classA a, b, c;   (a * b) = c; // 对a*b的结果赋值   操作(a * b) = c显然不符合编程者的初衷,也没有任何意义。   49. 什么是动态特性?   在绝大多数情况下, 程序的功能是在编译的时候就确定下来的, 我们称之为静态特性。 反之, 如果程序的功能是在运行时刻才能确定下来的, 则称之为动态特性。C++中, 虚函数,抽象基类, 动态绑定和多态构成了出色的动态特性。   50. 基类的有1个虚函数,子类还需要申明为virtual吗?   不申明没有关系的。 不过,我总是喜欢显式申明,使得代码更加清晰。   51. 在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明?   函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern “C”修饰的变量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。   52. 如何定义Bool变量的TRUE和FALSE的值。   不知道这个题有什么陷阱,写到现在神经已经大了,一般来说先要把TURE和FALSE给定义了,使用#define就可以: #define TURE 1 #define FALSE 0 如果有一个变量需要定义成bool型的,举个例子:bool a=TURE;就可以了。   false/true是标准C++语言里新增的关键字,而FALSE/TRUE是通过#define,这要用途   是解决程序在C与C++中环境的差异,以下是FALSE/TRUE在windef.h的定义:   #ifndef FALSE   #define FALSE 0   #endif   #ifndef TRUE   #define TRUE 1   #endif   也就是说FALSE/TRUE是int类型,而false/true是bool类型;所以两者不一样的,只不过   我们在使用中没有这种感觉,因为C++会帮你做隐式转换。   53. 内联函数INline和宏定义一起使用的区别。   内联函数是在编译的时候已经做好将对应的函数代码替换嵌入到对应的位置,适用于代码较少的函数。 宏定义是简单的替换变量,如果定义的是有参数的函数形式,参数不做类型校验。   54. 编写my_strcpy函数,实现与库函数strcpy类似的功能,不能使用任何库函数.   55. 完成程序,实现对数组的降序排序   56. C中static有什么作用?   (1)隐藏。 当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性,故使用static在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。   (2)static的第二个作用是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量。   (3)static的第三个作用是默认初始化为0.其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0×00,某些时候这一特点可以减少程序员的工作量。   57. 阅读下面代码,回答问题.   void GetMemory(char p, int num){   *p = (char *)malloc(num);   }   void Test(void){   char *str = NULL;   GetMemory(&str, 100);   strcpy(str, “hello”);   printf(str);   }   请问运行Test函数会有什么样的结果?   答案:可以运行   58. delete []arry 和 delete arry 一样吗?不一样请说明;   delete []arry 释放的是多个同一类型的地址空间   delete arry 释放的是一个某种类型的地址空间   59. 阅读下面代码,回答问题.   1) for (i=0; i<n; i++) {   if (condition)   DoSomething();   else   DoOtherthing();   }   2) if (condition) {   for (i=0; i<n; i++)   DoSomething();   }   else   {   for (i=0; i<n; i++)   DoSomething();   }   请简述两个for循环的优缺点?   1)优点:程序简洁。 缺点:多执行了n-1次逻辑判断,并且打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。   2)优点:循环的效率高。缺点:程序不简洁。   60. 如何编写高质量C++代码的建议?   1) extern C的作用是当程序被C++编译器编译时,让后续的链接器以C方式来寻找函数,方便C++程序调用C程序。   2) C++风格注释形如:// ….,推荐使用这样的注释。但是,头文件说明和函数默认参数的注释,还是用C风格(//)的较好。   3) 不要写与编译器依赖紧密的代码   例如:printf(“a %d %d”, p(), q()),p和 q函数的执行前后顺序与编译器实现相关,应当避免此类代码。类似的还有 c = p() * q() * r()。   4) 尽量用const,enum,inline代替#define   inline关键字用在函数调用展开,在类声明中定义并且实现的函数自动为内联函数。如果需要将其他函数定义为内联函数,则需要在函数实现头声明此关键字,才让编译器尝试去内联,至于具体是否内联,还要求函数体满足一定条件才行,总体原则是短小精悍。   在使用define的场合,注意用()来保护宏函数。例如:#define MAX(a, b) ((a) > (b) ? (a) : (b))   5) struct在C和C++下的异同,C语言的struct不允许定义函数程序,而C++语言下的struct可以。   6) 所有数据成员一律为private类型。如果派生类需要用到,那么在用到的时候再将其改为protect类型,否则,一律声明为private类型,对外隐藏。在具体声明时,可以按类型来多段声明,比如私有控件,来一个private,私有数据来另外一个private。   7) 当类中至少包含一个虚函数时,才需要将其析构函数设置为虚函数。不要在构造/析构函数中调用虚函数。   8) 以行为为中心的类设计,对外的public函数放在前面,需要继承的protect虚函数紧随其后,再后面是private的虚函数、普通函数以及成员变量。   9) 语法的背后含义是语义,接口设计要有明确的语义,不可模棱两可、职责不清。   10) 如底层发生异常,则需要逐级上报,直到有能力处理此异常的层级来处理。如果程序都没处理,则会被C++系统捕获并终止程序运行。异常可以将发生错误和处理错误分离。   11) 一般以传值来抛出异常,以 const 引用来捕获异常,不涉及到异常对象的清理工作,无对象切割问题,如本层级处理后还需要继续抛出异常,可调用throw来。   12) 优先使用shared_ptr,它内部工作原理是引用计数,线程安全,支持扩展,推荐使用。

2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/83794.html

(0)
上一篇 2024年 7月 27日 下午11:21
下一篇 2024年 7月 27日

相关推荐

关注微信