保持学习, 保持警惕, 居安思危, 走出舒适区.
来自鸡哥的面试 (百度)
FAST-CGI工作原理 ?
- 首先理解什么是CGI
CGI(Common Gateway Interface: 通用网关接口)
是Web 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能。
- CGI协议的缺陷
每次处理用户请求,都需要重新 fork CGI 子进程、销毁 CGI 子进程。
一系列的 I/O 开销降低了网络的吞吐量,造成了资源的浪费,在大并发时会产生严重的性能问题。
- 什么是Fast-CGI
常驻型CGI协议;协议采用 进程间通信(IPC) 来处理用户的请求
FastCGI协议运行原理
- FastCGI 进程管理器启动时会创建一个 主(Master) 进程和多个 CGI 解释器进程(Worker 进程),然后等待 Web 服务器的连接。
- Web 服务器接收 HTTP 请求后,将 CGI 报文通过 套接字(UNIX 或 TCP Socket)进行通信,将环境变量和请求数据写入标准输入,转发到 CGI 解释器进程。
- CGI 解释器进程完成处理后将标准输出和错误信息从同一连接返回给 Web 服务器。
- CGI 解释器进程等待下一个 HTTP 请求的到来。
PHP-FPM ?
php-fpm (PHP-FastCGI Process Manager: FastCGI 进程管理器)
php-fpm是FastCGI的实现,并提供了进程管理的功能。
php-fpm进程管理器有两种进程组成,一个 Master 进程和多个 Worker 进程。Master 进程负责监听端口,接收来自 Web 服务器的请求,然后指派具体的 Worker 进程处理请求;worker 进程则一般有多个 (依据配置决定进程数),每个进程内部都嵌入了一个 PHP 解释器,用来执行 PHP 代码。
Nginx 服务器如何与 FastCGI 协同工作
Nginx 服务器无法直接与 FastCGI 服务器进行通信,需要启用 ngx_http_fastcgi_module 模块进行代理配置,才能将请求发送给 FastCGI 服务。
tcp怎么保障可靠性 ?
TCP 是通过下面几个特性保证数据传输的可靠性:
- 序列号和确认应答信号
- 超时重发控制
- 连接管理
- 滑动窗口控制
- 流量控制
- 拥塞控制
TCP协议可靠性是如何保证之滑动窗口,超时重发,序列号确认应答信号
HTTP状态码502、504的区别 ?
502 Bad Gateway
此错误响应表明服务器作为网关需要得到一个处理这个请求的响应,但是得到一个错误的响应。
nginx无法与php-fpm进行连接。
nginx在连接php-fpm一段时间后发现与php-fpm的连接被断开。
503 Service Unavailable
服务器没有准备好处理请求。 常见原因是服务器因维护或重载而停机。 请注意,与此响应一起,应发送解释问题的用户友好页面。
504 Gateway Timeout
当服务器作为网关,不能及时得到响应时返回此错误代码
504即nginx超过了自己设置的超时时间,不等待php-fpm的返回结果,直接给客户端返回504错误。但是此时php-fpm依然还在处理请求(在没有超出自己的超时时间的情况下)。
http响应头connection的作用 ?
当client和server通信时对于长链接如何进行处理
在http1.1中,client和server都是默认对方支持长链接的, 如果client使用http1.1协议,但又不希望使用长链接,则需要在header中指明connection的值为close;如果server方也不想支持长链接,则在response中也需要明确说明connection的值为close.
HTTP Connection的close设置允许客户端或服务器中任何一方关闭底层的连接双方都会要求在处理请求后关闭它们的TCP连接。
top里面的参数 ?
1 | top - 15:29:38 up 21 min, 1 user, load average: 0.34, 0.36, 0.42 |
- 第一行是任务队列信息
- 第二行是进程信息
- 第三行是CPU信息
- 第四,五行是内存信息
lsof ps netstat 的区别 ?
lsof
:(列出打开文件lists openfiles)能看到pid和用户(有权限控制,只能看到本用户),可以找到哪个进程占用了这个端口ps
:(process)查看有终端控制的所有进程netstat
: 用于显示各种网络相关信息,如网络连接,路由表,接口状态
php脚本解析流程 ?
抛开各个SAPI实现上的差异,PHP程序的执行流程可以简单归结如下:
- 程序启动, Zend引擎和核心组件初始化
- 扩展初始化(MINT)
- 收到请求,扩展激活(RINIT)
- 解析,执行PHP脚本
- 请求结束, 扩展停用 (RSHUTDOWN)
- 卸载扩展 (MSHUTDOWN)
- 程序关闭
php的引用是怎么实现的 ?
在PHP中引用意味着用不同的名字访问同一个变量的内容;
__destruct() 触发机制 ?
当销毁一个对象时才会调用对象的析构函数,一般情况下,销毁一个对象的触发时机为
- 主动调用
unset($obj)
- 主动调用
$obj = null
- 程序自动结束
应用进程和内核的关系 ?
- 应用进程即用户态进程,运行于操作系统之上,通过系统调用与操作系统进行交互
- 内核即操作系统内核,用于控制计算机硬件。
普通索引的查询流程 ?
查询过程
- 普通索引: 查找到满足条件的第一个记录后,需要查找下一个记录, 直到碰到第一个不满足条件的记录。
- 唯一索引: 由于索引定义了唯一性,查找到第一个满足条件的记录后,就会停止继续检索
更新过程
- 普通索引: 唯一索引的更新就不能使用 change buffer,只有普通索引可以使用
- 唯一索引: 对于唯一索引来说,所有的更新操作都要先判断这个操作是否违反唯一性约束。
插入过程
- 普通索引: 对于普通索引来说,则是将更新记录在 change buffer,语句执行就结束了。
- 唯一索引: 对于唯一索引来说,需要将数据页读入内存,判断到没有冲突,插入这个值,语句执行结束
change buffer
写缓存(Change Buffer) 是一种特殊的数据结构,用于在对数据变更时,如果数据所在的数据页没有在 buffer pool 中的话,在不影响数据一致性的前提下,InnoDB 引擎会将对数据的操作缓存在 Change Buffer 中,这样就省去了从磁盘中读入这个数据页。
Docker网段冲突 ?
docker默认网段是172.17,和公司的网段冲突,因此解决的办法其实也挺简单的,要么换公司网段,要么换docker网段,是个正常人都会选择换docker网段
方案一:不改docker网段,创建不和公司网段冲突的docker子网段
1 | docker network create --driver=bridge --subnet=192.161.0.0/16 monitor_net |
docker network create –driver=bridge –subnet=192.161.0.0/16 monitor_net
1 | docker run -it --name <容器名> ---network monitor_net <镜像名 |
在docker-compose同样通过networks指定,形如下
1 | version: '3' |
方案二:修改docker默认网段
- 停止docker
1 | systemctl stop docker |
- 删除原有网桥
1 | ip link del docker0 down |
- vim /etc/docker/daemon.json,添加形如下内容
1 | "bip":"192.161.20.1/24" |
- 重启docker服务
1 | systemctl restart docker |
Docker for mac
如果本地环境是 Docker for mac,依次打开 Preferences -> Daemon -> Advanced ,增加配置 "bip" : "172.16.10.1/24"
,从而避免网段冲突的问题。
docker-machine
编辑文件/etc/docker/daemon.json
,增加 {"bip" : "172.16.10.1/24"}
,然后退出虚拟机,重启docker-machine即可成功修改网段。
来自鸡哥的面试 (SHEIN)
进程, 线程, 协程的区别 ?
进程是什么?
进程就是应用程序的启动实例。
例如:打开一个软件,就是开启了一个进程。
进程拥有代码和打开的文件资源,数据资源,独立的内存空间。
线程是什么?
线程属于进程,是程序的执行者。
一个进程至少包含一个主线程,也可以有更多的子线程。
线程有两种调度策略,一是:分时调度,二是:抢占式调度。
协程是什么?
协程是轻量级线程,协程的创建、切换、挂起、销毁全部为内存操作,消耗是非常低的。
协程是属于线程,协程是在线程里执行的。
协程的调度是用户手动切换的,所以又叫用户空间线程。
协程的调度策略是:协作式调度。
什么是缓存雪崩,缓存穿透,缓存击穿 ?
缓存雪崩
缓存在某一个时刻出现大规模的key失效
那么就会导致大量的请求打在了数据库上面,导致数据库压力巨大,如果在高并发的情况下,可能瞬间就会导致数据库宕机。
- 解决方案
(1): 事前
- 设置不同的过期时间,让缓存失效的时间尽量均匀,避免相同的过期时间导致缓存雪崩,造成大量数据库的访问。
- 分级缓存:第一级缓存失效的基础上,访问二级缓存,每一级缓存的失效时间都不同。
- 热点数据缓存永远不过期。
- 保证Redis缓存的高可用,防止Redis宕机导致缓存雪崩的问题。可以使用 主从+ 哨兵,Redis集群来避免 Redis 全盘崩溃的情况。
(2): 事中
- 互斥锁:在缓存失效后,通过互斥锁或者队列来控制读数据写缓存的线程数量,比如某个key只允许一个线程查询数据和写缓存,其他线程等待。这种方式会阻塞其他的线程,此时系统的吞吐量会下降
- 使用熔断机制,限流降级。当流量达到一定的阈值,直接返回“系统拥挤”之类的提示,防止过多的请求打在数据库上将数据库击垮,至少能保证一部分用户是可以正常使用,其他用户多刷新几次也能得到结果。
(3): 事后
- 开启Redis持久化机制,尽快恢复缓存数据,一旦重启,就能从磁盘上自动加载数据恢复内存中的数据。
缓存击穿
缓存击穿跟缓存雪崩有点类似,缓存雪崩是大规模的key失效,而缓存击穿是某个热点的key失效,大并发集中对其进行请求,就会造成大量请求读缓存没读到数据,从而导致高并发访问数据库,引起数据库压力剧增。这种现象就叫做缓存击穿。
- 解决方案
- 在缓存失效后,通过互斥锁或者队列来控制读数据写缓存的线程数量,比如某个key只允许一个线程查询数据和写缓存,其他线程等待。这种方式会阻塞其他的线程,此时系统的吞吐量会下降
- 热点数据缓存永远不过期。
缓存穿透
缓存穿透是指用户请求的数据在缓存中不存在即没有命中,同时在数据库中也不存在,导致用户每次请求该数据都要去数据库中查询一遍。如果有恶意攻击者不断请求系统中不存在的数据,会导致短时间大量请求落在数据库上,造成数据库压力过大,甚至导致数据库承受不住而宕机崩溃。
- 解决方案
- 将无效的key存放进Redis中
- 使用布隆过滤器
缓存预热
缓存预热是指系统上线后,提前将相关的缓存数据加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的缓存数据。
缓存降级
缓存降级是指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默认数据或访问服务的内存数据。降级一般是有损的操作,所以尽量减少降级对于业务的影响程度。
在项目实战中通常会将部分热点数据缓存到服务的内存中,这样一旦缓存出现异常,可以直接使用服务的内存数据,从而避免数据库遭受巨大压力。
B+tree是什么 ? 优缺点?
首先注意:B树就是B-树,”-“是个连字符号,不是减号。
B-树是一种平衡的多路查找(又称排序)树,在文件系统中有所应用。主要用作文件的索引。其中的B就表示平衡(Balance)
B+树有一个最大的好处,方便扫库,B树必须用中序遍历的方法按序扫库,而B+树直接从叶子结点挨个扫一遍就完了。
- 优点
- 单次请求涉及的磁盘IO次数少(出度d大,且非叶子节点不包含表数据,树的高度小)
- 查询效率稳定(任何关键字的查询必须走从根结点到叶子结点,查询路径长度相同)
- 遍历效率高(从符合条件的某个叶子节点开始遍历即可)
- 缺点
- B+树最大的性能问题在于会产生大量的随机IO,主要存在以下两种情况
- 主键不是有序递增的,导致每次插入数据产生大量的数据迁移和空间碎片;
- 即使主键是有序递增的,大量写请求的分布仍是随机的;
什么是死信队列 ?
死信队列:没有被及时消费的消息存放的队列
- 消息被拒绝(basic.reject/ basic.nack)并且不再重新投递 requeue=false
- TTL(time-to-live) 消息超时未消费
- 达到最大队列长度
消息变成死信后,会被重新投递(publish)到另一个交换机上(Exchange),这个交换机往往被称为DLX(dead-letter-exchange)“死信交换机”,然后交换机根据绑定规则转发到对应的队列上,监听该队列就可以被重新消费。
生产者–>发送消息–>交换机–>队列–>变成死信队列–>DLX交换机–>队列–>监听–>消费者
Redis的过期策略 ?
- 定时过期: 每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除
- 优点: 该策略可以立即清除过期的数据,对内存很友好
- 缺点 但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
- 惰性过期: 只有当访问一个key时,才会判断该key是否已过期,过期则清除。
- 优点: 该策略可以最大化地节省CPU资源,却对内存非常不友好。
- 缺点: 极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
- 定期过期: 每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。
- 优点: 通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用–处理”定时删除”的缺点 定期删除过期key–处理”惰性删除”的缺点。
- 缺点: 在内存友好方面,不如”定时删除”,因为是随机遍历一些key,因此存在部分key过期,但遍历key时,没有被遍历到,过期的key仍在内存中。在CPU时间友好方面,不如”惰性删除”,定期删除也会暂用CPU性能消耗。
Redis中同时使用了惰性过期和定期过期两种过期策略。
Redis的内存淘汰策略
noeviction
:当内存不足以容纳新写入数据时,新写入操作会报错。allkeys-lru
:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。allkeys-random
:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。volatile-lru
:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。volatile-random
:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。volatile-ttl
:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除
Laravel的队列实现原理 ?
redis的延迟队列一般是通过有序集合zset实现的
更早的一些面试题
Mysql负载均衡,主从复制问题
非关系型数据库的优缺点?
- 先说说关系型数据库优点
- 都是使用表结构,格式一致,易于维护。
- 使用通用的 SQL 语言操作,使用方便,可用于复杂查询。
- 数据存储在磁盘中,安全。
- 非关系型数据库优点
- 非关系型数据库存储数据的格式可以是 key-value 形式、文档形式、图片形式等。使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
- 速度快,效率高。 NoSQL 可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘。
- 海量数据的维护和处理非常轻松。
- 非关系型数据库具有扩展简单、高并发、高稳定性、成本低廉的优势。
- 可以实现数据的分布式处理。
- 非关系型数据库缺点
- 非关系型数据库暂时不提供 SQL 支持,学习和使用成本较高。
- 非关系数据库没有事务处理,没有保证数据的完整性和安全性。适合处理海量数据,但是不一定安全。
- 功能没有关系型数据库完善。