社招后端21连问(三年工作经验一面)

有位朋友工作三年,去面试,给大家整理一下面试题,并附上答案。

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

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

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

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

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

  6. Mysql主从同步怎么做

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

  8. 聊聊binlog日志

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

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

    扫描二维码关注公众号,回复: 14387141 查看本文章
  11. redis 的 zset 怎么实现的?

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

  13. Hashmap实现原理

  14. select 和 epoll的区别

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

  16. Raft算法原理

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

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

  19. 聊聊Redis的哨兵机制

  20. 算法题:无重复字符的最长子

正文:

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

  • 查询条件包含or,可能导致索引失效

  • 如何字段类型是字符串,where时一定用引号括起来,否则索引失效

  • like通配符可能导致索引失效。

  • 联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。

  • 在索引列上使用mysql的内置函数,索引失效。

  • 对索引列运算(如,+、-、*、/),索引失效。

  • 索引字段上使用(!= 或者 < >,not in)时,可能会导致索引失效。

  • 索引字段上使用is null, is 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 by,group by原理,深分页等等,都跟慢查询息息相关

最后就是慢查询的排查解决手段:

打开慢查询日志slow_query_log,确认SQL语句是否占用过多资源,用explain查询执行计划、对group by、order by、join等语句优化,如果数据量实在太大,是否考虑分库分表等等。

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

数据结构维度来讲的话,一般使用都是B+树索引

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

B-树与B+树的区别:

  • B-树内部节点是保存数据的;而B+树内部节点是不保存数据的,只作索引作用,它的叶子节点才保存数据。

  • B+树相邻的叶子节点之间是通过链表指针连起来的,B-树却不是。

  • 查找过程中,B-树在找到具体的数值以后就结束,而B+树则需要通过索引找到叶子结点中的数据才结束

  • B-树中任何一个关键字出现且只出现在一个结点中,而B+树可以出现多次。

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

  • Hash哈希,只适合等值查询,不适合范围查询。

  • 一般二叉树,可能会特殊化为一个链表,相当于全表扫描。

  • 红黑树,是一种特化的平衡二叉树,MySQL 数据量很大的时候,索引的体积也会很大,内存放不下的而从磁盘读取,树的层次太高的话,读取磁盘的次数就多了。

  • B-Tree,叶子节点和非叶子节点都保存数据,相同的数据量,B+树更矮壮,也是就说,相同的数据量,B+树数据结构,查询磁盘的次数会更少。

6. Mysql主从同步怎么做

大家要熟悉MySQL主从复制原理哈:

详细的主从复制过程如图:

上图主从复制过程分了五个步骤进行:

  1. 主库的更新SQL(update、insert、delete)被写到binlog

  2. 从库发起连接,连接到主库。

  3. 此时主库创建一个binlog dump thread,把binlog的内容发送到从库。

  4. 从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log

  5. 从库还会创建一个SQL线程,从relay log里面读取内容,从ExecMasterLog_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db

主从同步这块呢,还涉及到如何保证主从一致的数据库主从延迟的原因与解决方案数据库的高可用方案

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

悲观锁:

悲观锁她专一且缺乏安全感了,她的心只属于当前事务,每时每刻都担心着它心爱的数据可能被别的事务修改,所以一个事务拥有(获得)悲观锁后,其他任何事务都不能对数据进行修改啦,只能等待锁被释放才可以执行。

select ...for update就是悲观锁一种实现。

乐观锁:

乐观锁的“乐观情绪”体现在,它认为数据的变动不会太频繁。因此,它允许多个事务同时对数据进行变动。实现方式:乐观锁一般会使用版本号机制或CAS算法实现。

之前用乐观锁解决过实战的并发问题

8. 聊聊binlog日志

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

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

如果是statement格式,binlog记录的是SQL的原文,他可能会导致主库不一致(主库和从库选的索引不一样时)。我们来分析一下。假设主库执行删除这个SQL(其中a和create_time都有索引)如下:

delete from t where a > '666' and create_time<'2022-03-01' limit 1;

我们知道,数据选择了a索引和选择create_time索引,最后limit 1出来的数据一般是不一样的。所以就会存在这种情况:在binlog = statement格式时,主库在执行这条SQL时,使用的是索引a,而从库在执行这条SQL时,使用了索引create_time。最后主从数据不一致了。

如何解决这个问题呢?

可以把binlog格式修改为rowrow格式的binlog日志,记录的不是SQL原文,而是两个event:Table_map 和 Delete_rows。Table_map event说明要操作的表,Delete_rows event用于定义要删除的行为,记录删除的具体行数。row格式的binlog记录的就是要删除的主键ID信息,因此不会出现主从不一致的问题。

但是如果SQL删除10万行数据,使用row格式就会很占空间的,10万条数据都在binlog里面,写binlog的时候也很耗IO。但是statement格式的binlog可能会导致数据不一致,因此设计MySQL的大叔想了一个折中的方案,mixed格式的binlog。所谓的mixed格式其实就是rowstatement格式混合使用,当MySQL判断可能数据不一致时,就用row格式,否则使用就用statement格式。

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

既然它是基于内存的,如果Redis服务器挂了,数据就会丢失。为了避免数据丢失了,Redis提供了两种持久化方式,RDB和AOF

9.1 AOF 持久化

AOF(append only file) 持久化,采用日志的形式来记录每个写操作,追加到AOF文件的末尾。Redis默认情况是不开启AOF的。重启时再重新执行AOF文件中的命令来恢复数据。它主要解决数据持久化的实时性问题。

AOF是执行完命令后才记录日志的。为什么不先记录日志再执行命令呢?这是因为Redis在向AOF记录日志时,不会先对这些命令进行语法检查,如果先记录日志再执行命令,日志中可能记录了错误的命令,Redis使用日志回复数据时,可能会出错。

正是因为执行完命令后才记录日志,所以不会阻塞当前的写操作。但是会存在两个风险

  1. 更执行完命令还没记录日志时,宕机了会导致数据丢失

  2. AOF不会阻塞当前命令,但是可能会阻塞下一个操作。

这两个风险最好的解决方案是折中妙用AOF机制的三种写回策略 appendfsync

  1. always,同步写回,每个子命令执行完,都立即将日志写回磁盘。

  2. everysec,每个命令执行完,只是先把日志写到AOF内存缓冲区,每隔一秒同步到磁盘。

  3. no:只是先把日志写到AOF内存缓冲区,有操作系统去决定何时写入磁盘。

always同步写回,可以基本保证数据不丢失,no策略则性能高但是数据可能会丢失,一般可以考虑折中选择everysec

如果接受的命令越来越多,AOF文件也会越来越大,文件过大还是会带来性能问题。日志文件过大怎么办呢?AOF重写机制!就是随着时间推移,AOF文件会有一些冗余的命令如:无效命令、过期数据的命令等等,AOF重写机制就是把它们合并为一个命令(类似批处理命令),从而达到精简压缩空间的目的。

AOF重写会阻塞嘛?AOF日志是由主线程会写的,而重写则不一样,重写过程是由后台子进程bgrewriteaof完成。

  • AOF的优点:数据的一致性和完整性更高,秒级数据丢失。

  • 缺点:相同的数据集,AOF文件体积大于RDB文件。数据恢复也比较慢。

9.2 RDB

因为AOF持久化方式,如果操作日志非常多的话,Redis恢复就很慢。有没有在宕机快速恢复的方法呢,有的,RDB!

RDB,就是把内存数据以快照的形式保存到磁盘上。和AOF相比,它记录的是某一时刻的数据,,并不是操作。

什么是快照?可以这样理解,给当前时刻的数据,拍一张照片,然后保存下来。

RDB持久化,是指在指定的时间间隔内,执行指定次数的写操作,将内存中的数据集快照写入磁盘中,它是Redis默认的持久化方式。执行完操作后,在指定目录下会生成一个dump.rdb文件,Redis 重启的时候,通过加载dump.rdb文件来恢复数据。RDB触发机制主要有以下几种:

RDB通过bgsave命令的执行全量快照,可以避免阻塞主线程。basave命令会fork一个子进程,然后该子进程会负责创建RDB文件,而服务器进程会继续处理命令请求

快照时,数据能修改嘛?  Redis接住操作系统的写时复制技术(copy-on-write,COW),在执行快照的同时,正常处理写操作。

虽然bgsave执行不会阻塞主线程,但是频繁执行全量快照也会带来性能开销。比如bgsave子进程需要通过fork操作从主线程创建出来,创建后不会阻塞主线程,但是创建过程是会阻塞主线程的。可以做增量快照

  • RDB的优点:与AOF相比,恢复大数据集的时候会更快,它适合大规模的数据恢复场景,如备份,全量复制等

  • 缺点:没办法做到实时持久化/秒级持久化。

Redis4.0开始支持RDB和AOF的混合持久化,就是内存快照以一定频率执行,两次快照之间,再使用AOF记录这期间的所有命令操作。

9.3 如何选择RDB和AOF

  • 如果数据不能丢失,RDB和AOF混用

  • 如果只作为缓存使用,可以承受几分钟的数据丢失的话,可以只使用RDB。

  • 如果只使用AOF,优先使用everysec的写回策略。

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

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

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

  • 从库向主库发送psync 命令,告诉它要进行数据同步。

  • 主库收到 psync 命令后,响应FULLRESYNC命令(它表示第一次复制采用的是全量复制),并带上主库runID和主库目前的复制进度offset

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

  • 主库执行bgsave命令,生成RDB文件,接着将文件发给从库。从库接收到RDB 文件后,会先清空当前数据库,然后加载 RDB 文件。

  • 主库把数据同步到从库的过程中,新来的写操作,会记录到replication buffer

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

  • 主库完成RDB发送后,会把replication buffer中的修改操作发给从库,从库再重新执行这些操作。这样主从库就实现同步啦。

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

zset是Redis常用数据类型之一,它的成员是有序排列的,一般用于排行榜类型的业务场景,比如 QQ 音乐排行榜、礼物排行榜等等。

  • 它的简单格式举例:zadd key score member [score member ...],zrank key member

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

当 zset 满足以下条件时使用压缩列表

  • 当成员的数量小于128 个;

  • 每个 member (成员)的字符串长度都小于 64 个字节。

压缩列表做简单介绍,它由以下五部分组成

  • zlbytes 是一个无符号整数,表示当前ziplist占用的总字节数;

  • zltail 指的是压缩列表尾部元素相对于压缩列表起始元素的偏移量。

  • zllen 指 ziplist 中 entry 的数量。当 zllen 比2^16 - 2大时,需要完全遍历 entry 列表来获取 entry 的总数目。

  • entry 用来存放具体的数据项(score和member),长度不定,可以是字节数组或整数,entry 会根据成员的数量自动扩容。-zlend 是一个单字节的特殊值,等于 255,起到标识 ziplist 内存结束点的作用。

skiplist(跳跃表)在链表的基础上,增加了多级索引,通过索引位置的几个跳转,实现数据的快速定位,其插入、删除、查找的时间复杂度均为 O(logN)。

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

12.1 Redis的过期策略

我们在set key的时候,可以给它设置一个过期时间,比如expire key 60。指定这key60s后过期,60s后,redis是如何处理的嘛?我们先来介绍几种过期策略哈:

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

  • 定时过期

每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即对key进行清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。

  • 惰性过期

只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

  • 定期过期

每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。

Redis中同时使用了惰性过期和定期过期两种过期策略。

  • 假设Redis当前存放30万个key,并且都设置了过期时间,如果你每隔100ms就去检查这全部的key,CPU负载会特别高,最后可能会挂掉。

  • 因此,redis采取的是定期过期,每隔100ms就随机抽取一定数量的key来检查和删除的。

  • 但是呢,最后可能会有很多已经过期的key没被删除。这时候,redis采用惰性删除。在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间并且已经过期了,此时就会删除。

但是呀,如果定期删除漏掉了很多过期的key,然后也没走惰性删除。就会有很多过期key积在内存内存,直接会导致内存爆的。或者有些时候,业务量大起来了,redis的key被大量使用,内存直接不够了,运维小哥哥也忘记加大内存了。难道redis直接这样挂掉?不会的!Redis用8种内存淘汰策略保护自己~

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:默认策略,当内存不足以容纳新写入数据时,新写入操作会报错。


  • ....博主太懒了字数太多了,不想写了....文章已经做成PDF,有需要的朋友可以私信我免费获取!

猜你喜欢

转载自blog.csdn.net/weixin_70730532/article/details/125930676