面试整理

ZooKeeper

  1. ZooKeeper提供的一致性服务:

ZooKeeper从以下几点保证了数据的一致性。
顺序一致性:来自任意特定客户端的更新都会按其发送顺序被提交保持一致。也就是说,如果一个客户端将Znode z的值更新为a,在之后的操作中,它又将z的值更新为b,则没有客户端能够在看到z的值是b之后再看到值a(如果没有其他对z的更新)。
原子性:每个更新要么成功,要么失败。这意味着如果一个更新失败,则不会有客户端会看到这个更新的结果。
单一系统映像:一个客户端无论连接到哪一台服务器,它看到的都是同样的系统视图。这意味着,如果一个客户端在同一个会话中连接到一台新的服务器,它所看到的系统状态不会比 在之前服务器上所看到的更老。当一台服务器出现故障,导致它的一个客户端需要尝试连接集合体中其他的服务器时,所有滞后于故障服务器的服务器都不会接受该 连接请求,除非这些服务器赶上故障服务器。
持久性:一个更新一旦成功,其结果就会持久存在并且不会被撤销。这表明更新不会受到服务器故障的影响。
实时性:在特定的一段时间内,客户端看到的系统需要被保证是实时的(在十几秒的时间里)。在此时间段内,任何系统的改变将被客户端看到,或者被客户端侦测到。

  1. CAP定理:

一个分布式系统不可能同时满足以下三种,一致性(C:Consistency),可用性(A:Available),分区容错性(P:Partition Tolerance).
在此ZooKeeper保证的是CP,ZooKeeper不能保证每次服务请求的可用性,在极端环境下,ZooKeeper可能会丢弃一些请求,消费者程序需要重新请求才能获得结果。
另外在进行leader选举时集群都是不可用,所以说,ZooKeeper不能保证服务可用性。

  1. ZAB协议:

ZAB协议包括两种基本的模式:崩溃恢复和消息广播。当整个 Zookeeper 集群刚刚启动或者Leader服务器宕机、重启或者网络故障导致不存在过半的服务器与 Leader 服务器保持正常通信时,所有服务器进入崩溃恢复模式,首先选举产生新的 Leader 服务器,然后集群中 Follower 服务器开始与新的 Leader 服务器进行数据同步。
当集群中超过半数机器与该 Leader 服务器完成数据同步之后,退出恢复模式进入消息广播模式,Leader 服务器开始接收客户端的事务请求生成事物提案来进行事务请求处理。

  1. 选举算法和流程:

FastLeaderElection(默认提供的选举算法):
目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:
(1)服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking。
(2)服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
(3)服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为leader,服务器1,2成为follower。
(4)服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为follower。
(5)服务器5启动,后面的逻辑同服务器4成为follower。

  1. 节点类型:

(1)持久
(2)持久顺序
(3)临时
(4)临时顺序

  1. 如何保证事务的顺序一致性

(1)Leader为每个请求生成zxid,下发proposal给Follower,Follower会将请求写入到pendingTxns阻塞队列及txnLog中,然后发送ack给Leader。
(2)ack过半,Leader发送commit请求给所有Follower,Follower对比commit request的zxid和前面提到的pendingTxns的zxid,不一致的话Follower退出,重新跟Leader同步。
(3)Follower处理commit请求,如果不是本Follower提交的写请求,直接调用FinalRequestProcessor做持久化,触发watches;如果是本Follower提交,则做一些特殊处理(主要针对客户端连接断开的场景),然后调用FinalRequestProcessor等后续处理流程。
(4)FinalRequestProcessor做持久化,返回客户端。

  1. Zookeeper Watcher流程

客户端向服务端的某个节点路径上注册一个watcher,客户端同时会在本地watcherManager中存储特定的watcher,当发生节点数据或者节点子节点变化时,服务端会通知客户端节点变化信息,然后客户端收到通知后,会调用回调函数。

Redis

  1. 应用场景

(1)缓存
(2)共享Session
(3)消息队列系统
(4)分布式锁

  1. 单线程的Redis为什么快

(1)纯内存操作
(2)单线程操作,避免了频繁的上下文切换
(3)合理高效的数据结构
(4)采用了非阻塞I/O多路复用机制

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

(1)String字符串:字符串类型是 Redis 最基础的数据结构,首先键都是字符串类型,而且 其他几种数据结构都是在字符串类型基础上构建的,我们常使用的 set key value 命令就是字符串。常用在缓存、计数、共享Session、限速等。
(2)Hash哈希:在Redis中,哈希类型是指键值本身又是一个键值对 结构,形如value={{field1,value1},...{fieldN,valueN}},添加命令:hset key field value。哈希可以用来存放用户信息,比如实现购物车
(3)List列表:列表(list)类型是用来存储多个有序的字符串。可以做简单的消息队列的功能。
(4)Set集合:集合(set)类型也是用来保存多个的字符串元素,但和列表类型不一 样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过 索引下标获取元素。利用 Set 的交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。
(5)Sorted Set有序集合:Sorted Set 多了一个权重参数 Score,集合中的元素能够按 Score 进行排列。可以做排行榜应用,取 TOP N 操作。

  1. Redis 的数据过期策略

Redis 中数据过期策略采用定期删除+惰性删除策略
(1)定期删除策略:Redis 启用一个定时器定时监视所有的 key,判断key是否过期,过期的话就删除。这种策略可以保证过期的 key 最终都会被删除,但是也存在严重的缺点:每次都遍历内存中所有的数据,非常消耗 CPU 资源,并且当 key 已过期,但是定时器还处于未唤起状态,这段时间内 key 仍然可以用。
(2)惰性删除策略:在获取 key 时,先判断 key 是否过期,如果过期则删除。这种方式存在一个缺点:如果这个 key 一直未被使用,那么它一直在内存中,其实它已经过期了,会浪费大量的空间。
这两种策略天然的互补,结合起来之后,定时删除策略就发生了一些改变,不在是每次扫描全部的 key 了,而是随机抽取一部分 key 进行检查,这样就降低了对 CPU 资源的损耗,惰性删除策略互补了为检查到的key,基本上满足了所有要求。
但是有时候就是那么的巧,既没有被定时器抽取到,又没有被使用,这些数据又如何从内存中消失?没关系,还有内存淘汰机制,当内存不够用时,内存淘汰机制就会上场。淘汰策略分为:
(1)当内存不足以容纳新写入数据时,新写入操作会报错。(Redis 默认策略)
(2)当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key。(推荐使用)
(3)当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。
(4)当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 Key。这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。
(5)当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。
(6)当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。

  1. 如何解决 Redis 缓存雪崩问题

(1)使用 Redis 高可用架构:使用 Redis 集群来保证 Redis 服务不会挂掉
(2)缓存时间不一致,给缓存的失效时间,加上一个随机值,避免集体失效
(3)限流降级策略:有一定的备案,比如个性推荐服务不可用了,换成热点数据推荐服务

  1. 如何解决 Redis 缓存穿透问题

(1)在接口做校验
(2)布隆过滤器拦截: 将所有可能的查询key 先映射到布隆过滤器中,查询时先判断key是否存在布隆过滤器中,存在才继续向下执行,如果不存在,则直接返回。
布隆过滤器将值进行多次哈希bit存储,布隆过滤器说某个元素在,可能会被误判。布隆过滤器说某个元素不在,那么一定不在。

  1. redis的持久化机制

redis为了保证效率,数据缓存在了内存中,但是会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件中,以保证数据的持久化。Redis的持久化策略有两种:
(1)RDB:快照形式是直接把内存中的数据保存到一个dump的文件中,定时保存,保存策略。
当Redis需要做持久化时,Redis会fork一个子进程,子进程将数据写到磁盘上一个临时RDB文件中。当子进程完成写临时文件后,将原来的RDB替换掉。
(2)AOF:把所有的对Redis的服务器进行修改的命令都存到一个文件里,命令的集合。
使用AOF做持久化,每一个写命令都通过write函数追加到appendonly.aof中。aof的默认策略是每秒钟fsync一次,在这种配置下,就算发生故障停机,也最多丢失一秒钟的数据。
缺点是对于相同的数据集来说,AOF的文件体积通常要大于RDB文件的体积。根据所使用的fsync策略,AOF的速度可能会慢于RDB。
Redis默认是快照RDB的持久化方式。

  1. redis主从复制,主从同步

(1)从节点执行slaveofmasterIP,保存主节点信息
(2)从节点中的定时任务发现主节点信息,建立和主节点的socket连接
(3)从节点发送Ping信号,主节点返回Pong,两边能互相通信
(4)连接建立后,主节点将所有数据发送给从节点(数据同步)
(5)主节点把当前的数据同步给从节点后,便完成了复制的建立过程。接下来,主节点就会持续的把写命令发送给从节点,保证主从数据一致性。

  1. Redis和memcached的区别

(1)存储方式上:memcache会把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。redis有部分数据存在硬盘上,这样能保证数据的持久性。
(2)数据支持类型上:memcache对数据类型的支持简单,只支持简单的key-value,,而redis支持五种数据类型。
(3)用底层模型不同:它们之间底层实现方式以及与客户端之间通信的应用协议不一样。redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
(4)value的大小:redis可以达到1GB,而memcache只有1MB。

  1. redis并发竞争key的解决方案

(1)分布式锁+时间戳
(2)利用消息队列

  1. Redis与Mysql双写一致性方案

先更新数据库,再删缓存。数据库的读操作的速度远快于写操作的,所以脏数据很难出现。可以对异步延时删除策略,保证读请求完成以后,再进行删除操作。

Mysql

  1. 事务的基本要素

(1)原子性:事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行
(2)一致性:事务开始前和结束后,数据库的完整性约束没有被破坏。
(3)隔离性:同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。
(4)持久性:事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

  1. 事务的并发问题

(1)脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
(2)不可重复读:事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
(3)幻读:A事务读取了B事务已经提交的新增数据。注意和不可重复读的区别,这里是新增,不可重复读是更改(或删除)。

  1. MySQL事务隔离级别

事务隔离级别 | 脏读 | 不可重复读 | 幻读
读未提交 | 是 | 是 |是
不可重复读 | 否 | 是 |是
可重复读 | 否 | 否 |是
串行化 | 否 | 否 |否

在MySQL可重复读的隔离级别中并不是完全解决了幻读的问题,而是解决了读数据情况下的幻读问题。而对于修改的操作依旧存在幻读问题,就是说MVCC对于幻读的解决时不彻底的。
通过索引加锁,间隙锁可以解决幻读的问题。

  1. Mysql的逻辑结构

最上层的服务类似其他CS结构,比如连接处理,授权处理。第二层是Mysql的服务层,包括SQL的解析分析优化,存储过程触发器视图等也在这一层实现。
最后一层是存储引擎的实现,类似于Java接口的实现,Mysql的执行器在执行SQL的时候只会关注API的调用,完全屏蔽了不同引擎实现间的差异。
比如Select语句,先会判断当前用户是否拥有权限,其次到缓存(内存)查询是否有相应的结果集,如果没有再执行解析sql,优化生成执行计划,调用API执行。

  1. MVCC,redolog,undolog

undoLog 也就是我们常说的回滚日志文件 主要用于事务中执行失败,进行回滚,以及MVCC中对于数据历史版本的查看。
由引擎层的InnoDB引擎实现,是逻辑日志,记录数据修改被修改前的值,比如"把id='B' 修改为id = 'B2' ,那么undo日志就会用来存放id ='B'的记录”
当一条数据需要更新前,会先把修改前的记录存储在undolog中,如果这个修改出现异常,,则会使用undo日志来实现回滚操作,保证事务的一致性。
当事务提交之后,undo log并不能立马被删除,而是会被放到待清理链表中,待判断没有事物用到该版本的信息时才可以清理相应undolog。
它保存了事务发生之前的数据的一个版本,用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读。

redoLog 是重做日志文件是记录数据修改之后的值,用于持久化到磁盘中。
redo log包括两部分:一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。
由引擎层的InnoDB引擎实现,是物理日志,记录的是物理数据页修改的信息,比如“某个数据页上内容发生了哪些改动”
当一条数据需要更新时,InnoDB会先将数据更新,然后记录redoLog 在内存中,然后找个时间将redoLog的操作执行到磁盘上的文件上。不管是否提交成功我都记录,你要是回滚了,那我连回滚的修改也记录。它确保了事务的持久性。

MVCC多版本并发控制是MySQL中基于乐观锁理论实现隔离级别的方式,用于实现读已提交和可重复读取隔离级别的实现。
在MySQL中,会在表中每一条数据后面添加两个字段:最近修改该行数据的事务ID,指向该行(undolog表中)回滚段的指针。
Read View判断行的可见性,创建一个新事务时,copy一份当前系统中的活跃事务列表。意思是,当前不应该被本事务看到的其他事务id列表。

  1. binlog

由Mysql的Server层实现,是逻辑日志,记录的是sql语句的原始逻辑,比如"把id='B' 修改为id = ‘B2’。binlog会写入指定大小的物理文件中,是追加写入的,当前文件写满则会创建新的文件写入。 产生:事务提交的时候,一次性将事务中的sql语句,按照一定的格式记录到binlog中。
用于复制和恢复在主从复制中,从库利用主库上的binlog进行重播(执行日志中记录的修改逻辑),实现主从同步。业务数据不一致或者错了,用binlog恢复。

  1. InnoDB的行锁模式

(1)共享锁:又称读锁,允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
(2)排他锁:又称写锁,允许获取排他锁的事务更新数据,阻止其他事务取得相同的数据集共享读锁和排他写锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。
在没有索引的情况下,InnoDB只能使用表锁。

7.为什么选择B+树作为索引结构

(1)Hash索引:Hash索引底层是哈希表,哈希表是一种以key-value存储数据的结构,所以多个数据在存储关系上是完全没有任何顺序关系的,所以,对于区间查询是无法直接通过索引查询的,就需要全表扫描。所以,哈希索引只适用于等值查询的场景。
而B+ 树是一种多路平衡查询树,所以他的节点是天然有序的(左子节点小于父节点、父节点小于右子节点),所以对于范围查询的时候不需要做全表扫描
(2)二叉查找树:解决了排序的基本问题,但是由于无法保证平衡,可能退化为链表。
(3)平衡二叉树:通过旋转解决了平衡的问题,但是 旋转操作 效率太低。
(4)红黑树:通过舍弃严格的平衡和引入红黑节点,解决了 AVL旋转效率过低的问题,但是在磁盘等场景下,树仍然太高,IO次数太多。
(5)B+树:在B树的基础上,将非叶节点改造为不存储数据 纯索引节点,进一步降低了树的高度;此外将叶节点使用指针连接成链表,范围查询更加高效。

  1. B+树的叶子节点都可以存哪些东西

可能存储的是整行数据,也有可能是主键的值。B+树的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。而索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引

  1. 覆盖索引

指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。

  1. 查询在什么时候不走(预期中的)索引?

(1)模糊查询 %like
(2)索引列参与计算,使用了函数
(3)非最左前缀顺序
(4)where对null判断
(5)where不等于
(6)or操作有至少一个字段没有索引

JVM

  1. 运行时数据区域
Comments
Write a Comment