后端知识查漏补缺学习轨迹(长期更新)

后端知识查漏补缺学习轨迹(长期更新)

    • 2022-02-06
      • 尾递归
      • 布隆过滤器
        • 布隆过滤器原理
        • redis中的布隆过滤器
        • 布隆过滤器的应用
      • 跳表VS红黑树
      • redis RDB VS AOF
        • RDB
        • AOF
      • HashMap连环问
      • redis的过期策略以及内存淘汰机制
      • redis reactor模型
      • redis key value实现原理
    • 2022-02-07
      • 结合mvcc谈谈可重复读到底是怎么实现的
        • ReadView
          • Rr级别下的ReadView
          • Rc级别下的ReadView
      • mysql中binlog redolog undolog 各自的作用
        • 关系图
        • binlog
        • redolog
        • undolog
      • mysql事务隔离级别
        • 为什么Mysql默认级别是rr
        • RR VS RC
      • redis红锁
        • 红锁解决什么问题?
        • 如何解决
      • redis哨兵模式
        • 为什么要用哨兵模式
        • 实现方式
        • 启动顺序
        • 优缺点
    • 2022-02-08
      • redis集群模式
        • 为什么需要集群模式
        • 优缺点
      • mysql之b-树 b+树
        • B-树
        • B+树
        • 二者的区别
        • mysql Hash索引
    • 2022-02-09
      • 索引下推
      • 分布式事务
        • XA协议
        • 两阶段提交(2PC)
        • 三阶段提交协议(3PC)
        • TCC
    • 2022-02-12
      • Seata
        • 概念
        • AT机制
        • Seata AT与传统 2PC的差别
      • BitMap
        • 原理
        • 优缺点
        • 算法场景
        • 其他应用场景
    • 2022-02-15
      • JAVA不同的引用
        • 强引用(Strong Reference)
        • 弱引用(Weak Reference)
        • 软引用(Soft Reference)
        • 虚引用(Phantom Reference)
        • ThreadLocal为何使用弱引用
        • 关于ThreadLocal对象是否设置为static的利弊
    • 2022-02-17
      • RetreenLock原理分析
    • 2022-02-27
      • CountDownLatch和CyclicBarrier的区别
        • CountDownLatch :
        • CyclicBarrier:
    • 2022-03-06
      • SynchronousQueue
        • 概述
        • 线程池中的应用
    • 2022-03-08
      • Semaphore 限流可用性?
      • notify和wait
        • 为什么必许在同步块中使用
        • 为什么wait notify会放在Object里边
      • 什么是CopyOnWrite容器
        • 概念
        • CopyOnWriteArrayList的实现原理
        • 应用场景
        • 缺点
      • ReadWriteLock原理
    • 2022-03-11
      • Condition
        • 概念:
        • 和notify wait区别
        • 原理
    • 2022-03-12
      • UNSAFE
        • 概念
        • 功能介绍
      • volatile 和 总线风暴
      • MESI(缓存一致性协议)
      • Callable和Runnable的区别
    • 2022-03-13
      • CAP理论
        • 概念
      • BASE理论
        • 概念
      • zookeeper相关
        • Zookeeper 文件系统
        • Zookeeper 保证了如下分布式一致性特性
        • ZAB 协议
          • 消息广播
          • 崩溃恢复
          • 数据同步
      • Bean的初始化和生命周期
        • 初始化
      • 生命周期
      • spring aware 各种接口的作用解析
      • 当Spring AOP遇上循环依赖
      • 为什么@Async注解的对象不支持循环依赖
    • 2022-03-16
      • ThreadPoolExecutor详解
        • 执行流程
        • addWorker
        • 线程的复用
        • 如何合理配置线程池的大小
      • 技术选型:RocketMQ or Kafka
        • 性能
        • 可靠性
        • 消费失败重试机制
        • 定时/延时消息
        • 消息查询机制
      • RocketMQ为什么性能好
        • 存储结构
        • RocketMQ 如何基于mmap+page cache实现磁盘文件的高性能读写?
      • DirectBuffer
    • 2022-03-20
      • SpringBoot整合MyBatis原理
      • SpringBoot整合MyBatis一级缓存失效问题
      • zookeeper VS nacos
        • 存储和数据更新
        • 注册中心
      • 限流算法
        • 固定窗口限流算法
        • 滑动窗口限流算法
        • 漏桶算法
        • 令牌桶算法
      • Sentinel 中限流算法的实现
        • DefaultController
        • RateLimiterController
        • WarmUpController
    • 2022-03-21
      • java锁的升级
        • 升级过程
        • 为什么说重量级锁开销大呢
      • java锁降级
      • JDBC与双亲委派模型
        • 手动加载
        • spi机制
    • 2022-03-22
      • rocketmq 同一个group的消费者订阅不同的topic会发生什么?

2022-02-06

尾递归

什么是尾递归?
若函数在尾位置调用自身(或是一个尾调用本身的其他函数等等),则称这种情况为尾递归
尾调用的重要性在于它可以不在调用栈上面添加一个新的堆栈帧——而是更新它,如同迭代一般

但java中即使采用了这种编程方式实现了递归,也无法达到尾递归的效果

为啥java不支持呢,网上冲浪一番得出一些结论:

改变堆栈跟踪,从而使调试程序变得更加困难。我认为Java的主要目标之一是允许程序员轻松调试他们的代码,而堆栈跟踪对于做到这一点至关重要。
在高度面向对象的编程环境中。由于可以改用迭代,因此语言委员会必须认为不值得添加尾递归

布隆过滤器

布隆过滤器原理

这个是由柏顿.布隆在1970年提出
实现原理就是我们需要一个很长的二进制数组(也叫向量);在添加数据时,使用多个hash函数对key进行hash运算得到一个索引值(即二进制数组的索引值)

redis中的布隆过滤器

Redis布隆过滤器的基本使用
在Redis中,布隆过滤器有两个基本命令,分别是:

bf.add:添加元素到布隆过滤器中,类似于集合的sadd命令,不过bf.add命令只能一次添加一个元素,如果想一次添加多个元素,可以使用bf.madd命令。
bf.exists:判断某个元素是否在过滤器中,类似于集合的sismember命令,不过bf.exists命令只能一次查询一个元素,如果想一次查询多个元素,可以使用bf.mexists命令。

上面的例子中使用的布隆过滤器只是默认参数的布隆过滤器,它在我们第一次使用 bf.add 命令时自动创建的。Redis还提供了自定义参数的布隆过滤器,想要尽量减少布隆过滤器的误判,就要设置合理的参数。

在使用 bf.add 命令添加元素之前,使用bf.reserve 命令创建一个自定义的布隆过滤器。bf.reserve命令有三个参数,分别是:

key:键
error_rate:期望错误率,期望错误率越低,需要的空间就越大。
capacity:初始容量,当实际元素的数量超过这个初始化容量时,误判率上升。

布隆过滤器的应用

1.防止缓存穿透
一般情况下,先查询缓存是否有该条数据,缓存中没有时,再查询数据库。当数据库也不存在该条数据时,每次查询都要访问数据库,这就是缓存穿透。缓存穿透带来的问题是,当有大量请求查询数据库不存在的数据时,就会给数据库带来压力,甚至会拖垮数据库。

可以使用布隆过滤器解决缓存穿透的问题,把已存在数据的key存在布隆过滤器中。当有新的请求时,先到布隆过滤器中查询是否存在,如果不存在该条数据直接返回;如果存在该条数据再查询缓存查询数据库。

2.黑名单校验
发现存在黑名单中的,就执行特定操作。比如:识别垃圾邮件,只要是邮箱在黑名单中的邮件,就识别为垃圾邮件。假设黑名单的数量是数以亿计的,存放起来就是非常耗费存储空间的,布隆过滤器则是一个较好的解决方案。把所有黑名单都放在布隆过滤器中,再收到邮件时,判断邮件地址是否在布隆过滤器中即可。

跳表VS红黑树

跳跃表其实也是一种通过“空间来换取时间”的一个算法,通过在每个节点中增加了向前的指针,从而提升查找的效率。

为什么redis zset使用跳表而不是红黑树?
1.按照区间来查找数据这个操作,红黑树的效率没有跳表高。
2.跳表更容易代码实现,而简单就意味着可读性好,不容易出错。
3.跳表更加灵活,它可以通过改变索引构建策略,有效平衡执行效率和内存消耗。

为什么hashmap用红黑树而不用跳表?
1.跳表是以空间换时间的数据结构,红黑树更节省空间
2.concurrentskiplistmap的实现就是跳表,说明在并发有序的要求场景下,跳表实现起来性价比更高,加锁难度也会小很多。

redis RDB VS AOF

RDB

RDB适合冷备份 将某个时间点的所有数据都存放到硬盘上。
优点:
1.RDB是一个非常紧凑(compact)的文件,它保存了redis 在某个时间点上的数据集。这种文件非常适合用于进行备份和灾难恢复(将持久化到硬盘中的文件恢复即可)。
2.生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
3.RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
缺点:
1.如果你需要尽量避免在服务器故障时丢失数据,那么RDB 不适合你。 虽然Redis 允许你设置不同的保存点(save point)来控制保存 RDB 文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据(最后一次的数据)。

2.每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。

AOF

追加到AOF文件的末尾,最后以不同的频次保存到到磁盘)适合热备
同步磁盘策略:
选项同步频率always每个写命令都同步everysec每秒同步一次no让操作系统来决定何时同步
always 选项会严重减低服务器的性能;
everysec 选项比较合适,可以保证系统崩溃时只会丢失一秒左右的数据,并且 Redis 每秒执行一次同步对服务器性能几乎没有任何影响;
no 让操作系统来决定应该何时进行同步(并不能给服务器性能带来多大的提升,而且也会增加系统崩溃时数据丢失的数量)

随着服务器写请求的增多,AOF 文件会越来越大。Redis 提供了一种将 AOF 重写的特性,能够去除 AOF 文件中的冗余写命令。
用户可以向 Redis 发送 BGREWRITEAOF 命令,这个命令会移除 AOF 文件中冗余的命令来重写 AOF 文件,使 AOF 文件的体积变得尽可能地小

优点:
1.AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步一次,Redis 最多也就丢失 1 秒的数据而已。
2.AOF 文件使用 Redis 命令追加的形式来构造,因此,即使 Redis 只能向 AOF 文件写入命令的片断,使用 redis-check-aof 工具也很容易修正 AOF 文件。
3.AOF 文件的格式可读性较强,这也为使用者提供了更灵活的处理方式。例如,如果我们不小心错用了 FLUSHALL 命令,在重写还没进行时,我们可以手工将最后的 FLUSHALL 命令去掉,然后再使用 AOF 来恢复数据。

缺点:
1.对于具有相同数据的的 Redis,AOF 文件通常会比 RDF 文件体积更大。
虽然 AOF 提供了多种同步的频率,默认情况下,每秒同步一次的频率也具有较高的性能。但在 2.Redis 的负载较高时,RDB 比 AOF 具好更好的性能保证。
3.RDB 使用快照的形式来持久化整个 Redis 数据,而 AOF 只是将每次执行的命令追加到 AOF 文件中,因此从理论上说,RDB 比 AOF 方式更健壮。官方文档也指出,AOF 的确也存在一些 BUG,这些 BUG 在 RDB 没有存在。

HashMap连环问

参考
https://zhuanlan.zhihu.com/p/163283988

redis的过期策略以及内存淘汰机制

分析:这个问题其实相当重要,到底redis有没用到家,这个问题就可以看出来。比如你redis只能存5G数据,可是你写了10G,那会删5G的数据。怎么删的,这个问题思考过么?还有,你的数据已经设置了过期时间,但是时间到了,内存占用率还是比较高,有思考过原因么?
回答:
redis采用的是定期删除+惰性删除策略。
为什么不用定时删除策略?
定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.
定期删除+惰性删除是如何工作的呢?
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
采用定期删除+惰性删除就没其他问题了么?
不是的,如果定期删除没删除key。然后你也没及时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。
在redis.conf中有一行配置

#maxmemory-policy allkeys-lru
该配置就是配内存淘汰策略的(什么,你没配过?好好反省一下自己)
1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。
2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用。
3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。
4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又做持久化存储的时候才用。不推荐
5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐
6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐
ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。

redis reactor模型

首先,Redis服务器中有两类事件,文件事件和时间事件。

文件事件(file event):Redis客户端通过socket与Redis服务器连接,而文件事件就是服务器对套接字操作的抽象。例如,客户端发了一个GET命令请求,对于Redis服务器来说就是一个文件事件。

时间事件(time event):服务器定时或周期性执行的事件。例如,定期执行RDB持久化。

在这里我们主要关注Redis处理文件事件的模型。参考《Redis的设计与实现》,Redis的文件事件处理模型是这样的:
后端知识查漏补缺学习轨迹(长期更新)_第1张图片
在这个模型中,Redis服务器用主线程执行I/O多路复用程序、文件事件分派器以及事件处理器。而且,尽管多个文件事件可能会并发出现,Redis服务器是顺序处理各个文件事件的。

Redis服务器主线程的执行流程在Redis.c的main函数中体现,而关于处理文件事件的主要的有这几行:

int main(int argc, char **argv) {
   
    ...
    initServer();
    ...
    aeMain();
    ...
    aeDeleteEventLoop(server.el);
    return 0;
}

redis key value实现原理

本质也是数组+链表的形式来解决hash冲突,也会2倍扩容,和hashmap很类似,具体参考
https://www.cnblogs.com/wenbochang/p/11673590.html

2022-02-07

结合mvcc谈谈可重复读到底是怎么实现的

总体来说一句话:通过ReadView辅助记录记录事务版本号信息,继而通过查询比较undo log 版本链实现

不同事务的undo log是通过roll_pointer 指针链接在一起的。roll_pointer 指向上一条 undo log 日志。

具体参考 https://zhuanlan.zhihu.com/p/166152616

ReadView

什么是ReadView?
read view 用一个可见性的算法,来判断当前是读取真实的数据,还是undo log的数据。这里可以简单理解read view 内部维护了一个事物id列表,里面有最大值和最小值,可以判断其它事物的id是否在这个可见范围内。

Rr级别下的ReadView

在可重复读隔离级别下,只会在第一次执行查询语句时生成ReadView
后端知识查漏补缺学习轨迹(长期更新)_第2张图片
因此我们发现两次查询,第二次无论时机如何,他们的结果都会根据第一次ReadView进行判断,结果就是“张石”。

由此可见可重复读隔离级别下可以避免不可重复读。

Rc级别下的ReadView

RC隔离级别下,是每个快照读都会生成并获取最新的Read View;而在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。

mysql中binlog redolog undolog 各自的作用

关系图

后端知识查漏补缺学习轨迹(长期更新)_第3张图片

binlog

binlog 设计目标
binlog 是作为mysql操作记录归档的日志,这个日志记录了所有对数据库的数据、表结构、索引等等变更的操作。也就是说只要是对数据库有变更的操作都会记录到binlog里面来, 可以把数据库的数据当成我们银行账户里的余额,而binlog就相当于我们银行卡的流水。账户余额只是一个结果,至于这个结果怎么来的,那就必须得看流水了。而同样在mysql里我们就是通过binlog来归档、验证、恢复、同步数据。

binlog 记录内容
binlog应该说是Mysql里最核心的日志, 它记录了除了查询语句(select、show)之外的所有的 DDL 和 DML 语句,也就意味着我们基本上所有对数据库的操作变更都会记录到binlog里面。binlog以事件形式记录,不仅记录了操作的语句,同时还记录了语句所执行的消耗的时间。 binlog 有三种记录格式,分别是ROW、STATEMENT、MIXED。

1、ROW: 基于变更的数据行进行记录,如果一个update语句修改一百行数据,那么这种模式下就会记录100行对应的记录日志。

2、STATEMENT:基于SQL语句级别的记录日志,相对于ROW模式,STATEMENT模式下只会记录这个update 的语句。所以此模式下会非常节省日志空间,也避免着大量的IO操作。

3、MIXED: 混合模式,此模式是ROW模式和STATEMENT模式的混合体,一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog。

这三种模式需要注意的是:使用 row 格式的 binlog 时,在进行数据同步或恢复的时候不一致的问题更容易被发现,因为它是基于数据行记录的。而使用 mixed 或者 statement 格式的 binlog 时,很多事务操作都是基于SQL逻辑记录,我们都知道一个SQL在不同的时间点执行它们产生的数据变化和影响是不一样的,所以这种情况下,数据同步或恢复的时候就容易出现不一致的情况。

binlog 写入策略
在进行事务的过程中,首先会把binlog 写入到binlog cache中(因为写入到cache中会比较快,一个事务通常会有多个操作,避免每个操作都直接写磁盘导致性能降低),事务最终提交的时候再吧binlog 写入到磁盘中。当然事务在最终commit的时候binlog是否马上写入到磁盘中是由参数 sync_binlog 配置来决定的。

1、sync_binlog=0 的时候,表示每次提交事务binlog不会马上写入到磁盘,而是先写到page cache,相对于磁盘写入来说写page cache要快得多,不过在Mysql 崩溃的时候会有丢失日志的风险。

2、sync_binlog=1 的时候,表示每次提交事务都会执行 fsync 写入到磁盘 ;

3、sync_binlog的值大于1 的时候,表示每次提交事务都 先写到page cach,只有等到积累了N个事务之后才fsync 写入到磁盘,同样在此设置下Mysql 崩溃的时候会有丢失N个事务日志的风险。

很显然三种模式下,sync_binlog=1 是强一致的选择,选择0或者N的情况下在极端情况下就会有丢失日志的风险,具体选择什么模式还是得看系统对于一致性的要求。

为什么statement模式下主从复制会有bug
如果使用了一些函数,statement 格式无法完成主从复制的操作。
因为执行的sql语句在不同的场景下可能会有不同的效果。比如sql中使用的了date()函数,主从同步的时候用的不是原始存入的日期,而是当前日期。

redolog

redo log 设计目标
redo log 是属于引擎层(innodb)的日志,它的设计目标是支持innodb的“事务”的特性,事务ACID特性分别是原子性、一致性、隔离性、持久性, 一致性是事务的最终追求的目标,隔离性、原子性、持久性是达成一致性目标的手段,根据的文章我们已经知道隔离性是通过锁机制来实现的。 而事务的原子性和持久性则是通过redo log 和undo log来保障的。

redo log 能保证对于已经COMMIT的事务产生的数据变更,即使是系统宕机崩溃也可以通过它来进行数据重做,达到数据的一致性,这也就是事务持久性的特征,一旦事务成功提交后,只要修改的数据都会进行持久化,不会因为异常、宕机而造成数据错误或丢失,所以解决异常、宕机而可能造成数据错误或丢是redo log的核心职责。

redo log记录的内容
redo log记录的是操作数据变更的日志,听起来好像和binlog有类似的地方,有时候我都会想有了binlog为什么还要redo log,当然从其它地方可以找到很多的理由,但是我认为最核心的一点就是redo log记录的数据变更粒度和binlog的数据变更粒度是不一样的(事务提交前是不会记录bignlog的),也正因为这个binlog是没有进行崩溃恢复事务数据的能力的。

以修改数据为例,binlog 是以表为记录主体,在ROW模式下,binlog保存的表的每行变更记录。

比如update tb_user set age =18 where name =‘赵白’ ,如果这条语句修改了三条记录的话,那么binlog记录就是

 UPDATE `db_test`.`tb_user` WHERE @1=5 @2='赵白' @3=91 @4='1543571201' SET  @1=5 @2='赵白' @3=18 @4='1543571201'
 UPDATE `db_test`.`tb_user` WHERE @1=6 @2='赵白' @3=91 @4='1543571201' SET  @1=5 @2='赵白' @3=18 @4='1543571201'
 UPDATE `db_test`.`tb_user` WHERE @1=7 @2='赵白' @3=91 @4='154

你可能感兴趣的:(面试,后端,java,redis)