【面试】后端开发需要懂得奇奇怪怪的知识点 (2)

177 阅读24分钟

==================================================

nginx接入层限流 nginx中针对限流有两个模块可以处理: 1)ngx_http_limit_req_module 连接数限流模块,和一般的计数器限流是一样的思路,通过一个key来存取对应的value, value表示请求的数量。比如:限制某个IP的总请求数量,那么我们只需要改变和存取、 该IP对应的请求数量即可。 2)ngx_http_limit_conn_module 请求限流模块实现的是漏桶算法,主要是对请求的速率进行限制。

在限流特技中,常见的限流算法有两种: 1)令牌桶算法 令牌产生的速度是均匀的,也就是说系统能够接收的请求是均匀的 令牌总数不能超过桶的最大容量 发起一个请求,如果桶里面有足够的令牌,那么该请求就能够被处理(同时删除桶里 面的一个令牌),否则被丢弃或者缓存 令牌产生的速度是均匀的,所以接收的请求是均匀的,但是被处理的请求却不一定均匀 令牌桶算法是允许瞬间爆发的请求数量的 只要令牌桶里面有足够的令牌,那么请求都能够被处理,令牌桶算法只是限制了请求的 接收而不是请求的处理。

2)漏桶算法 接收请求除了容器最大值,是没有速度限制的 容器里面的请求则是按照固定的速度均匀的被处理

令牌桶和漏桶算法的区别 1、令牌桶限制的是流入(接收请求),漏桶限制的是流出(处理请求) 2、由于漏桶限制的是流出,所以相对令牌桶来说平滑了流入的速率 3、令牌桶算法允许瞬间爆发,而漏桶算法处理请求的速度永远是一致的 4、两种算法只是实现方案不同,如果参数相同的话,最终限流效果是一样的

==================================================

HyperLogLog 基数计数(cardinality counting)通常用来统计一个集合中不重复的元素个数 HLL中实际存储的是一个长度为mm的大数组SS,将待统计的数据集合划分成mm组, 每组根据算法记录一个统计值存入数组中。数组的大小mm由算法实现方自己确定, redis中这个数组的大小是16834,mm越大,基数统计的误差越小,但需要的内存空间也越大。

伯努利过程 分桶平均 偏差修正 在Redis里面,每个HyperLogLog键只需要花费12KB内存,就可以计算 接近2^64个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

基数的应用实例: PV,UV等流量指标的计算 传统的基数计数实现: B树或者bitmap

==================================================

哨兵集群中哨兵实例之间可以相互发现,基于Redis提供的发布/订阅机制(pub/sub 机制),

在已判定主库已下线后,又如何选举出新的主库? 新主库选举按照一定条件筛选出的符合条件的从库,并按照一定规则对其进行打分,最高分者为新主库。

通常一定条件包括: 1.从库的当前在线状态, 2.判断它之前的网络连接状态,通过down-after-milliseconds * num(断开连接次数),当断开连接次数超过阈值,不适合为新主库。

一定规则包括: 1.从库优先级 , 通过slave-priority 配置项,给不同的从库设置不同优先级,优先级最高的从库得分高 2.从库复制进度,和旧主库同步程度最接近的从库得分高,通过repl_backlog_buffer缓冲区记录主库 master_repl_offset 和从库slave_repl_offset 相差最小高分 3.从库 ID 号 , ID 号小的从库得分高。

全都都基于在只有在一定规则中的某一轮评出最高分从库就选举结束,哨兵发起主从切换。

Redis是4.0以上版本,用 UNLINK 命令替代 DEL,此命令可以把释放 key 内存的操作,放到后台线程中去执行,从而降低对 Redis 的影响 Redis是6.0以上版本,可以开启 lazy-free 机制(lazyfree-lazy-user-del = yes),在执行 DEL 命令时,释放内存也会放到后台线程中执行

Redis的过期数据采用被动过期 + 主动过期两种策略: 被动过期: 只有当访问某个 key 时,才判断这个 key 是否已过期,如果已过期,则从实例中删除

主动过期: Redis 内部维护了一个定时任务,默认每隔 100 毫秒(1秒10次)就会从全局的过期哈希表 中随机取出 20 个 key,然后删除其中过期的 key,如果过期 key 的比例超过了 25%,则继 续重复此过程,直到过期 key 的比例下降到 25% 以下,或者这次任务的执行耗时超过了 25 毫秒,才会退出循环

最常使用的是 allkeys-lru / volatile-lru 淘汰策略,它们的处理逻辑是, 每次从实例中随机取出一批 key(这个数量可配置),然后淘汰一个最少访问的 key, 之后把剩下的 key 暂存到一个池子中,继续随机取一批 key,并与之前池子中的 key 比较, 再淘汰一个最少访问的 key。以此往复,直到实例内存降到 maxmemory 之下。

内存大页 应用程序向操作系统申请内存时,是按内存页进行申请的,而常规的内存页大小是 4KB。 Linux 内核从 2.6.38 开始,支持了内存大页机制, 该机制允许应用程序以 2MB 大小为单位,向操作系统申请内存。 应用程序每次向操作系统申请的内存单位变大了,但这也意味着申请内存的耗时变长。

==================================================

MVCC MVCC,全称Multi-Version Concurrency Control,即多版本并发控制 MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突, 做到即使有读写冲突时,也能做到不加锁,非阻塞并发读

当前读 像select lock in share mode(共享锁), select for update ; update, insert ,delete (排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读 取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁

快照读 像不加锁的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行 级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能 的考虑,快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但 它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的 并不一定是数据的最新版本,而有可能是之前的历史版本

说白了MVCC就是为了实现读-写冲突不加锁,而这个读指的就是快照读, 而非当前读,当前读 实际上是一种加锁的操作,是悲观锁的实现

MVCC多版本并发控制指的是 “维持一个数据的多个版本,使得读写操作没有冲突”

快照读就是MySQL为我们实现MVCC理想模型的其中一个具体非阻塞读功能。而相对而言,当前读 就是悲观锁的具体功能实现

数据库并发场景有三种,分别为: 读-读:不存在任何问题,也不需要并发控制 读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读 写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失

MVCC是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个 修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。

解决以下问题 1.在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作, 提高了数据库并发读写的性能 2.可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题

实现原理是依赖记录中的 3个隐式字段,undo日志 ,Read View

隐式字段 DB_TRX_ID 最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID DB_ROLL_PTR 回滚指针,指向这条记录的上一个版本 DB_ROW_ID 隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引 flag 删除flag隐藏字段, 既记录被更新或删除并不代表真的删除,而是删除flag变了

undo日志 分为两种: insert undo log 代表事务在insert新记录时产生的undo log, 只在事务回滚时需要,并且在事务提交后可以 被立即丢弃

update undo log 事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需 要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被 purge线程统一清除

purge 为了实现InnoDB的MVCC机制,更新或者删除操作都只是设置一下老记录的deleted_bit,并不 真正将过时的记录删除。 为了节省磁盘空间,InnoDB有专门的purge线程来清理deleted_bit为true的记录。

Read View(读视图) Read View就是事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照 读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个 事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大)

MVCC只在repeatable read和read committed两个隔离级别下工作。其他两个隔离级别和MVCC不 兼容。因为READ UNCOMMITTED 总是读取最新的数据行,而不是符合当前事务版本的数据行。 而SERIALIZABLE 则会对所有读取的行都加锁。

==================================================

第一类更新丢失 - 回滚覆盖 指由于某个事务的回滚操作,参与回滚的旧数据将其他事务的数据更新覆盖了。 目前主流数据库支持的所有的隔离级别基本杜绝了第一类数据更新丢失的问题。

第二类数据丢失 - 更新覆盖 关于多个事务同时更新一行数据导致的问题,实际操作使用不当会出现问题。 解决方式:乐观锁 悲观锁

乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式 对数据的冲突与否进行检测,如果发现冲突了,则返回错误。 使用数据版本(Version)记录机制实现,即为数据增加一个版本标识,一般是通过为数据库表 增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出, 数据每更新一次,对此version值加1。当我们提交更新的时候,判断数据库表对应记录的当前 版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的 version值相等,则予以更新,否则认为是过期数据,不更新返回失败。

悲观锁就是锁定要更新的这一行,然后在事务提交之前不让其他事务对该行数据做任何 操作,直至释放行锁。

==================================================

1、位 bit 数据存储的最小单位。每个二进制数字0或者1就是1个位

2、字节 byte 是计算机中数据处理的基本单位,8个位构成一个字节;即:1 byte (字节)= 8 bit(位) 1 KB = 1024 B(字节) 1 MB = 1024 KB; (2^10 B) 1 GB = 1024 MB; (2^20 B) 1 TB = 1024 GB; (2^30 B)

3、字符 指计算机中使用的字母、数字、字和符号 a、A、中、+、*、の......均表示一个字符    ASCII码:一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。 一个二进制数字序列,在计算机中作为一个数字单元,一般为8位二进制数,换算为十进制。 最小值0,最大值255。如一个ASCII码就是一个字节。    UTF-8编码: 一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。    Unicode编码:一个英文字符等于两个字节,一个中文(含繁体)等于两个字节。

4、字符集 各种各个字符的集合,也就是说哪些汉字,字母(A、b、c)和符号(空格、引号..)会被收入标准中;

5、编码 规定每个“字符”分别用一个字节还是多个字节存储,用哪些字节来存储,这个规定就叫做“编码”。 编码就是按照规则对字符进行翻译成对应的二进制数,在计算器中运行存储,用户看的时候在用对应的编码解析出来用户能看懂的;

==================================================

php8新特性 1.命名参数 开发者可以按自己的意愿更改参数顺序 2.注解语法 通过重复使用现有标记T_SL和T_SR,注解是用“ <<”和“ >>”括起来的特殊格式的文本。 3.构造函数参数改进 4.联合类型(Union types) 当给函数传参,参数可能有多重类型 5.匹配表达 对switch语法的改进 6.空安全运算符 7.字符串和数字比较 在php8中,数字和字符串比较时,会将数字转成字符串,正好和之前相反。 8.函数内部一致性校验错误 php8如果参数验证失败,大多数内部函数将引发error异常,原来是warning级别 9.最重要的JIT(just in time) 即时编译功能,在综合基准测试中的性能提高了大约3倍

==================================================

HTTPS优势: 数据隐私性:内容经过对称加密,每个连接生成一个唯一的加密密钥 数据完整性:内容传输经过完整性校验 身份认证:第三方无法伪造服务端(客户端)身份

TLS/SSL 的功能实现主要依赖于三类基本算法:散列函数 、对称加密和非对称加密 其利用非对称加密实现身份认证和密钥协商 对称加密算法采用协商的密钥对数据加密 基于散列函数验证信息的完整性

HTTPS将对称加密与非对称加密结合起来,充分利用两者各自的优势,在交换密钥环节使用非对称加密方式,之后的建立通信交换报文阶段则使用对称加密方式。

具体做法是:发送密文的一方使用对方的公钥进行加密处理“对称的密钥”,然后对方用自己的私钥解密拿到“对称的密钥”,这样可以确保交换的密钥是安全的前提下,使用对称加密方式进行通信。所以,HTTPS采用对称加密和非对称加密两者并用的混合加密机制。

数字签名解决报文可能遭篡改问题,有两种功效: 1.能确定消息确实是由发送方签名并发出来的,因为别人假冒不了发送方的签名。 2.数字签名能确定消息的完整性,证明数据是否未被篡改过。

过程:将一段文本先用Hash函数生成消息摘要,然后用发送者的私钥加密生成数字签名,与原文文一起传送给接收者,接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用HASH函数对收到的原文产生一个摘要信息,与上一步得到的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过,因此数字签名能够验证信息的完整性。

数字证书解决通信方身份可能被伪装的问题,数字证书认证机构处于客户端与服务器双方都可信赖的第三方机构的立场上。

HTTPS工作流程 1.Client发起一个HTTPS的请求,根据规定,Client知道需要连接Server的443端口。 2.Server把事先配置好的公钥证书(public key certificate)返回给客户端。 3.Client验证公钥证书:如是否在有效期内,证书是否匹配Client请求的站点,是不是在CRL吊销列表里面,它的上一级证书是否有效, 递归验证到根证书为止(操作系统内置的Root证书或者Client内置的Root证书)。如果验证通过则继续,不通过则显示警告信息。 4.Client使用伪随机数生成器生成加密所使用的对称密钥,然后用证书的公钥加密这个对称密钥,发给Server。 5.Server使用自己的私钥(private key)解密这个消息,得到对称密钥。至此,Client和Server双方都持有了相同的对称密钥。 6.Server使用对称密钥加密“明文内容A”,发送给Client。 7.Client使用对称密钥解密响应的密文,得到“明文内容A”。 8.Client再次发起HTTPS的请求,使用对称密钥加密请求的“明文内容B”,然后Server使用对称密钥解密密文,得到“明文内容B”。

==================================================

CDN,Content Delivery Network,即内容分发网络,其目的是通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最 接近用户的网络“边缘”,使用户可以就近取得所需的内容,提高用户访问网站的响应速度。

依赖技术 1.负载均衡技术 2.动态内容分发与复制技术 3.缓存技术等。

工作原理 CDN网络是在用户和服务器之间增加Cache层,主要是通过接管DNS实现,将用户的请求引导到Cache上获得源服务器的数据,从而降低网络的访问时间。

工作流程 1.用户输入访问的域名,操作系统向 LocalDns 查询域名的ip地址; 2.LocalDns向 ROOT DNS 查询域名的授权服务器(假设LocalDns缓存过期); 3.ROOT DNS将域名授权dns记录回应给 LocalDns; 4.LocalDns得到域名的授权dns记录后,继续向域名授权dns查询域名的ip地址; 5.域名授权dns 查询域名记录后(一般是CNAME),回应给 LocalDns; 6.LocalDns 得到域名记录后,向智能调度DNS查询域名的ip地址; 7.智能调度DNS 根据一定的算法和策略(如静态拓扑,容量等),将最适合的CDN节点ip地址回应给 LocalDns; 8.LocalDns 将得到的域名ip地址,回应给用户端; 9.用户得到域名ip地址后,访问站点服务器,若该ip对应的节点缓存有资源,则会将数据直接返回给用户,请求结束。 若该节点未缓存资源,则节点会向业务源站发起对原始资源的请求(重复6,7步,向上级进行查询),获取资源后,结合预先配置的缓存策略,将资源存储到当前节点(当然也有可能不缓存),并返回给用户后请求结束。

==================================================

CNAME:别名记录,这种记录允许您将多个名字映射到另外一个域名。 LocalDNS:提供缓存和递归的服务。 授权DNS:提供权威解析。

如何解决DNS劫持 HTTPDNS 利用 HTTP 协议与 DNS 服务器交互,代替了传统的基于 UDP 协议的 DNS 交互,绕开了运营商的 Local DNS(本地 DNS 服务器),有效防止了域名劫持,提高域名解析效率,能够精确定位客户端地理位置、运营商信息,从而有效改进调度精确性。

==================================================

标准的布隆过滤器的一大限制是不能删除已经存在的数据。如果使用它的变种,比如Counting Bloom Filter,空间却被撑大了3到4倍。

Cuckoo hashing,布谷鸟hash 工作原理: 它有两个 hash 表,记为 T1,T2,两个 hash 函数,记为 h1,h2。 当一个不存在的元素插入的时候,会先根据 h1 计算出其在 T1 表的位置,如果该位置为空则可以放进去。 如果T1该位置不为空,则根据 h2 计算出其在 T2 表的位置,如果该位置为空则可以放进去。 如果T2该位置不为空,就把当前位置上的元素踢出去,然后把当前元素放进去就行了。 也可以随机踢出两个位置中的一个,总之会有一个元素被踢出去,被踢出去的元素,将会放置到自己的另外一个位置。 但是以此类推,总是有出现循环踢出导致放不进x的问题 当循环相互踢出的过程执行次数超过一个阈值MaxLoop时,说明布谷鸟hash已经到了极限情况,应该进行扩容,或者hash函数的优化。 布谷鸟hash实质是一种解决hash冲突的另类操作

与标准布隆过滤器相比,布谷鸟过滤器支持删除,在空间或性能上并不需要更高的开销。 优势: 1.支持动态的新增和删除元素。 2.提供更高的查找性能,即使在空间接近满的情况下。 3.比诸如商过滤器(quotient filter,另一种过滤器),更容易实现。 4.占用的空间更小。

布谷鸟过滤器的限制 其强制数组的长度必须是2的指数倍,2的指数倍的二进制是这样的:10000000...(n个0)。 这个限制带来的好处就是,进行异或运算时,可以保证计算出来的下标一定是落在数组中的。

==================================================

Lucene 是 Elasticsearch所基于的 Java 库,它引入了按段搜索的概念。 Elasticsearch ,一个特点是 near real-time 准实时 —— 当文档存储在Elasticsearch中时,将在1秒内以几乎实时的方式对其进行索引和完全搜索

写入过程 1.不断将 Document 写入到 In-memory buffer (内存缓冲区)。 2.当满足一定条件后内存缓冲区中的 Documents 刷新到 高速缓存(cache)。 3.生成新的 segment ,这个 segment 还在 cache 中。 4.这时候还没有 commit ,但是已经可以被读取了。 5.数据从buffer到cache的过程是定期每秒刷新一次。所以新写入的Document最慢1秒就可以在cache中被搜索到。 Document从buffer到cache的过程叫做refresh。一般是1秒刷新一次,这也就是为什么说Elasticsearch是准实时的。

Translog事务日志 类似Mysql的binlog 1.Document不断写入到In-memory buffer,此时也会追加translog。 2.当buffer中的数据每秒refresh到cache中时,translog并没有进入到刷新到磁盘,是持续追加的。 3.translog每隔5s会fsync到磁盘。 4.translog会继续累加变得越来越大,当translog大到一定程度或者每隔一段时间(默认30分钟),会执行flush。

倒排索引(Inverted Index),反向索引,正向索引是通过key找value,反向索引则是通过value找key(关键词到文件ID的映射)。 倒排索引是实现“单词-文档矩阵”的一种具体存储形式,通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。倒排索引主要由两个部分组成:“单词词典”和“倒排文件”。 单词词典(Lexicon):搜索引擎的通常索引单位是单词,单词词典是由文档集合中出现过的所有单词构成的字符串集合,单词词典内每条索引项记载单词本身的一些信息以及指向“倒排列表”的指针。 倒排列表(PostingList):倒排列表记载了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。

为什么 Elasticsearch/Lucene 检索可以比 MySQL 快呢? MySQL 只有 Term Dictionary 这一层,是以 B+Tree 排序的方式存储在磁盘上的。 检索一个 Term 需要若干次的 Random Access 的磁盘操作。 Lucene 在 Term Dictionary 的基础上添加了 Term Index 来加速检索,Term Index 以树的形式缓存在内存中。 从 Term Index 查到对应的 Term Dictionary 的 Block 位置之后,再去磁盘上找 Term,大大减少了磁盘的 Random Access 次数。

==================================================

Nginx负载均衡策略 轮询 默认方式 weight 权重方式 ip_hash 依据ip分配方式 least_conn 最少连接方式 fair(第三方) 响应时间方式 url_hash(第三方) 依据URL分配方式

1、轮询 最基本的配置方法,负载均衡默认策略。每个请求会按时间顺序逐一分配到不同的后端服务器。

  • 在轮询中,如果服务器down掉了,会自动剔除该服务器。
  • 此策略适合服务器配置相当,无状态且短平快的服务使用。

2、weight 权重方式,在轮询策略的基础上指定轮询的几率 weight参数用于指定轮询几率,weight的默认值为1,weight的数值与访问比率成正比

  • 权重越高分配到需要处理的请求越多。
  • 此策略可以与least_conn和ip_hash结合使用。
  • 此策略比较适合服务器的硬件配置差别比较大的情况。

3、ip_hash 指定负载均衡器按照基于客户端IP的分配方式,确保了相同的客户端的请求一直发送到相同的服务器,以保证session会话。 这样每个访客都固定访问一个后端服务器,可以解决session不能跨服务器的问题。

  • ip_hash不能与backup同时使用。
  • 此策略适合有状态服务,比如session。
  • 当有服务器需要剔除,必须手动down掉。

4、least_conn 把请求转发给连接数较少的后端服务器

  • 适合请求处理时间长短不一造成服务器过载的情况。

5、fair (第三方) 按照服务器端的响应时间来分配请求,响应时间短的优先分配。

6、url_hash (第三方) 按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用。 使用url_hash,可以使得同一个url(也就是同一个资源请求)会到达同一台服务器,一旦缓存住了资源,再次收到请求,就可以从缓存中读取。 

例子: # 动态服务器组

upstream dynamic_server {
    ip_hash;                # 3.ip_hash
    least_conn;             # 4.least_conn
    fair;                   # 5.fair
    hash $request_uri;      # 6.url_hash

    server localhost:8080 weight=2;
    server localhost:8081;
    server localhost:8082 backup;
    server localhost:8083 max_fails=3 fail_timeout=20s;
}

参数: fail_timeout 与max_fails结合使用 max_fails 设置在fail_timeout参数设置的时间内最大失败次数 fail_time 服务器会被认为停机的时间长度,默认10s backup 标记该服务器为备用服务器 down 标记服务器永久停机

声明:本文为本人在准备面试过程中,将学习知识点整理的汇总笔记,内容来源为各大博客网站相关技术文章,仅做技术分享用途。如有部分篇幅侵权,请联系告知,本人会立即删除。