• 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最后几天了,奥利给,冲冲冲!

    赞赏码

    非学,无以致疑;非问,无以广识

  • 相关阅读:
    ssi服务器端指令
    json格式的转换为json字符串函数
    接口测试基础和jmeter
    【JZOJ6274】梦境
    【JZOJ6275】小L的数列
    【luoguP4721】分治 FFT
    【luoguP3868】猜数字
    中国剩余定理与扩展中国剩余定理
    【JZOJ6277】矩阵游戏
    【JZOJ6271】锻造 (forging)
  • 原文地址:https://www.cnblogs.com/lxwphp/p/15452648.html
Copyright © 2020-2023  润新知