2020年PHP中级面试知识点及答案

一、前言

      最近博主也是历尽千辛万苦换了份工作,每次换之前不找点面试题看似乎就没自信一样。。奈何网上有些面试题是比较老套的,所以这里重新总结一份2020年的,题目是有些是博主自己不熟悉的点,有些是boss直聘论坛找到的,有的是朋友的面试经历,仅作为记录。

      以下面试题部分带有博主自己学习时候写下的答案,也许并不全或者并不正确,大家只看题就好,希望能对大家有所帮助。

二、零散的知识点

1、laravel相关面试题

(1)什么是服务提供者
(2)什么是容器,什么是依赖注入,控制反转 	(ok)
(3)laravel的流程
(4)laravel的路由机制	(从index.php到router的web.php,然后找到对应的控制器和方法)

2、一个人余额有10块,买了一个8块的东西,同时点击100次,那这个订单会不会重复,并说出解决方案

(1)隔离级别串行化?
(2)RR隔离级别+间隙锁next-key
(3)redis的hash保证唯一性,随后再同步db

3、zookeeper是什么

配置管理,名字服务,提供分布式同步以及集群管理。目前HBase使用它来维护集群的配置信息,Kafka使用Zookeeper来维护broker的信息
zookeeper使用zab协议实现强一致性,1、选举出leader;2、同步节点之间的状态达到数据一致;3、数据的广播

4、rpc是什么?

答:RPC就是要像调用本地的函数一样去调远程函数
主要作用:
	(1)解决分布式系统中,服务之间的调用问题
	(2)远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑。
原理步骤:
	(1)call_id映射:客户端和服务端维护一个类似于的表,对应方法id和方法名。当远程调用的时候,需要的传递id过去,
		这样服务端才能知道你要调用哪个方法。一般是一个哈希表
	(2)序列化和反序列化。由于传输都是二进制的,所以传输前要序列化,收到数据要反序列化
	(3)网络传输,一般使用tcp协议,也可以是udp
场景:

5、分布式面临的首要问题就是nginx的负载均衡

(1)什么是负载均衡
(2)负载均衡实践
	1、轮询
	2、权重
	3、绑定ip.ip_hash算法。 可以解决session不同步问题,但是均衡性差。比如高校等局域网ip一致,不能有效区分
(3)四层,七层负载均衡?
	四层:IP+端口的负载均衡
	七层:基于URL等应用层信息,可以分析应用层的信息,如HTTP协议URI或Cookie信息

6、nginx面试题

Nginx 常用命令有哪些?
需要熟悉:nginx -t ,nginx -s stop 之类
Nginx 返回 502 错误的可能原因?
	(1)进程数不够,需要更改配置
	(2)php-fpm自动重启问题
	(3)php-fpm请求超时
	(4)是否有大量数据库句柄没释放,导致进程卡住
Nginx的504错误一般是fastcgi的超时配置方面有问题
正向代理和反向代理之间的区别是什么?
正向代理:代理端代理的是客户端反向代理:代理端代理的是服务端
什么是负载均衡?
	代理服务器将接收的请求均衡的分发到各服务器
(1)session同步问题
	1、使用cookies (户端把cookie禁掉了的话,那么session就无从同步)
	2、存储到数据库	(增加数据库的负担。而且数据库读写速度较慢,不利于session的适时同步)
	3、存到memcache或者redis缓存(常用)
(2)一般是lvs做4层负载;nginx做7层负载(也能做4层负载, 通过stream模块)
	七层负载均衡基本都是基于http协议的,适用于web服务器的负载均衡。(nginx)
	 四层负载均衡主要是基于tcp协议报文,可以做任何基于tcp/ip协议的软件的负载均衡。(haproxy、LVS)

7、swoole必须要学习下

(1)
https://blog.csdn.net/DarkAngel1228/article/details/82053360	(swoole的一些基础概念)
新建laravel项目来使用swoole做一些简单的demo
(2)easyswoole的文档
	http://noobcourse.php20.cn/NoobCourse/Introduction.html#%E6%96%B0%E6%89%8B%E5%85%A5%E9%97%A8
(3)swoole的文档
	http://wiki.swoole.com/wiki/page/487.html
(4)共享变量
(5)协成
(6)go的channel

8、linux查看性能调试等命令

 top/iostat/vmstat/free/strace/tcpdump 等监控工具
top和iostat是查看cpu和硬盘的使用情况
strace是可以调试程序的,显示系统调用的步骤
free是查看内存的使用情况的

9、epoll是干什么的 (此处仅作为了解)

(1)select,poll,epoll都是IO多路复用的机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
(2)select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
(3)epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))
(4)表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。
(5)epoll是线程安全的
(6)nginx和epoll的关系?  
https://www.zhihu.com/question/63193746
https://segmentfault.com/q/1010000010427586

10、部分面试题链接

https://blog.csdn.net/arvesri70299/article/details/101695117
https://blog.csdn.net/dongdonggegelovezcj/article/details/101347644
https://blog.csdn.net/lcli2009/article/details/82825890		(可以看看他的卡夫卡)
https://blog.csdn.net/lxw1844912514/article/details/100028857
https://blog.csdn.net/yilukuangpao/article/details/90234348	算法题
https://zhuanlan.zhihu.com/p/147569045?utm_source=wechat_session	面试题

三、redis和mysql相关

作为一名后端人员,mysql,Redis 永远是绕不开的。。

1、redis的五大数据类型的使用场景

https://blog.csdn.net/fenghuoliuxing990124/article/details/84983694
(1)string	存储字符串,场景是简单缓存
(2)list		队列,场景是模拟队列,秒杀,点赞,回复等有一些先后顺序的
(3)set		无序的唯一列表,	场景:抽奖,去重,好友圈,共同好友之类的
(4)SortedSet	有序的		场景:排行榜,各种热度排行
(5)hash	参考:https://www.cnblogs.com/pangzizhe/p/10657801.html	购物车

2、redis是单线程的吗,可以多线程吗? (可以多线程,redis6.0可以了)

优点:
(1) 绝大部分请求是纯粹的内存操作(非常快速)
(2) 采用单线程,避免了不必要的上下文切换和竞争条件
(3) 非阻塞IO - IO多路复用(select,poll,epoll)
(4)高效的数据结构
(5)value大小:redis最大可以达到1GB,而memcache只有1MB

3、mysql的乐观锁和悲观锁

(1)乐观锁是不加锁的方式,通过添加版本号实现。比如A事务要修改数据,此时版本号为1。B事务也要修改 ,此时读取版本号也是1.
	等A事务修改的时候,此时读取version,当version=1的时候才更新version=2。 B事务要更新的时候,再次读取version发现version=2了,
	和初始读取的version=1对不上,因此就会更新失败。
可以理解为:比如当前版本是1,A和B获取到version=1,此时更新,那A和B的更新条件都为“version = 1”,如果A先提交了,
	此时表中该条数据version已经被A更新为2,B再提交,发现不满足“version=1”,所以无法更新,排他异常
(2)乐观锁只能防止脏读后数据的提交,不能解决脏读。
(3)悲观锁包括:共享锁,排它锁。共享锁是其它事务可以读但是不能写。排他锁是只有自己得事务有权限对此数据进行读写
(4)事务A加上排它锁,事务B在不加排它锁的情况下,是可以select数据的。
(5)乐观锁适合读比较多,写比较少的场景。 悲观锁适合写比较多的场景
(6)乐观锁的时候,当事务B更新不成功的时候,会继续重试。如果重试的多了,会造成大量资源消耗,然而不如使用悲观锁了

4、测试redis秒杀

(1)通过redis的list类型,先创建一个列表,插入10条库存。用户抢购的时候,就从这10个库存里面取,取完为止。这里因为pop操作是原子性的,可以防止超卖。
(2)如何保证每个用户都只能抢到一个呢?
	采用hash的算法。首先库存还是存在列表里面。
	hash部分,设置一个key,key中对应的属性名和属性值都是user_id。比如 test_key:1:1
	在实际抢购的时候,通过hset($test_key, $user_id, $user_id) 判断返回,当user_id不存在hash表的时候,则hash表会自动创建并返回1。当存在这个user_id的时候,会返回0
		当返回1的时候,代表是新用户,此时可以减库存,通知存入用户到hash中。
		当返回0的时候,代表用户重复,此时提示已经抢购过了

5、redis的分布式锁setnx

(1)先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放
(2)可以通过set命令,直接设置nx并设置过期时间,防止出现当拿到锁之后,redis挂掉导致来不及设置过期时间的问题,锁一直释放不了
(3)keys读取所有的键,会导致进程堵塞。可以用scan无阻塞的提取出指定模式的key列表,scan获取的数据可能会重复,需要手动去重
(4)RDB持久化也分两种:SAVE(阻塞)和BGSAVE(非阻塞,一般用这个)。  AOF的话,一般是1s同步一次,如果每条记录都同步的话,会非常损耗性能
(5)redis同步机制:(1)master使用bgsave生成rbd快照,同时后续的修改等操作都会记录到内存。快照生成之后,同步给从节点
		(2)slave同步完快照,通知master,把后续的修改记录都同步到从节点即可

6、用redis使用场景?

hash实现购物车:
(1)每个用户的购物车作为一个hash表,user_id作为key,商品id作为field,商品数量作为value.
(2)hset添加商品,hincrby增加数量,hlen为商品总量。hdel删除商品,hgetall获取所有商品	
list实现队列,和栈:
(1)栈:LPUSH + LPOP	(左侧进,左侧出。即先进后出)(栈是先进后出,类似于箱子里放东西)
(2)先进先出队列:LPUSH + RPOP	(左侧进,右侧出)
(3)先进先出,阻塞队列:LPUSH+BRPOP 	:brpop意思 block right pop 阻塞式右侧出队
	brpop(['queue1', 'queue2'], 0),当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素
(4)先进先出,等待阻塞队列: Lpush + Brpoplpush  :
	Brpoplpush 命令从列表中取出最后一个元素,并插入到另外一个列表的头部; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。	
		相当于减少无用的轮询,而且每次消费队列时候,还进行了备份,比较安全
(5)优先级队列设计:
	1、普通的队列,碰到优先级高的,就从右侧插入,这样会优先消费。(缺点:连续多个优先级任务的话,会先进后出,无法保证顺序)
	2、使用brpop来阻塞的读:brpop(['queue1', 'queue2'], 0),先读优先级高的队列,再读优先级低的队列
	3、很多优先级的话,只设置一个队列,并保证它是按照优先级排序号的。然后通过二分查找法查找一个任务合适的位置,并通过 lset 命令插入到相应的位置。
list使用场景:
(1)微信公众号,后台为每个订阅用户Lpush一条消息,id为key,文件id集合为value,查看的时候,只需要Lrange指定的消息即可

四、BOSS论坛上遇到的面试题

1、滴滴的面试题

(1)mysql保存在磁盘的数据格式是什么 ,然后又是如何变异成我们能识别的数据格式?
	二进制的吧
(2)mysql索引在内存中以什么格式保存?
(3)B+树是怎样的树状,为什么会这样

2、一个10年经验大哥遇到的题

(1)什么是缓存穿透,什么是缓存击穿,如何解决
		缓存穿透是:不论redis还是数据库,都没有这个数据
			(1)布隆过滤器	(4.0之后布隆过滤器作为一个插件加载到Redis Server中,就会给Redis提供了强大的布隆去重功能。)
				主要是add和exists命令,就是判断某个key在不在这个集合中
			(2)判断不存在,就吧空结果写入到缓存。设置比较短的过期时间即可
		缓存击穿:redis没有,mysql有
			(1)利用锁,先获取这个key的锁,然后同步db数据到缓存。没获取到锁的时候,就先等待
	(2)epoll和select的区别是什么	(ok)
	(3)单机redis与集群redis
		1、集群redis解决了单机redis宕机问题
		2、解决了单机性能不足,内存不足的情况
		3、同时借用主从,也解决了读写之间的分离问题
		4、集群比较难以维护
	(4)为什么memchace只支持kv,而redis支持类型这么多
		1、这是由于redis高效的数据结构。len:用于记录buf中已使用空间的长度,free:buf中空闲空间的长度,buf[]:存储实际内容
		2、由数据类型来记录数据是什么方式存储的
	(5)redis的过期策略是什么
		整体数据的LRU,random等
		有过期时间的LRU,randow等
	(6)如何快速定位php程序运行慢的地方
		(1)打开php-fpm慢日志:slow_log
		(2)使用xdebug来跟踪程序
		(3)phptrace跟踪 	(类似于linux下的trace命令,只不过一个是追踪系统调用,一个是追踪程序调用。也是命令行调用,需要输入php-fpm的pid)

3、其他的面试题

(1)谈谈反射的优缺点
	应用场景:插件,框架开发等
	优点:可以通过反射类,获取被反射类的属性,方法等
(2)如何优化in_array的性能
	1、array_flip: key,value,反转之后,使用isset()
	2、implode连接成字符串,直接用strpos判断(php里面字符串取位置速度非常快,尤其是在大数据量的情况下)
(3)如何处理脏读
	1、隔离级别设置成RC及以上
(4)大文件读取和存储
	1、yield生成器,一次读取一行,返回的是生成器对象,可以防止内存溢出
	2、php自带的SplFileObject类去读取,可以指定行数,指定位置读取开始读取
	3、复制大文件的话还是用数据流,stream_copy_to_stream
(5)b树和b+树的异同,B+树的叶子节点是双向链表吗
	1、是的叶子也是根据页中用户记录的主键大小顺序排成一个双向链表
(6)redis哪些操作和方法是原子性的
	1、有种说法是,redis的单个命令都是原子性的
	2、还有人说,pop,push是原子性的,而len这种不是原子性的,所以判断库存会用pop去减去库存,而不是用len判断
(7)mysql分区表和数据统计问题
	分区和分表不一样,分区是把一个表,通过Range、List、Hash、Key,其中Range比较常用 等方法,分成不同的磁盘文件存储。
(8)yield
	1、返回生成器对象,可以使用foreach进行迭代。比如读取文件的时候,返回的是一行一行的数据,
		就避免了之前容易出现的数组内存溢出情况
	2、占用内存极小,近似为一行数据的内存大小
(9)二分查找
	
(10)解决卡夫卡的rebalance问题,还有事务的使用方法
	1、Rebalance本身是Kafka集群的一个保护设定,用于剔除掉无法消费或者过慢的消费者
	2、当消费数据过慢,或者比较耗时,都会触发这个重平衡
	3、坏处
		(1)数据重复消费: 消费过的数据由于提交offset任务也会失败,在partition被分配给其他消费者的时候,会造成重复消费,数据重复且增加集群压力
		(2)影响集群速度
		(3)数据不能及时消费,会累积lag,在Kafka的TTL之后会丢弃数据
		(4)频繁的Rebalance反而降低了消息的消费速度,大部分时间都在重复消费和Rebalance
	
(11)redis的分布式锁不适合高并发场景,如何优化
	(1)比如悲观锁,分布式锁,乐观锁,队列串行化,异步队列分散,Redis原子操作,等等,很多方案,我们对库存超卖有自己的一整套优化机制
	(2)问题:分布式锁一旦加了之后,对同一个商品的下单请求,会导致所有客户端都必须对同一个商品的库存锁key进行加锁。不适合高并发,因为这个类似于串行化
	(3)解决方案:
		1、分段加锁。就是1000个库存,你分成20个key的库,用户请求随机分配到这20个库,这样分开加锁提升性能。(库存不足则记得手动释放锁,并重新选择其他库)
(12)说一下悲观锁和互斥锁的具体区别
	1、互斥锁、自旋锁、读写锁都属于悲观锁,悲观锁认为并发访问共享资源时,冲突概率可能非常高,所以在访问共享资源前,都需要先加锁。
	2、互斥锁和自旋锁都是最基本的锁
	3、互斥锁加锁失败后,线程会释放 CPU ,给其他线程;自旋锁加锁失败后,线程会忙等待,直到它拿到锁;
(13)你pop了redis里的数据,最后进程挂了怎么办
	1、redis挂了的话,数据从内存溢出,但是没有持久化到磁盘,这时候就要看持久化的策略了,是aof还是rdb,是一秒一写还是每次命令都写,然后恢复数据
	2、综合使用AOF和RDB两种持久化机制,用AOF来保证数据不丢失,作为数据恢复的第一选择; 用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB来进行快速的数据恢复
	3、如果是php进程被kill了。可以通过信号机制,重新push
(14)一句话描述binlog,undo log等
	1、redo log是为了持久化数据,在数据还没从内存刷新到磁盘时,如果发生故障,可读取该日志持久化到磁盘。
	2、binlog 是为了复制和恢复数据的,即Mysql从服务器可以读取主服务器的binlog复制数据,数据库数据丢失,也可以读取binlog恢复
	3、undo log是为了保证原子性的。(为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为Undo Log)。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。)

五、部分公司面试题

1、甲公司

(1)laravel相关面试题
		服务提供者是什么?	:服务容器就是管理类的依赖和执行依赖注入的工具,它可以为你的类库提供一套可以重用的实例化方案。
		IoC 容器是什么?
	(2)vue基础面试题

	(3)谈谈你对闭包的理解
		1、通过匿名函数实现,一般是普通函数中调用匿名函数,返回数据。匿名函数也可以作为参数传递给普通函数
		2、闭包要使用外界的变量,则需要使用use关键字
	(4)什么是CSRF攻击?XSS攻击?如何防范
		CSRF:跨站请求伪造 。  一般是token验证的方案
		XSS:跨域脚本攻击。	一般是对输入进行encode转义和过滤
		sql注入:		(1)pdo的预处理	(2)对用户参数进行过滤转义处理
		ddos:		(1 防火墙	(2)禁用过滤ip	(3 使用CDN,提供一层缓冲,不会全部涌向服务器

2、乙公司

(1)设计模式
		单例,工厂,观察者模式
		https://www.cnblogs.com/yueguanguanyun/p/9584501.html

	(2)php代码优化
		函数
		数组
		释放内存
		定义方法,注意循环

	(3)可以于yy的上线下线功能,长连接方面
		长连接还是通过websocket最好,主要是swoole部分

3、丙公司

(1)redis集群相关
	(2)mysql回表		(ok)
	(3)php-fpm有没有挂过,怎么处理的
		1、503错误,一般是进程太多导致的。比如max_children的数量等
		2、502错误,php或者php-fpm超时
		3、504是nginx错误
		4、至于什么错误,可以查看php-fpm的日志文件
		5、通过配置max_request等配置,可以自动重启php-fpm
	(4)慢查询有没有通过改框架去优化
		1、一般使用原生的sql多一些
		2、下载laravel-debuger文件,看看慢在哪里了。也可以使用x-debug查看
	(5)redis单机挂了怎么办,有什么策略:(单机的问题就是内存不够,处理能力有限,不能高可用)
		(1)查询前先ping一下,无响应就先去数据库
		(2)定时脚本轮询,ping redis,错误了就报警出来
		(3)给key设置过期时间,减小内存压力,释放部分内存出来
		(4)挂了就重启,通过持久化恢复数据。业务不忙的话可以先预热,业务比较忙的话,就直接恢复redis
	(6)es怎么用的
		(1)存储日志用的
		(2)从传统的关系型表设计,改为文档json设计
		(3)连表没那么方便了,可以进行聚合查询等
	(7)redis问的比较多
	(8)php的安全策略
		1、文件系统安全(尽量不要用root权限,php权限也不能太高)
		2、数据库安全(防止sql注入等)
		3、用户数据安全(对用户数据进行过滤,能防止xss和csrf)
		4、线上环境安全(php配置关闭错误提示,关闭危险函数等)
	(9)redis过期策略
		定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
		惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
		定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

	(10)内存淘汰策略:
		noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
		allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
		allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
		volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
		volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
		volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
	(11)一台服务器可以安装多个redis,主要通过修改端口号,修改配置文件路径等实现,互不干扰即可

2020最后几天了,奥利给,冲冲冲!

posted @ 2020-12-29 19:19  码农编程进阶笔记  阅读(459)  评论(0编辑  收藏  举报
返回顶部 有事您Q我