一些面试题
发表于 2023-11-10 | | 自我提升

1. Mysql索引在什么情况下会失效

  • 查询条件包含or,可能导致索引失效。
  • 字段类型是字符串,where时一定用引号括起来,否则索引失效。
  • like通配符可能导致索引失效。
  • 联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。
  • 在索引列上使用mysql的内置函数,索引失效。
  • 对索引列运算(如,+-*/),索引失效。
  • 索引字段上使用(!= 或者 < >not in)时,可能会导致索引失效。
  • 索引字段上使用is nullis not null,可能导致索引失效。
  • 左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效。
  • mysql估计使用全表扫描要比使用索引快,则不使用索引。

2. MySql的存储引擎InnoDB与MyISAM的区别

  • InnoDB支持事务,MyISAM不支持事务。
  • InnoDB支持外键,MyISAM不支持外键。
  • InnoDB 支持 MVCC(多版本并发控制),MyISAM 不支持。
  • select count(*) from table时,MyISAM更快,因为它有一个变量保存了整个表的总行数,可以直接读取,InnoDB就需要全表扫描。
  • Innodb不支持全文索引,而MyISAM支持全文索引(5.7以后的InnoDB也支持全文索引)。
  • InnoDB支持表、行级锁,而MyISAM支持表级锁。
  • InnoDB表必须有主键,而MyISAM可以没有主键。
  • Innodb表需要更多的内存和存储,而MyISAM可被压缩,存储空间较小。
  • Innodb按主键大小有序插入,MyISAM记录插入顺序是,按记录插入顺序保存。
  • InnoDB 存储引擎提供了具有提交、回滚、崩溃恢复能力的事务安全,与 MyISAM 比 InnoDB 写的效率差一些,并且会占用更多的磁盘空间以保留数据和索引。

3. mysql在项目中的优化场景,慢查询解决等

面对慢查询,首先想到的就是加索引。一个加了索引的SQL,是怎么执行查找的。还有就是order bygroup by原理,深分页等等,都跟慢查询息息相关。最后就是慢查询的排查解决手段:打开慢查询日志slow_query_log,确认SQL语句是否占用过多资源,用explain查询执行计划、对group byorder byjoin等语句优化,如果数据量实在太大,是否考虑分库分表等等。

4. Mysql有什么索引,索引模型是什么

一般使用都是B+树索引,详细理解的话,可以看这篇文章:MySQL索引底层:B+树详解

5. B-树与B+树的区别?为什么不用红黑树

  • B-树内部节点是保存数据的;而B+树内部节点是不保存数据的,只作索引作用,它的叶子节点才保存数据。
  • B+树相邻的叶子节点之间是通过链表指针连起来的,B-树却不是。
  • 查找过程中,B-树在找到具体的数值以后就结束,而B+树则需要通过索引找到叶子结点中的数据才结束。
  • B-树中任何一个关键字出现且只出现在一个结点中,而B+树可以出现多次。

为什么索引结构默认使用B+树,而不是B-Tree,Hash哈希,二叉树,红黑树?

  • Hash哈希,只适合等值查询,不适合范围查询。
  • 一般二叉树,可能会特殊化为一个链表,相当于全表扫描。
  • 红黑树,是一种特化的平衡二叉树,MySQL 数据量很大的时候,索引的体积也会很大,内存放不下的而从磁盘读取,树的层次太高的话,读取磁盘的次数就多了。
  • B-Tree,叶子节点和非叶子节点都保存数据,相同的数据量,B+树更矮壮,也是就说,相同的数据量,B+树数据结构,查询磁盘的次数会更少。

6. Mysql主从同步怎么做

MySQL主从复制原理:

主库的更新SQL(update、insert、delete)被写到binlog
从库发起连接,连接到主库。
此时主库创建一个binlog dump thread,把binlog的内容发送到从库。
从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log
从库还会创建一个SQL线程,从relay log里面读取内容,从ExecMasterLog_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db

7. 乐观锁与悲观锁的区别?

  • 悲观锁:一个事务拥有(获得)悲观锁后,其他任何事务都不能对数据进行修改。
  • 乐观锁:它认为数据的变动不会太频繁。因此,它允许多个事务同时对数据进行变动。实现方式:乐观锁一般会使用版本号机制或CAS算法实现。

8. 聊聊binlog日志

binlog是归档日志,属于MySQL Server层的日志。可以实现主从复制和数据恢复两个作用。当需要恢复数据时,可以取出某个时间范围内的binlog进行重放恢复即可。

binlog 日志有三种格式,分别是statement,row和mixed。

9. Redis 持久化有哪几种方式,怎么选?

Redis提供了两种持久化方式,RDB和AOF。

9.1 AOF 持久化

AOF(append only file) 持久化,采用日志的形式来记录每个写操作,追加到AOF文件的末尾。

9.2 RDB

RDB,就是把内存数据以快照的形式保存到磁盘上。

9.3 如何选择RDB和AOF

  • 如果数据不能丢失,RDB和AOF混用。
  • 如果只作为缓存使用,可以承受几分钟的数据丢失的话,可以只使用RDB。
  • 如果只使用AOF,优先使用everysec的写回策略。

10. Redis 主从同步是怎样的过程?

Redis主从同步包括三个阶段:

第一阶段:主从库间建立连接、协商同步。

第二阶段:主库把数据同步到从库,从库收到数据后,完成本地加载。

第三阶段,主库把新写的命令,发送到从库。

11. 聊聊Redis的zset,它是怎么实现的?

zset是Redis常用数据类型之一,它的成员是有序排列的,一般用于排行榜类型的业务场景。

它的底层内部编码:ziplist(压缩列表)、skiplist(跳跃表)

12. Redis 过期策略和内存淘汰策略

12.1 Redis的过期策略

一般有定时过期、惰性过期、定期过期三种。

12.2 Redis 内存淘汰策略

  • volatile-lru:当内存不足以容纳新写入数据时,从设置了过期时间的key中使用LRU(最近最少使用)算法进行淘汰;
  • allkeys-lru:当内存不足以容纳新写入数据时,从所有key中使用LRU(最近最少使用)算法进行淘汰。
  • volatile-lfu:4.0版本新增,当内存不足以容纳新写入数据时,在过期的key中,使用LFU(最少访问算法)进行删除key。
  • allkeys-lfu:4.0版本新增,当内存不足以容纳新写入数据时,从所有key中使用LFU算法进行淘汰;
  • volatile-random:当内存不足以容纳新写入数据时,从设置了过期时间的key中,随机淘汰数据;
  • allkeys-random:当内存不足以容纳新写入数据时,从所有key中随机淘汰数据。
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的key中,根据过期时间进行淘汰,越早过期的优先被淘汰;
  • noeviction:默认策略,当内存不足以容纳新写入数据时,新写入操作会报错。

13. Hashmap 是怎样实现的?为什么要用红黑树,而不用平衡二叉树?为什么在1.8中链表大于8时会转红黑树?HashMap是线性安全的嘛?如何保证安全?

13.1 Hashmap 是怎样实现的?

JDK1.7 Hashmap的底层数据结构是数组+链表
JDK1.8 Hashmap的底层数据结构是数组+链表+红黑树

13.2 为什么要用红黑树,为什么不用二叉树?为什么不用平衡二叉树?

红黑树是一种平衡的二叉树,其插入、删除、查找的最坏时间复杂度都为 O(logn),避免了二叉树最坏情况下的O(n)时间复杂度。

13.3 为什么在1.8中链表大于8时会转红黑树?

红黑树的平均查找长度是log(n),如果长度为8,平均查找长度为log(8)=3,链表的平均查找长度为n/2,当长度为8时,平均查找长度为8/2=4,这才有转换成树的必要;链表长度如果是小于等于6,6/2=3,而log(6)=2.6,虽然速度也很快的,但是转化为树结构和生成树的时间并不会太短。

13.4 HashMap是线性安全的嘛?如何保证安全?

HashMap不是线程安全的,多线程下扩容死循环。可以使用HashTable、Collections.synchronizedMap、以及 ConcurrentHashMap 可以实现线程安全。

14. select和epoll的区别

14.1 IO多路复用之select

应用进程通过调用select函数,可以同时监控多个fd,在select函数监控的fd中,只要有任何一个数据状态准备就绪了,select函数就会返回可读状态,这时应用进程再发起recvfrom请求去读取数据。

14.2 IO多路复用之epoll

epoll先通过epoll_ctl()来注册一个fd(文件描述符),一旦基于某个fd就绪时,内核会采用回调机制,迅速激活这个fd,当进程调用epoll_wait()时便得到通知。这里去掉了遍历文件描述符的坑爹操作,而是采用监听事件回调的机制。这就是epoll的亮点。

15. http与https的区别,https的原理,如何加密的?

http与https的区别

HTTP,即超文本传输协议,是一个基于TCP/IP通信协议来传递明文数据的协议。HTTPS= HTTP+SSL/TLS,可以理解Https是身披SSL(Secure Socket Layer,安全套接层)的HTTP。

https的原理,如何加密的

客户端发起Https请求,连接到服务器的443端口。服务器必须要有一套数字证书(证书内容有公钥、证书颁发机构、失效日期等)。服务器将自己的数字证书发送给客户端(公钥在证书里面,私钥由服务器持有)。客户端收到数字证书之后,会验证证书的合法性。如果证书验证通过,就会生成一个随机的对称密钥,用证书的公钥加密。客户端将公钥加密后的密钥发送到服务器。服务器接收到客户端发来的密文密钥之后,用自己之前保留的私钥对其进行非对称解密,解密之后就得到客户端的密钥,然后用客户端密钥对返回数据进行对称加密,酱紫传输的数据都是密文啦。服务器将加密后的密文返回到客户端。客户端收到后,用自己的密钥对其进行对称解密,得到服务器返回的数据。

16. Raft算法原理

Raft 算法是分布式系统开发首选的共识算法,它通过“一切以领导者为准”的方式,实现一系列值的共识和各节点日志的一致。

16.1 Raft 角色

  • 跟随者(Follower)
  • 候选人(Candidate)
  • 领导者(Leader)

16.2 领导选举过程

1.在初始时,集群中所有的节点都是Follower状态,都被设定一个随机选举超时时间(一般150ms-300ms):

  1. 如果Follower在规定的超时时间,都没有收到来自Leader的心跳,它就发起选举:将自己的状态切为 Candidate,增加自己的任期编号,然后向集群中的其它Follower节点发送请求,询问其是否选举自己成为Leader:

其他节点收到候选人A的请求投票消息后,如果在编号为1的这届任期内还没有进行过投票,那么它将把选票投给节点A,并增加自己的任期编号:

当收到来自集群中过半节点的接受投票后,A节点即成为本届任期内 Leader,他将周期性地发送心跳消息,通知其他节点我是Leader,阻止Follower发起新的选举:

16.2 日志复制

当系统leader收到一个来自客户端的写请求,就会添加一个log entry(日志项)到本地日志。Leader通过日志复制(AppendEntries)RPC 消息,将日志项并行复制到集群其它Follower节点。如果Leader接收到大多数的“复制成功”响应后,它将日志项应用到自己的状态机,并返回成功给客户端。相反没有收到大多数的“复制成功”响应,那么就返回错误给客户端;当Follower接收到心跳信息,或者新的AppendEntries消息后,如果发现Leader已经提交了某条日志项,而自己还没应用,那么Follower就会将这条日志项应用到本地的状态机中。

Raft算法,Leader是通过强制Follower直接复制自己的日志项,来处理不一致日志,从而最终实现了集群各节点日志的一致。

17. 消息中间件如何做到高可用

消息中间件如何保证高可用呢?单机是没有高可用可言的,高可用都是对集群来说的,一起看下kafka的高可用吧。

Kafka 的基础集群架构,由多个broker组成,每个broker都是一个节点。当你创建一个topic时,它可以划分为多个partition,而每个partition放一部分数据,分别存在于不同的 broker 上。也就是说,一个 topic 的数据,是分散放在多个机器上的,每个机器就放一部分数据。

18. 消息队列怎么保证不丢消息的

一个消息从生产者产生,到被消费者消费,主要经过这3个过程:生产者保证不丢消息、存储端不丢消息、消费者不丢消息。

19. Redis如何保证高可用?聊聊Redis的哨兵机制

主从模式中,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址。显然,多数业务场景都不能接受这种故障处理方式。Redis从2.8开始正式提供了Redis Sentinel(哨兵)架构来解决这个问题。

20. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

这道题可以使用滑动窗口来实现。滑动窗口就是维护一个窗口,不断滑动,然后更新答案。

滑动窗口的大致逻辑框架,伪代码如下:

int left =0,right = 0;
while (right < s.size()){
    //增大窗口
    window.add(s[right]);
    right++;

    while (window needs shrink){
        //缩小窗口
        window.remove (s[left]);
        left ++;
    }
}

解法流程如下:

首先呢,就是获取原字符串的长度。
接着维护一个窗口(数组、哈希、队列)
窗口一步一步向右扩展
窗口在向右扩展滑动过程,需要判断左边是否需要缩减
最后比较更新答案
完整代码如下:

int lengthOfLongestSubstring(String s){
//获取原字符串的长度
int len = s.length();
//维护一个哈希集合的窗口
Set windows = new HashSet<>();
int left=0,right =0;
int res =0;

while(right<len){
   char c = s.charAt(right);
   //窗口右移
   right++;

   //判断是否左边窗口需要缩减,如果已经包含,那就需要缩减
    while(windows.contains(c)){
        windows.remove(s.charAt(left));
        left++;
   }
   windows.add(c);
   //比较更新答案
   res = Math.max(res,windows.size());
  }
  return res;
}

之前写过一篇滑动窗口解析,大家有兴趣可以看下哈:

leetcode必备算法:聊聊滑动窗口

参考与感谢
分布式理论之分布式一致性:Raft算法原理[2]、一文搞懂Raft算法[3]

参考资料
[1]分布式一致性:Raft算法原理: https://www.tpvlog.com/article/66

[2]分布式理论之分布式一致性:Raft算法原理: https://www.tpvlog.com/article/66

[3]一文搞懂Raft算法: https://www.cnblogs.com/xybaby/p/10124083.html

发表评论:

TOP