2021提前批-字节跳动班级同学提前批面经解析

    班内同学拿到字节跳动提前批意向书,羡慕羡慕,赶紧拿来面经学习一波学习过程中如有错误,欢迎私聊我纠正。

第一轮面试题

有了解过OAuth2.0么,说说你对OAuth2.0的理解

答:OAuth 2.0 协议是一种三方授权协议,目前大部分的第三方登录与授权都是基于该协议的标准或改进实现。

对其它的一些博客框架有了解么,比如hexo

答:hexo静态博客引擎的主要功能为将模板与数据通过程序进行拼接编译,生成可以浏览的网页。大量不同的数据与相同的模板拼接即可形成展示的静态网页组。多个网页组相结合即可生成静态网站。在 Hexo 中,source 文件夹和 themes 文件夹是在同级的,我们就可以将source文件夹理解为数据库,而主题文件夹相当于界面。然后我们 hexo g 就将我们的数据和界面相结合生成静态文件public。

谈谈Redis单线程模型和I/O多路复用

答:单线程架构是Redis基于Reator模式开发了自己的网络事件处理器:文件事件就是对套接字的抽象,每当一个套接字准备好执行连接、写入、读取、关闭等操作时,都会产生一个文件事件。Redis服务器会连接多个套接字,多个文件事件可能会并发出现。
IO多路复用模型:一个线程,通过记录IO流的状态来同时管理多个IO,可以提高服务器的吞吐能力。
I/O多路复用为异步阻塞 步骤如下:
(1)用户在一个进程/线程中,发出一个请求后,此时用户可以发送其他多个请求,同时收不到响应数据,就会阻塞在那里。
(2)此时用户会有一个监听,监听数据;
(3)如果请求的数据准备好了,会通知用户,然后用户再次发送请求,来获取改数据。
I/O多路复用程序:
(1)负责监听多个套接字,按照处理顺序,将套接字存放在一个队列中。
(2)套接字队列以有序、同步、每次一个套接字的方式向文件事件分派器传送套接字。
(3)当上一个套接字产生的事件被处理完毕之后,I/O多路复用程序才会继续向文件事件分派器传送下一个套接字。
注:所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。

cat、tail、vi、vim命令的区别,分别说一说?

答:Cat不单单是查看文件内容,还可以创建文件或者附加文件内容;vi/vim可以查看文件的同时也可以对文件内容进行修改,且可以进行文件内容搜索;tail 命令常用于查看文件的最后几行命令,并且不停的刷新,比较适合实时观察程序的日志情况。

如果Linux下需要打开或者查看大文件,你会怎么做?

答:(1)查看文件的前多少行 head -1000 (2)查看文件的后多少行 tail -1000 (3)查看文件的几行到几行 sed -n ‘10,10000p’

1
2
3
head -1000 /var/lib/mysql/slowquery.log > temp.log //前1000行写入  
tail -1000 /var/lib/mysql/slowquery.log > temp.log //后1000行写入
sed -n '10,10000p' /var/lib/mysql/slowquery.log > temp.log //写入文件的第10到10000行

Linux你平时都有用到什么命令呢

touch aa.file 创建文件;
FIND / -NAME “bash_profile” 查找文件;
cp redis-serever redis-cli /usr/local/redis 拷贝;
ps aux | less 查看后台运行文件;
netstat -tunlp|grep 3306 查看某个端口的进程;
ps -el |grep mysql 查看端口号;
ps -ef| grep kafka 查看kafka对应的进程号;

下面聊聊Http Code,你知道 3XX 状态码 对应的是什么?

300 客户端请求一个实际指向多个资源的URL时会返回这个状态码。
301 重定向,在请求的URL已被移除时使用。响应的Location首部中应该包含资源现在所处的URL。
302 与301状态码类似,但是客户端应该使用Location首部给出的URL来临时定位资源,将来的请求仍应使用老的URL。

在谈谈你知道的其它一些状态码,4XX 和 5XX?

3XX类状态码信息表示:客户端浏览器必须采取更多操作来实现请求。例如,浏览器可能不得不请求服务器上的不同页面,或者通过代理服务器重复该请求;
4XX类状态码信息表示:发生错误,客户端似乎有问题。例如:客户端请求不存在的页面,客户端为提供有效的身份验证信息;
5XX类状态码信息表示:服务器遇到错误而不能完成该请求;

第二轮面试题

博客已经开源了么,用的什么开源协议?

答:我们在 Github 上创建一个开源项目时,新建一个名为 LICENSE 的文件时,就会弹出选择开源协议的按钮,我们点进去就可以看到,Github 默认支持的协议模板。点击协议会有详细的介绍。常用的有GPL2.0、GPL3.0、Apache Licence 2.0等
我使用到的GPL2.0,其协议必须遵守
(1) 使用 / 修改 / 衍生 GPL 类库的代码或软件,必须也采用 GPL 协议进行开源
(2) 项目开源后可以再增加其他开源协议,但是协议必须比 GPL 宽泛
(3) 不提供品质担保,使用采用此协议的软件产生的任何后果都不会负责
(4) 可以用于商业,但是必须开放源码

谈谈Solr的原理,以及倒排索引?

答:Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。
倒排索引:每个分词的单词对应所在的文档列表(倒排列表)

聊聊MySQL的Myisam和Innodb存储引擎的区别?

MyISAM事务不是安全的,且不支持外键;
Innodb支持事务安全的引擎,它是存储记录和文件的标准方法,支持外键、行锁和事务。如果有大量的Update、Insert且针对多个并发和QPS较高的情况。
(1)存储结构:
(2)存储空间:
(3)可移植性、备份及恢复:
(4)事务支持:
(5)AUTO_INCREMENT:MyISAM:可以和其他字段一起建立联合索引。引擎的自动增长列必须是索引,如果是组合索引,自动增长可以不是第一列,他可以根据前面几列进行排序后递增。
InnoDB:InnoDB中必须包含只有该字段的索引。引擎的自动增长列必须是索引,如果是组合索引也必须是组合索引的第一列。
转至链接:https://www.jianshu.com/p/dc60346d55a2
(6)表锁差异:
(7)全文索引:
MyISAM:支持 FULLTEXT类型的全文索引。
InnoDB:不支持FULLTEXT类型的全文索引,但是innodb可以使用sphinx插件支持全文索引,并且效果更好。
(8)表主键:
MyISAM:允许没有任何索引和主键的表存在,索引都是保存行的地址。
InnoDB:如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见),数据是主索引的一部分,附加索引保存的是主索引的值。
(9)表的具体行数:
MyISAM:保存有表的总行数,如果select count( ) from table;会直接取出出该值。
InnoDB:没有保存表的总行数,如果使用select count(
) from table;就会遍历整个表,消耗相当大,但是在加了wehre条件后,myisam和innodb处理的方式都一样。
(10)CURD操作:
MyISAM:如果执行大量的SELECT,MyISAM是更好的选择。
InnoDB:如果你的数据执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表。DELETE 从性能上InnoDB更优,但DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除,在innodb上如果要清空保存有大量数据的表,最好使用truncate table这个命令。
(11)外键:

聊聊MySQL的乐观锁和悲观锁?

答:乐观锁:通常不是数据库自带的,需要自己去实现。实现:在表中的数据进行操作时,先给数据表加一个版本字段,每操作一次,将那条记录的版本号加1。如果要对那条记录进行操作,先查询之前的版本号是否和现在查出来的数据相等,如果相等可更新,不相等,则回退。(在数据表中加一个version字段)
悲观锁:跟JAVA中的synchronized很相似,由数据库自己实现。涉及到的两个概念,共享锁和排它锁
共享锁:lock in share mode实现,共享锁指的是对于多个不同的事务,对同一个资源共享同一个锁。
排它锁:for update实现。指对于多个不同的事务,对同一个资源只能有一把锁。
行锁:就是给某一行加上锁,也就是一条记录加上锁。开销小,加锁快,不会出现死锁;锁定力度大,发生锁冲突概率搞,并发度最低。
表锁:给这个表上加锁。加锁慢,会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高。

表锁和行锁应用场景

表级锁使用与并发性不高,以查询为主,少量更新的应用,比如小型的web应用; 而行级锁适用于高并发环境下,对事务完整性要求较高的系统,如在线事务处理系统。 以上就是行锁、表锁等的特点和应用场景。
行锁注意:没有索引或者索引失效时,InnoDB 的行锁变表锁。

聊聊MySQL的底层索引结构,InnoDB里面的B+Tree?

答: B树和平衡二叉树稍有不同的是B树属于多叉树又名平衡多路查找树,B+树是B树的一个升级版,相对于B树来说B+树更充分的利用了节点的空间,让查询速度更加稳定,其速度完全接近于二分法查找。
(1)B+树的层级更少:相较于B树B+每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快;
(2)B+树查询速度更稳定:B+所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同所以查询速度要比B树更稳定;
(3)B+树天然具备排序功能:B+树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B树高。
(4)B+树全节点遍历更快:B+树遍历整棵树只需要遍历所有的叶子节点即可,,而不需要像B树一样需要对每一层进行遍历,这有利于数据库做全表扫描。
B树相对于B+树的优点是,如果经常访问的数据离根节点很近,而B树的非叶子节点本身存有关键字其数据的地址,所以这种数据检索的时候会要比B+树快。

聊聊MySQL索引的发展过程

mysql的索引从数据结构可以分为B+树索引,Hash索引,空间索引和全文索引。
hash索引能实现高速的精确搜索,这是其他索引不能比的。Innodb隐式的支持“自适应哈希索引”,(当注意到一些索引值使用的非常频繁,且符合哈希特点,它会在内存中基于B-Tree索引之上再创建一个哈希索引,自动的行为,其不支持where范围查询,只支持等值比较查询)
BTree索引为一颗多叉平衡搜索树,左右字树高度差小于等于1。其缺点:
(1)无法定位到数据行。
(2)无法处理范围查询。通过定位索引位置,可以先后查询范围的左右界获得,这样操作无法很好的利用磁盘预读的局部性原理,先后查询可能会造成通过预读取的物理地址离散,使得I/O的效率不高。
(3)当数据量一大时,BTree的高度依旧会变得很高。
B+树索引由于其数据结构的特点,使得其支持丰富的索引特性,优化搜索。 特点:
(1)非叶子节点只存储键值信息,不再存储数据。
(2)所有叶子节点之间都有一个链指针,指向下一个叶子节点
(3)数据都存放在叶子节点中
优势:(1)I/O 次数更少;(2)数据遍历更为方便;(3)查询性能更稳定。
聚集索引:主键索引的叶子节点存储的是键值对应的数据本身。辅助索引的叶子结点存储的是键值对应的数据的主键键值。它的每个叶子节点都包含主键值、事务ID、用于事务和MVCC的回滚指针以及所有剩余列。
覆盖索引:覆盖索引使得只访问索引的查询会很快。??? 索引覆盖是一种避免回表查询的优化策略。 具体的做法就是将要查询的数据作为索引列建立普通索引(单列索引或者联合索引,直接返回索引中的的数据,不需要再通过聚集索引去定位行记录,避免了回表的情况发生。)覆盖索引必须要存储索引列的值,所以mysql只能用B-tree索引做覆盖索引。
using index :使用覆盖索引的时候就会出现
using where:未使用索引,需要全表查询
using index condition:查找使用了索引,但是需要回表查询数据
using index & using where:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据。
————————————————
原文链接:https://blog.csdn.net/w1014074794/article/details/89886068
复合索引:也就是多列索引即索引包含多个列。究其原因是复合索引能通过前面的索引项减小搜索范围。

MySQL里面的事务

事务用于处理操作量大,复杂度高的数据
(1)Mysql使用Innodb存储引擎的数据库或表才支持事务
(2)事务处理可以用来维护数据库的完整性,保证成批的SQL语句要么全部执行,要么全部不执行
(3)事务用来管理insert,update,delete语句
其事务必须满足4个条件。原子性(要么全部完成,要么回滚到最初始的状态),一致性,隔离性{包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)}和持久性
下面重点介绍隔离性:
(1)READ UNCOMMITTED(读未提交):事务一未提交的内容,事务二看得到为更新之前的,只有当事务一commit之后,事务二才能看到。
(2)REPEATABLE READ(可重复读):事务一未提交的内容,事务二看不到,此时在事务二插入一条数据,会报主键冲突(幻读)。只有当事务二commit之后,才能看到。
(3)SERIALIZABLE(序列化):该隔离级别下事务都是串行顺序执行的,MySQL 数据库的 InnoDB 引擎会给读操作隐式加一把读共享锁,从而避免了脏读、不可重读复读和幻读问题。
脏读解释:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
不可重复读解释:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
幻读解释:第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

MySQL里面有那些事务级别,并且不同的事务级别会出现什么问题

| 隔离级别 | 脏读(Dirty Read| 不可重复读(NonRepeatable Read)| 幻读(Phantom Read)
| 未提交读 | 可能            | 可能                          |可能|
| 已提交读 | 不可能          | 可能                          |可能|
| 可重复读 | 不可能          | 不可能                        |可能|
| 可串行化 | 不可能          | 不可能                        |不可能|

可重复读和幻读的区别

答:幻读指的是在同一事务下,连续执行两次同样的SQL语句第二次的SQL语句可能返回之前不存在的行;
可重复读指的是同一行在同一个事务下无论怎么读取都是同一个结果(除非自己把它改了);

MySQL中如果使用like进行模糊匹配的时候,是否会使用索引?一定不会用么?

like语句要使索引生效,like后不能以%开始,也就是说 (like %字段名%) 、(like %字段名)这类语句会使索引失效,而(like 字段名)、(like 字段名%)这类语句索引是可以正常使用。

Java NIO的原理:

答:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的链接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。soceket主要的读写、注册和接受函数,在等待就绪阶段都是非阻塞的,真正的I/O操作是同步阻塞的。
将每个连接(IO通道)都注册到Selector多路复用器上,告诉复用器我已经连接了,如果有IO事件发生,你就通知我。Selector就不断地调用select函数,去访问每一个通道看那个通道有IO事件发生,如果发生了就通知,内核开启一个对应事件的线程去工作

配置过java启动设置吗?

JVM在1.8之后不会出现永久代空间不够而抛出OOM异常了,在1.8之前jdk版本的class和jar包数据存储在permGen下面 ,permGen大小是固定的,而且项目之间无法共用公有的class,所以很容易碰到OOM异常。
答:快手比赛的时候需要配置,详细见:

1
-XX:+UseG1GC -XX:MaxGCPauseMillis=500  -Xss256k -Xms6G -Xmx6G -XX:MaxDirectMemorySize=1G

+UseG1GC 使用 G1 (Garbage First) 垃圾收集器
Xss 每个线程的堆栈大小
Xms 程序程序启动时占用内存大小
Xmx 程序运行期间最大可占用的内存大小
MaxGCPauseMillis 为所需的最长暂停时间设置目标值
MaxDirectMemorySize New I/O(java.nio) direct-buffer allocations的最大大小
JVM运行时开启详细GC的日志: -XX:+PrintGCDetails

1
[GC (Allocation Failure) [PSYoungGen: 262976K->1216K(427520K)] 270005K->8917K(513536K), 0.0038406 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

解析:GC表示一次YGC(Young GC),Allocation Failure表示失败的类型,PSYoungGen表示年轻代大小,262976K->1216K表示年轻代占用从
262976K降为1216K 427520K表示年轻代的大小,270005K->8917K表示整个堆占用从270005K降为8917K,270005K表示整个堆的大小。0.0038406 secs 表示这次GC总计所用的时间,[Times: user=0.02 sys=0.01, real=0.00 secs] 分别表示,用户态占用时长,内核用时,真实用时。
下面介绍一下GC的过程:
(1)新new的对象都放在Eden区;
(2)Eden区满或者快满的时候进行一次清理(Young GC/Minor Gc)
不被引用的对象直接被干掉;还有引用的对象,但是年龄比较大的,挪到S0区;
(3)下次Eden区快满的时候,会进行上一步的操作,并且将Eden和S0区的年纪大的对象放到S1区【原理上随时保持S0和S1有一个是空的,用来存下一次的对象】;
(4)下下次,Eden区快满的时候,会进行上一步操作,并且将Eden和S1区的年纪大的对象放到S0区【此时S1区就是空的】;
(5)直到Eden区快满,S0或者S1也快满的时候,这时候就把这两个区的年纪大的对象放到Old区;
(6)依次循环,直到Old区也快满的时候,Eden区也快满的时候,会对整个这一块内存区域进行一次大清洗(FullGC),腾出内存,为之后的对象创建,程序运行腾地方。

聊聊 JVM的组成结构

线程私有:程序计数器、虚拟机栈、本地方法栈
线程共享:堆、方法区
程序计数器:是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念里字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
虚拟机栈:是每个线程私有,线程运行时,在执行每个方法的时候都会打包成一个栈帧,存储局部变量表,操作数据栈,动态链接,方法出口等信息,然后放入栈。每个时刻正在执行的当前方法就是虚拟机栈顶的栈桢。方法的执行就对应着栈帧在虚拟机栈中入栈和出栈的过程。 可使用-Xss调整大小,默认为1M
本地方法栈:虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则是为虚拟机使用到的Native方法服务,通过JNI调用到了底层的C/C++。当一个JVM创建的线程调用native方法后,JVM不再为其在虚拟机栈中创建栈帧,JVM只是简单地动态链接(栈帧会持有一个引用,即符号引用。符号引用在每次运行期间转换为直接引用,即每次运行都重新转换。)并直接调用native方法。
方法区:要存储类信息、常量池、静态变量、即时编译期编译后的代码等数据。永久代和元空间,方法区在 jdk1.7 及其之前又背称为永久代,jdk1.8 又被称为元空间。
jdk1.7 及以前:-XX:PermSize;-XX:MaxPermSize;
jdk1.8 以后:-XX:MetaspaceSize; -XX:MaxMetaspaceSize
jdk1.8 以后大小就只受本机总内存的限制
:几乎所有对象都分配在堆内存,也是垃圾回收发生的主要区域
堆内存由多个线程共享。堆内存随着JVM启动而创建

类加载的过程

标准I/O(缓存IO)

缓存IO又称为标准IO,大多数文件系统的默认IO操作都是缓存IO,在Linux的缓存IO机制中,操作系统会将IO的数据缓存在文件系统的页缓存中,即数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核缓冲区拷贝到应用程序的内存地址。

谈谈垃圾收集原理?以及垃圾收集算法

垃圾收集器它主要关注的是线程共享的部分(堆、方法区),线程共享部分由于被多线程共享,所以要确定哪些对象数据是可回收的。下面介绍垃圾收集器判断对象存活的算法。
对象可回收算法-根搜索算法(GC Roots Tracing):定义一个叫GC Roots列表,凡是在这个列表中存在的强引用对象或根这个列表中的强引用对象有关联的其它强引用对象均为存活对象,如果某个强引用对象不存在这个GC Roots列表也没有跟任何GC Roots列表强引用对象有关联,则称为从GC Roots到这个对象不可达,这种对象会被标记为下次垃圾收集时回收目标对象。
强引用:通过直接new出来的对象就是强引用类型对象。
软引用:通过new SoftReference(…)包装的对象为软引用,当内存不够时,这类对象将被无条件回收。
弱引用:通过new WeakReference(…)包装的对象为弱引用对象,这类对象在下次垃圾收集器工作时无条件回收。
虚引用:通过new PhantomReference(…)包装的对象为虚引用对象,这类对象引用是无法使用的。
垃圾收集算法:
(1)标记 - 清除算法:分为标记和清除两个阶段,首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
缺点:标记和清除的效率都很低;标记清除之后会产生大量不连续的内存碎片,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
(2)复制算法:该算法针对Java堆新生代内存设计。内存按照容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存使用完了,就将还存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。
(3)标记-整理算法:该算法主要是针对java堆老年代的的对象特点来设计。标记过程与“标记-清除”算法的标记过程相同,但是后续步骤不是直接对可回收的对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

有了解过Volatile么?谈谈你对Volatile的理解

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。

Volatile如何保证可见性的?以及如何实现可见性的机制

(1)将主存中的数据加载到缓存中
(2)CPU对缓存中的数据进行修改
(3)将修改后的值刷新到内存中

如果大量的使用Volatile存在什么问题

volatile的作用是很微妙的,它并不能替代synchronized,因此它无法提供同步的能力,它只能提供改变可见性的能力。
由于总是读写与主存,它的读写性能要低于普通的变量。
正确使用的模式总结下来就是一个线程写,多个线程读。
但也不要过于迷信它的功效,大部分情况下,都完全不需要使用这个关键字的。

手撕代码# 链表的两两翻转

给定链表: 1->2->3->4->5->6->7
返回结果: 2->1->4->3->6->5->7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public ListNode swapPairs(ListNode head){  
// 链表头增加虚拟结点 dummy
ListNode dummy = new ListNode(-1);
dummy.next = head;
head = dummy;
// 循环退出条件,注意链表结点数单双的情况
while(head.next != null && head.next.next != null){
// 开始反转
ListNode a = head.next;
ListNode b = a.next;
head.next = b; // 步骤①
a.next = b.next; // 步骤①
b.next = a; // 步骤②
// dummy 指针前移
head = a;
}
return dummy.next;
}

第三轮面试题

hash和一致性hash的区别,为什么要用一致性hash

普通hash算法:直接寻址法、除留余数法、随机数法等
一致性hash算法:普通hash算法在分布式环境下会遇到,如果初始算法为hash%4,则如果其中一台机器宕机的话,就需要改成hash%3算法,则所有的数据都要重新计算存储的位置,所以开销比较大。
一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0–2^32-1(即哈希值是一个32位无符号整形)

总结

坚持原创技术分享,您的支持将鼓励我继续创作!