图灵300题-81~100-笔记005

图灵300题

图灵面试题视频:https://www.bilibili.com/video/BV17z421B7rB?spm_id_from=333.788.videopod.episodes&vd_source=be7914db0accdc2315623a7ad0709b85&p=20。
本文是学习笔记,如果需要面试没有时间阅读原博文,可以快速浏览笔记。
推荐深度阅读对应书籍或者知识点原文,避免碎片化学习。

81. 最左前缀原则介绍

当一个SQL想要利用索引时,就一定要提供该索引所对应的字段中最左边的字段,也就是排在最前面的字段。比如针对abc三个字段建立了一个联合索引,那么在写一个SQL时就一定要提供a字段的条件,这样才能用到联合索引。这是由于在建立abc三个字段的联合索引时,底层的B+树是按照abc三个字段从左往右去比较大小进行排序的,所以如果想要利用B+树进行快速查找也得符合这个规则。

82. Innodb实现事务的方式

Innodb通过Buffer PoolLogBufferRedo LogUndo Log来实现事务。以一个update语句为例:

  1. Innodb在收到一个update语句后,会先根据条件找到数据所在的页,并将该页缓存在Buffer Pool中。
  2. 执行update语句,修改Buffer Pool中的数据,也就是内存中的数据。
  3. 针对update语句生成一个RedoLog对象,并存入LogBuffer中。
  4. 针对update语句生成undolog日志,用于事务回滚。
  5. 如果事务提交,那么则把RedoLog对象进行持久化,后续还有其他机制将Buffer Pool中所修改的数据页持久化到磁盘中。
  6. 如果事务回滚,则利用undolog日志进行回滚。

83. B树与B+树的区别

  • B树的特点
  1. 节点支持排序,节点之间有指针,可较好地支持范围查找等操作。
  2. 一个节点可以存放多个元素,且这些元素也进行了排序 。
  • B+树的特点
  1. 拥有B树的特点。
  2. 叶子节点之间有指针,并且叶子节点上存储了所有数据元素,且元素是排好顺序的。
  • MySQL使用B+树的原因
    MySQL索引用于加快查询速度,B+树对数据进行了排序,这有助于提高查询速度。同时,B+树节点可存储多个元素,能使树的高度不会过高。在MySQL中,一个InnoDB页就是一个B+树节点,InnoDB页默认大小为16KB ,一般情况下一两层的B+树就可以存储2000万左右的数据。此外,通过利用B+树叶子节点存储所有数据且有序这一特性,SQL语句能很好地支持全表扫描、范围查找等操作。

84. MySQL的锁分类及间隙锁介绍

从锁的粒度来区分:

  1. 行锁:加锁粒度小,但是加锁资源开销比较大。InnDB支持。
    • 共享锁:读锁。多个事务可以对同一个数据共享同一把锁。持有锁的事务都可以访问数据,但是只能读不能修改。使用select xxx LOCK IN SHARE MODE语句添加。
    • 排他锁:写锁。只有一个事务能够获得排他锁,其他事务都不能获取该行的锁。InnoDB会对updatedeleteinsert语句自动添加排他锁,也可使用SELECT xxx FOR UPDATE手动添加。
    • 自增锁:通常是针对MySQL当中的自增字段。如果有事务回滚这种情况,数据会回滚,但是自增序列不会回滚。
  2. 表锁:加锁粒度大,加锁资源开销比较小。MyISAM和InnoDB都支持。
    • 表共享读锁
    • 表排他写锁
    • 意向锁:是InnoDB自动添加的一种锁,不需要用户干预。
  3. 全局锁:使用Flush tables with read lock语句添加。加锁之后整个数据库实例都处于只读状态,所有的数据变更操作都会被挂起,一般用于全库备份的时候。

常见的锁算法:

  1. 记录锁:锁一条具体的数据。
  2. 间隙锁:RR隔离级别下,会加间隙锁。锁一定的范围,而不锁具体的记录,是为了防止产生幻读,例如(-∞, 1)(1, 4)(4, 9)(9, +∞)这些区间。
  3. Next-key:间隙锁 + 右记录锁,例如(-∞,1](1,4](4,9](9, +∞]

85. MySQL慢查询的优化方法

  1. 检查是否走了索引,如果没有则优化SQL利用索引。
  2. 检查所利用的索引,是否是最优索引。
  3. 检查所查字段是否都是必须的,是否查询了过多字段,查出了多余数据。
  4. 检查表中数据是否过多,是否应该进行分库分表了。
  5. 检查数据库实例所在机器的性能配置,是否太低,是否可以适当增加资源。

86. RDB和AOF机制详解

RDB:即Redis DataBase,在指定的时间间隔内将内存中的数据集快照写入磁盘。实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

  • 优点
    • 整个Redis数据库将只包含一个文件dump.rdb,方便持久化。
    • 容灾性好,方便备份。
    • 性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是IO最大化。使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis的高性能。
    • 相对于数据集大时,比AOF的启动效率更高。
  • 缺点
    • 数据安全性低。RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候。
    • 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

AOF:即Append Only File,以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。

  • 优点
    • 数据安全,Redis中提供了3种同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。
    • 通过append模式写文件,即使中途服务器宕机也不会破坏已经存在的内容,可以通过redis-check-aof工具解决数据一致性问题。
    • AOF机制的rewrite模式,定期对AOF文件进行重写,以达到压缩的目的。
  • 缺点
    • AOF文件比RDB文件大,且恢复速度慢。
    • 数据集大的时候,比rdb启动效率低。
    • 运行效率没有RDB高。

AOF文件比RDB更新频率高,优先使用AOF还原数据。AOF比RDB更安全也更大,RDB性能比AOF好,如果两个都配了优先加载AOF。

87. Redis过期键的删除策略

Redis是key-value数据库,可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。

  • 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
  • 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)

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

88. Redis线程模型及单线程快的原因

Redis基于Reactor模式开发了网络事件处理器,这个处理器叫做文件事件处理器file event handler。这个文件事件处理器是单线程的,所以Redis才叫做单线程的模型,它采用IO多路复用机制来同时监听多个Socket,根据Socket上的事件类型来选择对应的事件处理器来处理这个事件。可以实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了Redis内部的线程模型的简单性。

文件事件处理器的结构包含4个部分:多个Socket、IO多路复用程序、文件事件分派器以及事件处理器(命令请求处理器、命令回复处理器、连接应答处理器等)。

多个Socket可能并发的产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用程序会监听多个Socket,会将Socket放入一个队列中排队,每次从队列中取出一个Socket给事件分派器,事件分派器把Socket给对应的事件处理器。然后一个Socket的事件处理完之后,IO多路复用程序才会将队列中的下一个Socket给事件分派器。文件事件分派器会根据每个Socket当前产生的事件,来选择对应的事件处理器来处理。

单线程快的原因:

  1. 纯内存操作。
  2. 核心是基于非阻塞的IO多路复用机制。
  3. 单线程反而避免了多线程的频繁上下文切换带来的性能问题。

89. Redis事务实现简述

  1. 事务开始MULTI命令的执行,标识着一个事务的开始。MULTI命令会将客户端状态的flags属性中打开REDIS_MULTI标识来完成。
  2. 命令入队:当一个客户端切换到事务状态之后,服务器会根据这个客户端发送来的命令来执行不同的操作。如果客户端发送的命令为MULTIEXECWATCHDISCARD中的一个,立即执行这个命令,否则将命令放入一个事务队列里面,然后向客户端返回QUEUED回复。
    • 如果客户端发送的命令为EXECDISCARDWATCHMULTI四个命令的其中一个,那么服务器立即执行这个命令。
    • 如果客户端发送的是四个命令以外的其他命令,那么服务器并不立即执行这个命令。首先检查此命令的格式是否正确,如果不正确,服务器会在客户端状态(redisClient)的flags属性关闭REDIS_MULTI标识,并且返回错误信息给客户端。如果正确,将这个命令放入一个事务队列里面,然后向客户端返回QUEUED回复。事务队列是按照FIFO的方式保存入队的命令。
  3. 事务执行:客户端发送EXEC命令,服务器执行EXEC命令逻辑。
    • 如果客户端状态的flags属性不包含REDIS_MULTI标识,或者包含REDIS_DIRTY_CAS或者REDIS_DIRTY_EXEC标识,那么就直接取消事务的执行。
    • 否则客户端处于事务状态(flagsREDIS_MULTI标识),服务器会遍历客户端的事务队列,然后执行事务队列中的所有命令,最后将返回结果全部返回给客户端。

redis不支持事务回滚机制,但是它会检查每一个事务中的命令是否错误。Redis事务不支持检查那些程序员自己逻辑错误,例如对String类型的数据库键执行对HashMap类型的操作。

WATCH命令是一个乐观锁,可以为Redis事务提供check-and-set (CAS)行为。可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。

MULTI命令用于开启一个事务,它总是返回OKMULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。

EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值nil

通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务,并且客户端会从事务状态中退出。

UNWATCH命令可以取消watch对所有key的监控。

90. Redis主从复制的核心原理

通过执行slaveof命令或设置slaveof选项,让一个服务器去复制另一个服务器的数据。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。

全量复制

  1. 主节点通过bgsave命令fork子进程进行RDB持久化,该过程是非常消耗CPU、内存(页表复制)、硬盘IO的。
  2. 主节点通过网络将RDB文件发送给从节点,对主从节点的带宽都会带来很大的消耗。
  3. 从节点清空老数据、载入新RDB文件的过程是阻塞的,无法响应客户端的命令;如果从节点执行bgrewriteaof,也会带来额外的消耗。

部分复制

  1. 复制偏移量:执行复制的双方,主从节点,分别会维护一个复制偏移量offset
  2. 复制积压缓冲区:主节点内部维护了一个固定长度的、先进先出(FIFO)队列作为复制积压缓冲区,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。
  3. 服务器运行ID(runid):每个Redis节点,都有其运行ID,运行ID由节点在启动时自动生成,主节点会将自己的运行ID发送给从节点,从节点会将主节点的运行ID存起来。从节点Redis断开重连的时候,就是根据运行ID来判断同步的进度:
    • 如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况)。
    • 如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制。

从服务器收到slaveof命令,第一次执行复制时,向master发送psync {runid} {offset}runid是上一次主节点的运行ID,offset是当前从节点的复制偏移量),如果master返回continue则执行增量同步,如果master返回fullresync {runid} {offset}runid表示主节点的运行ID,offset表示当前主节点的复制偏移量)则执行全量同步。

91. Redis的数据结构及使用场景

Redis的数据结构有:

  1. 字符串:可以用来做最简单的数据缓存,可以缓存某个简单的字符串,也可以缓存某个json格式的字符串。Redis分布式锁的实现就利用了这种数据结构,还包括可以实现计数器、Session共享、分布式ID。
  2. 哈希表:可以用来存储一些key-value对,更适合用来存储对象。
  3. 列表:Redis的列表通过命令的组合,既可以当做栈,也可以当做队列来使用,可以用来缓存类似微信公众号、微博等消息流数据。
  4. 集合:和列表类似,也可以存储多个元素,但是不能重复,集合可以进行交集、并集、差集操作,从而可以实现类似我和某人共同关注的人、朋友圈点赞等功能。
  5. 有序集合:集合是无序的,有序集合可以设置顺序,可以用来实现排行榜功能。

92. Redis分布式锁底层实现方式

  1. 首先利用setnx来保证:如果key不存在才能获取到锁,如果key存在,则获取不到锁。
  2. 然后还要利用lua脚本来保证多个redis操作的原子性。
  3. 同时还要考虑到锁过期,所以需要额外的一个看门狗定时任务来监听锁是否需要续约。
  4. 同时还要考虑到redis节点挂掉后的情况,所以需要采用红锁的方式来同时向N/2 + 1个节点申请锁,都申请到了才证明获取锁成功,这样就算其中某个redis节点挂掉了,锁也不能被其他客户端获取到。

93. Redis集群策略介绍

Redis提供了三种集群策略:

  1. 主从模式:这种模式比较简单,主库可以读写,并且会和从库进行数据同步。这种模式下,客户端直接连主库或某个从库,但是当主库或从库宕机后,客户端需要手动修改IP。另外,这种模式也比较难进行扩容,整个集群所能存储的数据受到某台机器的内存容量限制,所以不可能支持特大数据量。
  2. 哨兵模式:这种模式在主从的基础上新增了哨兵节点,当主库节点宕机后,哨兵会发现主库节点宕机,然后在从库中选择一个库作为新的主库。另外哨兵也可以做集群,从而可以保证当某一个哨兵节点宕机后,还有其他哨兵节点可以继续工作。这种模式可以比较好的保证Redis集群的高可用,但是仍然不能很好的解决Redis的容量上限问题。
  3. Cluster模式Cluster模式是用得比较多的模式,它支持多主多从。这种模式会按照key进行槽位的分配,可以使得不同的key分散到不同的主节点上,利用这种模式可以使得整个集群支持更大的数据容量。同时每个主节点可以拥有自己的多个从节点,如果该主节点宕机,会从它的从节点中选举一个新的主节点。

94. 缓存穿透、缓存击穿、缓存雪崩的概念及解决方案

缓存中存放的大多都是热点数据,目的是防止请求直接访问MySQL,而是先从缓存中获取数据。

  1. 缓存雪崩:如果缓存中某一时刻大批热点数据同时过期,那么就可能导致大量请求直接访问MySQL。解决办法是在过期时间上增加一点随机值,另外搭建一个高可用的Redis集群也是防止缓存雪崩的有效手段。
  2. 缓存击穿:和缓存雪崩类似,缓存雪崩是大批热点数据失效,而缓存击穿是指某一个热点key突然失效,也导致了大量请求直接访问MySQL数据库。解决方案是考虑这个热点key不设过期时间。
  3. 缓存穿透:假如某一时刻访问redis的大量key都在redis中不存在(比如黑客故意伪造一些乱七八糟的key),那么也会给数据库造成压力,这就是缓存穿透。解决方案是使用布隆过滤器,它的作用是如果它认为一个key不存在,那么这个key就肯定不存在,所以可以在缓存之前加一层布隆过滤器来拦截不存在的key

95. Redis和Mysql保证数据一致的方法

  1. 先更新Mysql,再更新Redis:如果更新Redis失败,可能仍然会出现数据不一致的情况。
  2. 先删除Redis缓存数据,再更新Mysql:再次查询的时候再将数据添加到缓存中。这种方案能解决第一种方案的一些问题,但是在高并发下性能较低,而且仍然会出现数据不一致的问题。比如线程1删除了Redis缓存数据,正在更新Mysql,此时另外一个查询线程进行查询,那么就会把Mysql中老数据又查到Redis中。
  3. 延时双删:步骤是先删除Redis缓存数据,再更新Mysql,延迟几百毫秒再删除Redis缓存数据。这样就算在更新Mysql时,有其他线程读了Mysql,把老数据读到了Redis中,那么也会被第二次删除掉,从而保证数据一致。

96. Redis单线程快的原因(原视频问题重复)

Redis基于Reactor模式开发了网络事件处理器、文件事件处理器file event handler。它是单线程的,所以Redis才叫做单线程的模型,它采用IO多路复用机制来同时监听多个Socket,根据Socket上的事件类型来选择对应的事件处理器来处理这个事件。可以实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了Redis内部的线程模型的简单性。

文件事件处理器的结构包含4个部分:多个Socket、IO多路复用程序、文件事件分派器以及事件处理器(命令请求处理器、命令回复处理器、连接应答处理器等)。

多个Socket可能并发的产生不同的事件,IO多路复用程序会监听多个Socket,会将Socket放入一个队列中排队,每次从队列中有序、同步取出一个Socket给事件分派器,事件分派器把Socket给对应的事件处理器。然后一个Socket的事件处理完之后,IO多路复用程序才会将队列中的下一个Socket给事件分派器。文件事件分派器会根据每个Socket当前产生的事件,来选择对应的事件处理器来处理。

  1. Redis启动初始化时,将连接应答处理器跟AE_READABLE事件关联。
  2. 若一个客户端发起连接,会产生一个AE_READABLE事件,然后由连接应答处理器负责和客户端建立连接,创建客户端对应的socket,同时将这个socketAE_READABLE事件和命令请求处理器关联,使得客户端可以向主服务器发送命令请求。
  3. 当客户端向Redis发请求时(不管读还是写请求),客户端socket都会产生一个AE_READABLE事件,触发命令请求处理器。处理器读取客户端的命令内容,然后传给相关程序执行。
  4. 当Redis服务器准备好给客户端的响应数据后,会将socketAE_WRITABLE事件和命令回复处理器关联,当客户端准备好读取响应数据时,会在socket产生一个AE_WRITABLE事件,由对应命令回复处理器处理,即将准备好的响应数据写入socket,供客户端读取。
  5. 命令回复处理器全部写完到socket后,就会删除该socketAE_WRITABLE事件和命令回复处理器的映射。

单线程快的原因:

  1. 纯内存操作。
  2. 核心是基于非阻塞的IO多路复用机制。
  3. 单线程反而避免了多线程的频繁上下文切换带来的性能问题。

97. CAP理论介绍

CAP理论是分布式领域中非常重要的一个指导理论,C(Consistency)表示强一致性,A(Availability)表示可用性,P(Partition Tolerance)表示分区容错性。CAP理论指出在目前的硬件条件下,一个分布式系统是必须要保证分区容错性的,而在这个前提下,分布式系统要么保证CP,要么保证AP,无法同时保证CAP

分区容错性表示,一个系统虽然是分布式的,但是对外看上去应该是一个整体,不能由于分布式系统内部的某个结点挂点,或网络出现了故障,而导致系统对外出现异常。所以,对于分布式系统而言是一定要保证分区容错性的。

强一致性表示,一个分布式系统中各个结点之间能及时的同步数据,在数据同步过程中,是不能对外提供服务的,不然就会造成数据不一致,所以强一致性和可用性是不能同时满足的。

可用性表示,一个分布式系统对外要保证可用。

98. BASE理论

  1. BASE理论概述
    由于无法同时满足CAP理论,因此出现了BASE理论。
  2. B - Basically Available(基本可用)
    表示基本可用,意味着允许一定程度的不可用。比如由于系统故障、请求时长变长,或者因系统故障导致部分非核心功能不可用等情况,都是基本可用的体现。
  3. A - Soft state(软状态)
    表示一种中间状态,例如数据正在同步过程中时所处的状态。
  4. E - Eventually consistent(最终一致性)
    表示最终一致性,不要求分布式系统数据实时达到一致,允许经过一段时间后再达到一致。在达到一致的过程中,系统处于软状态 。

99. RPC介绍

RPC,表示远程过程调用,对于Java这种面向对象语言,也可以理解为远程方法调用。RPC调用和HTTP调用是有区别的,RPC表示的是一种调用远程方法的方式,可以使用HTTP协议、或直接基于TCP协议来实现RPC。在Java中,可以通过直接使用某个服务接口的代理对象来执行方法,而底层则通过构造HTTP请求来调用远端的方法。所以,有一种说法是RPC协议是HTTP协议之上的一种协议,也是可以理解的。

100. 数据一致性模型介绍

  1. 强一致性:当更新操作完成之后,任何多个后续进程的访问都会返回最新的更新过的值,这种是对用户最友好的,就是用户上一次写什么,下一次就保证能读到什么。根据CAP理论,这种实现需要牺牲可用性。
  2. 弱一致性:系统在数据写入成功之后,不承诺立即可以读到最新写入的值,也不会具体的承诺多久之后可以读到。用户读到某一操作对系统数据的更新需要一段时间,称这段时间为“不一致性窗口”。
  3. 最终一致性:最终一致性是弱一致性的特例,强调的是所有的数据副本,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。到达最终一致性的时间,就是不一致窗口时间,在没有故障发生的前提下,不一致窗口的时间主要受通信延迟、系统负载和复制副本的个数影响。最终一致性模型根据其提供的不同保证可以划分为更多的模型,包括因果一致性和会话一致性等。
  4. 因果一致性:要求有因果关系的操作顺序得到保证,非因果关系的操作顺序则无所谓。进程A在更新完某个数据项后通知了进程B,那么进程B之后对该数据项的访问都应该能够获取到进程A更新后的最新值,并且如果进程B要对该数据项进行更新操作的话,务必基于进程A更新后的最新值。例如在微博或者微信进行评论的时候,比如你在朋友圈发了一张照片,朋友给你评论了,而你对朋友的评论进行了回复,这条朋友圈的显示中,你的回复必须在朋友之后,这是一个因果关系,而其他没有因果关系的数据,可以允许不一致。

你可能感兴趣的:(面试笔记,笔记,java,面试)