目录

20. 一致性算法

20.1.1. Paxos

20.1.2. Zab

21. JAVA 算法

21.1.1. 二分查找

21.1.2. 冒泡排序算法

21.1.3. 插入排序算法

21.1.4. 快速排序算法

21.1.2. 归并排序算法

22. 数据结构

22.1.1. 栈(stack)

22.1.2. 队列(queue)

22.1.3. 链表(Link)

22.1.5. 排序二叉树

22.1.5.1. 插入操作

22.1.6. 红黑树

22.1.7. B-TREE

22.1.8. 位图

23. 加密算法

23.1.4. MD5

24. 分布式缓存

24.1.1. 缓存雪崩

24.1.2. 缓存穿透

24.1.3. 缓存预热

24.1.4. 缓存更新

24.1.5. 缓存降级


20. 一致性算法

20.1.1. Paxos


Paxos 算法解决的问题是一个分布式系统如何就某个值(决议)达成一致。一个典型的场景是, 在一个分布式数据库系统中,如果各节点的初始状态一致,每个节点执行相同的操作序列,那么 他们最后能得到一个一致的状态。为保证每个节点执行相同的命令序列,需要在每一条指令上执 行一个“一致性算法”以保证每个节点看到的指令一致。zookeeper 使用的 zab 算法是该算法的


一个实现。 在 Paxos 算法中,有三种角色:Proposer,Acceptor,Learners


Paxos 三种角色: Proposer Acceptor Learners


Proposer


只要 Proposer 发的提案被半数以上 Acceptor 接受,Proposer 就认为该提案里的 value 被选定 了。


Acceptor


只要 Acceptor 接受了某个提案,Acceptor 就认为该提案里的 value 被选定了。


Learner


Acceptor 告诉 Learner 哪个 value 被选定,Learner 就认为那个 value 被选定 。


Paxos 算法分为两个阶段。具体如下:


阶段一(准 leader 确定 ):


(a) Proposer 选择一个提案编号 N,然后向半数以上的 Acceptor 发送编号为 N 的 Prepare 请求。


(b) 如果一个 Acceptor 收到一个编号为 N 的 Prepare 请求,且 N 大于该 Acceptor 已经响应过的 所有 Prepare 请求的编号,那么它就会将它已经接受过的编号最大的提案(如果有的话)作为响 应反馈给 Proposer,同时该 Acceptor 承诺不再接受任何编号小于 N 的提案。


阶段二( leader 确认):


(a) 如果 Proposer 收到半数以上 Acceptor 对其发出的编号为 N 的 Prepare 请求的响应 ,那么它 就会发送一个针对[N,V]提案的 Accept 请求给半数以上的 Acceptor 。注意:V 就是收到的响应中 编号最大的提案的 value,如果响应中不包含任何提案,那么 V 就由 Proposer 自己决定。


(b) 如果 Acceptor 收到一个针对编号为 N 的提案的 Accept 请求,只要该 Acceptor 没有对编号 大于 N 的 Prepare 请求做出过响应,它就接受该提案。


20.1.2. Zab


ZAB( ZooKeeper Atomic Broadcast , ZooKeeper 原子消息广播协议)协议包括两种基本的模 式:崩溃恢复和消息广播


1. 当整个服务框架在启动过程中,或是当 Leader 服务器出现网络中断崩溃退出与重启等异常情 况时,ZAB 就会进入恢复模式并选举产生新的 Leader 服务器。


2. 当选举产生了新的 Leader 服务器,同时集群中已经有过半的机器与该 Leader 服务器完成了 状态同步之后,ZAB 协议就会退出崩溃恢复模式,进入消息广播模式。


3. 当有新的服务器加入到集群中去,如果此时集群中已经存在一个 Leader 服务器在负责进行消 息广播,那么新加入的服务器会自动进入数据恢复模式,找到 Leader 服务器,并与其进行数 据同步,然后一起参与到消息广播流程中去。


以上其实大致经历了三个步骤:


1. 崩溃恢复:主要就是 Leader 选举过程


2. 数据同步: Leader 服务器与其他服务器进行数据同步


3. 消息广播: Leader 服务器将数据发送给其他服务器


说明:zookeeper 章节对该协议有详细描述。


20.1.3. Raft


与 Paxos 不同 Raft 强调的是易懂(Understandability),Raft 和 Paxos 一样只要保证 n/2+1 节 点正常就能够提供服务;raft 把算法流程分为三个子问题:选举(Leader election)、日志复制 (Log replication)、安全性(Safety)三个子问题。


20.1.3.1. 角色


Raft 把集群中的节点分为三种状态: Leader、 Follower 、Candidate ,理所当然每种状态负 责的任务也是不一样的, Raft 运行时提供服务的时候只存在 Leader 与 Follower 两种状态 ;


Leader (领导者 - 日志管理)


负责日志的同步管理,处理来自客户端的请求,与 Follower 保持这 heartBeat 的联系;


Follower (追随者 - 日志同步)


刚启动时所有节点为Follower状态,响应Leader的日志同步请求,响应Candidate的请求, 把请求到 Follower 的事务转发给 Leader;


Candidate (候选者 - 负责选票)


负责选举投票,Raft 刚启动时由一个节点从 Follower 转为 Candidate 发起选举,选举出Leader 后从 Candidate 转为 Leader 状态;


20.1.3.2. Term (任期)


在 Raft 中使用了一个可以理解为周期(第几届、任期)的概念,用 Term 作为一个周期,每 个 Term 都是一个连续递增的编号,每一轮选举都是一个 Term 周期,在一个 Term 中只能产生一 个 Leader;当某节点收到的请求中 Term 比当前 Term 小时则拒绝该请求。


20.1.3.3. 选举( Election


选举定时器


Raft 的选举由定时器来触发, 每个节点的选举定时器时间都是不一样的 ,开始时状态都为 Follower 某个节点定时器触发选举后 Term 递增,状态由 Follower 转为 Candidate,向其他节点 发起 RequestVote RPC 请求,这时候有三种可能的情况发生:


1:该 RequestVote 请求接收到 n/2+1(过半数)个节点的投票,从 Candidate 转为 Leader, 向其他节点发送 heartBeat 以保持 Leader 的正常运转。


2:在此期间如果收到其他节点发送过来的 AppendEntries RPC 请求,如该节点的 Term 大 则当前节点转为 Follower,否则保持 Candidate 拒绝该请求。


3:Election timeout 发生则 Term 递增,重新发起选举在一个 Term 期间每个节点只能投票一次, 所以当有多个 Candidate 存在时就会出现每个Candidate 发起的选举都存在接收到的投票数都不过半的问题,这时每个 Candidate 都将 Term 递增、重启定时器并重新发起选举,由于每个节点中定时器的时间都是随机的,所以就不会多次 存在有多个 Candidate 同时发起投票的问题。


在 Raft 中当接收到客户端的日志(事务请求)后先把该日志追加到本地的 Log 中,然后通过 heartbeat 把该 Entry 同步给其他 Follower,Follower 接收到日志后记录日志然后向 Leader 发送ACK,当 Leader 收到大多数(n/2+1)Follower 的 ACK 信息后将该日志设置为已提交并追加到本地磁盘中,通知客户端并在下个 heartbeat 中 Leader 将通知所有的 Follower 将该日志存储在


自己的本地磁盘中。


20.1.3.4. 安全性( Safety


安全性是用于保证每个节点都执行相同序列的安全机制如当某个 Follower 在当前 Leader commit Log 时变得不可用了,稍后可能该 Follower 又会倍选举为 Leader,这时新 Leader 可能会用新的Log 覆盖先前已 committed 的 Log ,这就是导致节点执行不同序列;Safety 就是用于保证选举出来的 Leader 一定包含先前 commited Log 的机制;


选举安全性(Election Safety):每个 Term 只能选举出一个 Leader


Leader 完整性(Leader Completeness):这里所说的 完整性是指 Leader 日志的完整性 ,


Raft 在选举阶段就使用 Term 的判断用于保证完整性:当请求投票的该 Candidate 的 Term 较大或 Term 相同 Index 更大则投票 ,该节点将容易变成 leader。


20.1.3.5. raft 协议和 zab 协议区别


相同点


 采用 quorum 来确定整个系统的一致性,这个 quorum 一般实现是集群中半数以上的服务器,


 zookeeper 里还提供了带权重的 quorum 实现.


 都由 leader 来发起写操作.


 都采用心跳检测存活性


 leader election 都采用先到先得的投票方式


不同点


 zab 用的是 epoch 和 count 的组合来唯一表示一个值, 而 raft 用的是 term 和 index


 zab 的 follower 在投票给一个 leader 之前必须和 leader 的日志达成一致 而 raft 的 follower则简单地说是谁的 term 高就投票给谁


 raft 协议的心跳是从 leader 到 follower, 而 zab 协议则相反


 raft 协议数据只有单向地从 leader 到 follower(成为 leader 的条件之一就是拥有最新的 log), 而 zab 协议在 discovery 阶段, 一个 prospective leader 需要将自己的 log 更新为 quorum 里面 最新的 log, 然后才好在 synchronization 阶段将 quorum 里的其他机器的 log 都同步到一致.


20.1.4. NWR


N :在分布式存储系统中,有多少份备份数据


W :代表一次成功的更新操作要求至少有 w 份数据写入成功


R : 代表一次成功的读数据操作要求至少有 R 份数据成功读取


NWR值的不同组合会产生不同的一致性效果,当W+R>N 的时候,整个系统对于客户端来讲能保 证强一致性。而如果 R+W<=N,则无法保证数据的强一致性。以常见的 N=3、W=2、R=2 为例: N=3,表示,任何一个对象都必须有三个副本(Replica),W=2 表示,对数据的修改操作 (Write)只需要在 3 个 Replica 中的 2 个上面完成就返回,R=2 表示,从三个对象中要读取到 2 个数据对象,才能返回。




java开源免费的加密算法工具包_面试



20.1.5. Gossip


Gossip 算法又被称为反熵(Anti-Entropy),熵是物理学上的一个概念, 代表杂乱无章,而反熵 就是在杂乱无章中寻求一致 ,这充分说明了 Gossip 的特点:在一个有界网络中,每个节点都随机 地与其他节点通信,经过一番杂乱无章的通信,最终所有节点的状态都会达成一致。每个节点可能知道所有其他节点,也可能仅知道几个邻居节点,只要这些节可以通过网络连通,最终他们的状态都是一致的,当然这也是疫情传播的特点。


20.1.6. 一致性 Hash


一致性哈希算法(Consistent Hashing Algorithm)是一种分布式算法,常用于负载均衡。


Memcached client 也选择这种算法,解决将 key-value 均匀分配到众多 Memcached server 上 的问题。它可以取代传统的取模操作,解决了取模操作无法应对增删 Memcached Server 的问题(增删 server 会导致同一个 key,在 get 操作时分配不到数据真正存储的 server,命中率会急剧下降)。


20.1.6.1. 一致性 Hash 特性


 平衡性(Balance):平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得 所有的缓冲空间都得到利用。


 单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中, 又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓 冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。容易看到,上面的简单求余算法hash(object)%N 难以满足单调性要求。


 平滑性(Smoothness):平滑性是指缓存服务器的数目平滑改变和缓存对象的平滑改变是一致 的。


20.1.6.2. 一致性 Hash 原理


1. 建构环形 hash 空间:


1. 考虑通常的 hash 算法都是将 value 映射到一个 32 为的 key 值,也即是 0~2^32-1 次方的 数值空间;我们可以 将这个空间想象成一个首( 0 )尾( 2^32-1 )相接的圆环 。


2. 把需要缓存的内容 ( 对象 ) 映射到 hash 空间


2. 接下来考虑 4 个对象 object1~object4 ,通过 hash 函数计算出的 hash 值 key 在环上的分 布


3. 把服务器 ( 节点 ) 映射到 hash 空间


3. Consistent hashing 的基本思想就是将对象和 cache 都映射到同一个 hash 数值空间中,并 且使用相同的 hash 算法。一般的方法可以使用 服务器(节点) 机器的 IP 地址或者机器名作为 hash 输入。


4. 把对象映射到服务节点


4. 现在服务节点和对象都已经通过同一个 hash 算法映射到 hash 数值空间中了 ,首先确定对象 hash 值在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位 到的服务 器。


java开源免费的加密算法工具包_面试_02



考察 cache 的变动


5. 通过 hash 然后求余的方法带来的最大问题就在于不能满足单调性,当 cache 有所变动时, cache 会失效。


5.1 移除 cache:考虑假设 cache B 挂掉了,根据上面讲到的映射方法,这时受影响的将仅是 那些沿 cache B 逆时针遍历直到下一个 cache ( cache C )之间的对象。


5.2 添加 cache:再考虑添加一台新的 cache D 的情况,这时受影响的将仅是那些沿 cache D 逆时针遍历直到下一个 cache 之间的对象,将这些对象重新映射到 cache D 上即可。


虚拟节点 hash 算法并不是保证绝对的平衡,如果 cache 较少的话,对象并不能被均匀的映射到 cache 上, 为了解决这种情况, consistent hashing 引入了“虚拟节点”的概念,它可以如下定义: 虚拟节点( virtual node )是实际节点在 hash 空间的复制品( replica ) ,一实际个节点对应了若干个“虚拟节点”,这个对应个数也成为“复制个数”,“虚拟节点”在 hash 空间中以 hash


值排列。 仍以仅部署 cache A 和 cache C 的情况为例。现在我们引入虚拟节点,并设置“复制个数”为 2 , 这就意味着一共会存在 4 个“虚拟节点”, cache A1, cache A2 代表了 cache A; cache C1,cache C2 代表了 cache C 。此时,对象到“虚拟节点”的映射关系为:objec1->cache A2 ; objec2->cache A1 ; objec3->cache C1 ; objec4->cache C2 ;因此对象 object1 和 object2 都被映射到了 cache A 上,而 object3 和 object4 映射到了 cacheC 上;平衡性有了很大提高。


引入“虚拟节点”后,映射关系就从 { 对象 -> 节点 } 转换到了 { 对象 -> 虚拟节点 } 。查询物体所在 cache 时的映射关系如下图 所示。


java开源免费的加密算法工具包_服务器_03



21. JAVA 算法

21.1.1. 二分查找


又叫折半查找,要求待查找的序列有序。每次取中间位置的值与待查关键字比较,如果中间位置 的值比待查关键字大,则在前半部分循环这个查找的过程,如果中间位置的值比待查关键字小, 则在后半部分循环这个查找的过程。直到查找到了为止,否则序列中没有待查的关键字


java开源免费的加密算法工具包_面试_04



21.1.2. 冒泡排序算法


(1)比较前后相邻的二个数据,如果前面数据大于后面的数据,就将这二个数据交换。


(2)这样对数组的第 0 个数据到 N-1 个数据进行一次遍历后,最大的一个数据就“沉”到数组第 N-1 个位置。


(3)N=N-1,如果 N 不为 0 就重复前面二步,否则排序完成。


java开源免费的加密算法工具包_服务器_05



java开源免费的加密算法工具包_分布式_06


21.1.3. 插入排序算法


通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应的位置并插入。 插入排序非常类似于整扑克牌。在开始摸牌时,左手是空的,牌面朝下放在桌上。接着, 一次从 桌上摸起一张牌,并将它插入到左手一把牌中的正确位置上 。 为了找到这张牌的正确位置,要将 它与手中已有的牌从右到左地进行比较。无论什么时候,左手中的牌都是排好序的 。


如果输入数组已经是排好序的话,插入排序出现最佳情况,其运行时间是输入规模的一个线性函数。如果输入数组是逆序排列的,将出现最坏情况。平均情况与最坏情况一样,其时间代价是(n2)。



java开源免费的加密算法工具包_java_07



21.1.4. 快速排序算法


快速排序的原理:选择一个关键值作为基准值。比基准值小的都在左边序列(一般是无序的), 比基准值大的都在右边(一般是无序的)。一般选择序列的第一个元素。


一次循环: 从后往前比较 ,用基准值和最后一个值比较,如果比基准值小的交换位置,如果没有 继续比较下一个,直到找到第一个比基准值小的值才交换。 找到这个值之后,又从前往后开始比 较 ,如果有比基准值大的,交换位置,如果没有继续比较下一个,直到找到第一个比基准值大的 值才交换。直到从 前往后的比较索引>从后往前比较的索引 ,结束第一次循环,此时,对于基准值 来说,左右两边就是有序的了。


java开源免费的加密算法工具包_服务器_08

 

java开源免费的加密算法工具包_服务器_09



java开源免费的加密算法工具包_面试_10




21.1.1. 希尔排序算法


基本思想:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列 中的记录“基本有序”时,再对全体记录进行依次直接插入排序。


1. 操作方法:选择一个增量序列 t1,t2,…,tk,其中 ti>tj,tk=1;


2. 按增量序列个数 k,对序列进行 k 趟排序;


3. 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进 行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长 度。


java开源免费的加密算法工具包_java_11




java开源免费的加密算法工具包_面试_12


21.1.2. 归并排序算法


归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列 分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。


java开源免费的加密算法工具包_面试_13



java开源免费的加密算法工具包_服务器_14



java开源免费的加密算法工具包_分布式_15



java开源免费的加密算法工具包_分布式_16



java开源免费的加密算法工具包_分布式_17


21.1.3. 桶排序算法


桶排序的基本思想是: 把数组 arr 划分为 n 个大小相同子区间(桶),每个子区间各自排序,最 后合并 。计数排序是桶排序的一种特殊情况,可以把计数排序当成每个桶里只有一个元素的情况。 


1.找出待排序数组中的最大值 max、最小值 min


2.我们使用 动态数组 ArrayList 作为桶,桶里放的元素也用 ArrayList 存储。桶的数量为(maxmin)/arr.length+1


3.遍历数组 arr,计算每个元素 arr[i] 放的桶


4.每个桶各自排序


java开源免费的加密算法工具包_服务器_18



java开源免费的加密算法工具包_面试_19


21.1.4. 基数排序算法


将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位 开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列



java开源免费的加密算法工具包_分布式_20



java开源免费的加密算法工具包_分布式_21



java开源免费的加密算法工具包_java开源免费的加密算法工具包_22


21.1.5. 剪枝算法


在搜索算法中优化中,剪枝,就是通过某种判断,避免一些不必要的遍历过程,形象的说,就是 剪去了搜索树中的某些“枝条”,故称剪枝。应用剪枝优化的核心问题是设计剪枝判断方法,即 确定哪些枝条应当舍弃,哪些枝条应当保留的方法


java开源免费的加密算法工具包_java开源免费的加密算法工具包_23


21.1.6. 回溯算法


回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现 已不满足求解条件时,就“回溯”返回,尝试别的路径。


21.1.7. 最短路径算法


从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径叫做最 短路径。解决最短路的问题有以下算法,Dijkstra 算法,Bellman-Ford 算法,Floyd 算法和 SPFA 算法等。


21.1.8. 最大子数组算法


21.1.9. 最长公共子序算法


21.1.10. 最小生成树算法


现在假设有一个很实际的问题:我们要在 n 个城市中建立一个通信网络,则连通这 n 个城市需要 布置 n-1 一条通信线路,这个时候我们需要考虑如何在成本最低的情况下建立这个通信网? 于是我们就可以引入连通图来解决我们遇到的问题,n 个城市就是图上的 n 个顶点,然后,边表示 两个城市的通信线路,每条边上的权重就是我们搭建这条线路所需要的成本,所以现在我们有 n 个 顶点的连通网可以建立不同的生成树,每一颗生成树都可以作为一个通信网,当我们构造这个连 通网所花的成本最小时,搭建该连通网的生成树,就称为最小生成树。构造最小生成树有很多算法,但是他们都是利用了最小生成树的同一种性质:MST 性质(假设 N=(V,{E})是一个连通网,U 是顶点集 V 的一个非空子集,如果(u,v)是一条具有最小权值的边, 其中 u 属于 U,v 属于 V-U,则必定存在一颗包含边(u,v)的最小生成树),下面就介绍两种使 用 MST 性质生成最小生成树的算法:普里姆算法和克鲁斯卡尔算法。



java开源免费的加密算法工具包_java开源免费的加密算法工具包_24


22. 数据结构

22.1.1. 栈(stack)


栈(stack)是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫做栈顶 (top)。它是后进先出(LIFO)的。对栈的基本操作只有 push(进栈)和 pop(出栈)两种, 前者相当于插入,后者相当于删除最后的元素。



java开源免费的加密算法工具包_java开源免费的加密算法工具包_25


22.1.2. 队列(queue)


队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的 后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为 队尾,进行删除操作的端称为队头。



java开源免费的加密算法工具包_面试_26


22.1.3. 链表(Link)


链表是一种数据结构,和数组同级。比如,Java 中我们使用的 ArrayList,其实现原理是数组。而 LinkedList 的实现原理就是链表了。链表在进行循环遍历时效率不高,但是插入和删除时优势明显。



java开源免费的加密算法工具包_面试_27


22.1.4. 散列表( Hash Table


散列表(Hash table,也叫哈希表)是一种查找算法,与链表、树等算法不同的是,散列表算法 在查找时不需要进行一系列和关键字(关键字是数据元素中某个数据项的值,用以标识一个数据 元素)的比较操作。


散列表算法希望能尽量做到不经过任何比较,通过一次存取就能得到所查找的数据元素 ,因而必 须要在数据元素的存储位置和它的关键字(可用 key 表示)之间建立一个确定的对应关系,使每个 关键字和散列表中一个唯一的存储位置相对应。因此在查找时,只要根据这个对应关系找到给定 关键字在散列表中的位置即可。这种对应关系被称为散列函数(可用 h(key)表示)。


用的构造散列函数的方法有:


(1)直接定址法:取关键字或关键字的某个线性函数值为散列地址:即h(key) = key或 h(key) = a * key + b其中 a 和 b 为常数。


(2)数字分析法


(3)平方取值法: 取关键字平方后的中间几位为散列地址。


(4)折叠法:将关键字分割成位数相同的几部分,然后取这几部分的叠加和作为散列地址。


(5)除留余数法:取关键字被某个不大于散列表表长 m 的数 p 除后所得的余数为散列地址即:h(key) = key MOD p p ≤ m


(6)随机数法:选择一个随机函数,取关键字的随机函数值为它的散列地址, 即:h(key) = random(key)


22.1.5. 排序二叉树


首先如果普通二叉树每个节点满足:左子树所有节点值小于它的根节点值,且右子树所有节点值 大于它的根节点值,则这样的二叉树就是排序二叉树。


22.1.5.1. 插入操作


首先要从根节点开始往下找到自己要插入的位置(即新节点的父节点);具体流程是:新节点与 当前节点比较,如果相同则表示已经存在且不能再重复插入;如果小于当前节点,则到左子树中寻找,如果左子树为空则当前节点为要找的父节点,新节点插入到当前节点的左子树即可;如果大于当前节点,则到右子树中寻找,如果右子树为空则当前节点为要找的父节点,新节点插入到


当前节点的右子树即可。



java开源免费的加密算法工具包_分布式_28


22.1.5.2. 删除操作


删除操作主要分为三种情况, 即要删除的节点无子节点,要删除的节点只有一个子节点,要删除 的节点有两个子节点 。


1. 对于要删除的节点无子节点可以直接删除,即让其父节点将该子节点置空即可。


2. 对于要删除的节点只有一个子节点,则替换要删除的节点为其子节点。


3. 对于要删除的节点有两个子节点, 则首先找该节点的替换节点 (即右子树中最小的节点), 接着替换要删除的节点为替换节点,然后删除替换节点。



java开源免费的加密算法工具包_java开源免费的加密算法工具包_29


22.1.5.3. 查询操作


查找操作的主要流程为:先和根节点比较,如果相同就返回, 如果小于根节点则到左子树中 递归查找,如果大于根节点则到右子树中递归查找 。因此在排序二叉树中可以很容易获取最大(最右最深子节点)和最小(最左最深子节点)值。


22.1.6. 红黑树


R-B Tree,全称是 Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每 个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。


22.1.6.1. 红黑树的特性


(1)每个节点或者是黑色,或者是红色。


(2)根节点是黑色。


(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL 或NULL)的叶子节点!]


(4)如果一个节点是红色的,则它的子节点必须是黑色的。


(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。


22.1.6.1. 左旋


对 x 进行左旋,意味着,将“x 的右孩子”设为“x 的父亲节点”;即,将 x 变成了一个左节点(x 成了为 z 的左孩子)!。 因此,左旋中的“左”,意味着“被旋转的节点将变成一个左节点”。



java开源免费的加密算法工具包_java_30


java开源免费的加密算法工具包_java_31

java开源免费的加密算法工具包_分布式_32


22.1.6.1. 右旋


对 x 进行右旋,意味着,将“x 的左孩子”设为“x 的父亲节点”;即,将 x 变成了一个右节点(x 成了为 y 的右孩子)! 因此,右旋中的“右”,意味着“被旋转的节点将变成一个右节点”。


java开源免费的加密算法工具包_java_33

java开源免费的加密算法工具包_服务器_34


22.1.6.1. 添加


第一步: 将红黑树当作一颗二叉查找树,将节点插入。


第二步:将插入的节点着色为"红色"。


根据被插入节点的父节点的情况,可以将"当节点 z 被着色为红色节点,并插入二叉树"划分为三 种情况来处理。


① 情况说明:被插入的节点是根节点。


处理方法:直接把此节点涂为黑色。


② 情况说明:被插入的节点的父节点是黑色。


处理方法:什么也不需要做。节点被插入后,仍然是红黑树。


③ 情况说明:被插入的节点的父节点是红色。这种情况下,被插入节点是一定存在非空祖父节点 的;进一步的讲,被插入节点也一定存在叔叔节点(即使叔叔节点为空,我们也视之为存在,空节点本身就是黑色节点)。理解这点之后,我们依据"叔叔节点的情况",将这种情况进一步划分为 3 种情况(Case)。




java开源免费的加密算法工具包_分布式_35


第三步: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。


22.1.6.2. 删除


第一步:将红黑树当作一颗二叉查找树,将节点删除。


这和"删除常规二叉查找树中删除节点的方法是一样的"。分 3 种情况:


① 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就 OK 了。


② 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。


③ 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给 “该节点的内容”;之后,删除“它的后继节点”。


第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。


因为"第一步"中删除节点之后,可能会违背红黑树的特性。所以需要通过"旋转和重新着色"来修正该树使之重新成为一棵红黑树。


选择重着色 3 种情况。


① 情况说明:x 是“红+黑”节点。


处理方法:直接把 x 设为黑色,结束。此时红黑树性质全部恢复。


② 情况说明:x 是“黑+黑”节点,且 x 是根。


处理方法:什么都不做,结束。此时红黑树性质全部恢复。


③ 情况说明:x 是“黑+黑”节点,且 x 不是根。


处理方法:这种情况又可以划分为 4 种子情况。这 4 种子情况如下表所示:


java开源免费的加密算法工具包_分布式_36


参考: https://www.jianshu.com/p/038585421b73


代码实现:


22.1.7. B-TREE


B-tree 又叫平衡多路查找树。一棵 m 阶的 B-tree (m 叉树)的特性如下(其中 ceil(x)是一个取上限的函数):


1. 树中每个结点至多有 m 个孩子;


2. 除根结点和叶子结点外,其它每个结点至少有有 ceil(m / 2)个孩子;


3. 若根结点不是叶子结点,则至少有 2 个孩子(特殊情况:没有孩子的根结点,即根结点为叶子结点整棵树只有一个根节点);


4. 所有叶子结点都出现在同一层,叶子结点不包含任何关键字信息(可以看做是外部结点或查询 失败的结点,实际上这些结点不存在,指向这些结点的指针都为 null);


5. 每个非终端结点中包含有 n 个关键字信息: (n,P0,K1,P1,K2,P2,......,Kn,Pn)。其 中:


a) Ki (i=1...n)为关键字,且关键字按顺序排序 K(i-1)< Ki。


b) Pi 为指向子树根的接点,且指针 P(i-1)指向子树种所有结点的关键字均小于 Ki,但都大于 K(i- 1)。


c) 关键字的个数 n 必须满足: ceil(m / 2)-1 <= n <= m-1。



java开源免费的加密算法工具包_服务器_37


一棵 m 阶的 B+tree 和 m 阶的 B-tree 的差异在于:


1.有 n 棵子树的结点中含有 n 个关键字; (B-tree 是 n 棵子树有 n-1 个关键字)


2.所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本 身依关键字的大小自小而大的顺序链接。 (B-tree 的叶子节点并没有包括全部需要查找的信息)


3.所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (B-tree 的非终节点也包含需要查找的有效信息)


java开源免费的加密算法工具包_java开源免费的加密算法工具包_38


参考: https://www.jianshu.com/p/1ed61b4cca12 13/04/2018


Page 254 of 283


22.1.8. 位图


位图的原理就是用一个 bit 来标识一个数字是否存在,采用一个 bit 来存储一个数据,所以这样可 以大大的节省空间。 bitmap 是很常用的数据结构,比如用于 Bloom Filter 中;用于无重复整数的 排序等等。bitmap 通常基于数组来实现,数组中每个元素可以看成是一系列二进制数,所有元素组成更大的二进制集合。




23. 加密算法


23.1.1. AES


高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。对称加密算法也就是加密和解密用相同的密钥,具体的加密流程如下图


java开源免费的加密算法工具包_分布式_39


23.1.2. RSA


RSA 加密算法是一种典型的非对称加密算法,它基于大数的因式分解数学难题,它也是应用最广 泛的非对称加密算法。


非对称加密是通过两个密钥(公钥-私钥)来实现对数据的加密和解密的。公钥用于加密,私钥用 于解密。


java开源免费的加密算法工具包_面试_40


java开源免费的加密算法工具包_java开源免费的加密算法工具包_41


23.1.3. CRC


循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或电脑文件等数据产生简短固定位数校验码的一种散列函数,主要用来检测或校验数据传输或者保存后可能出现的错误。


它是利用除法及余数的原理来作错误侦测的。


23.1.4. MD5


MD5 常常作为文件的签名出现,我们在下载文件的时候,常常会看到文件页面上附带一个扩展 名为.MD5 的文本或者一行字符,这行字符就是就是把整个文件当作原数据通过 MD5 计算后的值,我们下载文件后,可以用检查文件 MD5 信息的软件对下载到的文件在进行一次计算。两次结果对 比就可以确保下载到文件的准确性。 另一种常见用途就是网站敏感信息加密,比如用户名密码,支付签名等等。随着 https 技术的普及,现在的网站广泛采用前台明文传输到后台,MD5 加密 (使用偏移量)的方式保护敏感数据保护站点和数据安全。



24. 分布式缓存

24.1.1. 缓存雪崩


缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间所有原本应该访问缓存的请求都 去查询数据库了,而对数据库 CPU 和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列 连锁反应,造成整个系统崩溃。一般有三种处理办法:


1. 一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。


2. 给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓 存。


3. 为 key 设置不同的缓存失效时间。


24.1.2. 缓存穿透


缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在 缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请 求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。


有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用 布隆过滤器 ,将所有可能存在的数据哈 希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存 储系统的查询压力。另外也有一个更为简单粗暴的方法, 如果一个查询返回的数据为空(不管是数据不 存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。 通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了 ,而不会继续访问数据库。


24.1.3. 缓存预热


缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候, 先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!


24.1.4. 缓存更新


缓存更新除了缓存服务器自带的缓存失效策略之外(Redis 默认的有 6 中策略可供选择),我们还可以 根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:


(1)定时去清理过期的缓存;


(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数 据并更新缓存。


24.1.5. 缓存降级


当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然 需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开 关实现人工降级。降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的 (如加入购物车、结算)。