百度PHP工程师面试题和答案解析

#百度PHP工程师面试题和答案解析

一、单选题(共10题,每题5分

1.以下代码输出的结果是?

$a=[0,1,2,3]; $b=[1,2,3,4,5]; $a+=$b; echo json_encode($a);

A. [0,1,2,3]
B. [1,3,5,7,5]
C. [1,2,3,4,5]
D. [0,1,2,3,5]

参考答案:D

答案解析:

考的是数组+和array_merge的区别 当下标为数值时,array_merge()不会覆盖掉原来的值,但array+array合并数组则会把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉(不是覆盖). 当下标为字符时,array+array仍然把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉,但array_merge()此时会覆盖掉前面相同键名的值.


2.以下代码执行结果是?

$count = 5;function get_count(){
static $count = 0;
return $count++;}++$count;get_count();echo get_count();

A.0

B.1

C.2

D.3

参考答案:B

答案解析:

如果你回答 2 ,恭喜,你掉入陷阱了。 其实这道题主要考两点,第一点是static静态类型。这种的值永远都是静态的,第一次调用声明等于0,并且自增等于1。第二次调用,1再自增就等于2。但其实这里还有一道陷阱,那就是++a与a++的区别,前++是先自增,后++是先返回值再自增,所以结果等于 1。


3.以下PHP代码执行的结果是?

$a=[1,2,3]; foreach($a as &$v){

} foreach($a as $v){

} echo json_encode($a);

A、[1,2,3]

B、[1,2,2]

C、[1,1,2]

D、[1,3,2]

参考答案:B

答案解析:

在 PHP 中,foreach 结束后,循环中的索引值(index)及內容(value)並不会被重置。 所以最后的 $v还指向最后一个元素,再次循环,就会把最后个元素的值修改掉了。

解决的办法是,循环完毕之后,用unset($v); https://www.php.net/manual/en/control-structures.foreach.php


4.php执行过程的顺序正确的是?

A、扫描->解析->编译->执行->输出

B、扫描->解析->执行->编译->输出

C、扫描->编译->解析->执行->输出

D、扫描->执行->编译->解析->输出

参考答案:A

答案解析:

PHP简化执行过程: 1.扫描(scanning) ,将index.php内容变成一个个语言片段(token) 2.解析(parsing) , 将一个个语言片段变成有意义的表达式 3.编译(complication),将表达式编译成中间码(opcode) 4.执行(execution),将中间码一条一条的执行 5.输出(output buffer),将要输出的内容输出到缓冲区


5.php垃圾回收机制的说法错误的是?

A、在一个垃圾周期中,通过检查引用计数是否减1,并且检查哪些变量容器的引用次数是零,来发现哪部分是垃圾

B、可以通过调用gc_enable() 和 gc_disable()函数来打开和关闭垃圾回收机制

C、通过清理未被使用的变量来节省内存的占用

D、php代码执行完毕后会自动执行垃圾回收,所以不需要手动执行垃圾回收

参考答案:D

答案解析:

php一段代码有可能要长时间执行,但若此期间有未引用的变量的话,就会占用内存的空间,导致运行缓慢等问题


6.关于XSS防御,错误的是?

A、禁止字符

B、限制字符(reg_match)

C、过滤(preg_replace)

D、转义标签(htmlspecialchars)

参考答案:A

答案解析:

转义标签(htmlspecialchars) 限制字符(reg_match) 过滤(preg_replace) http only 模板引擎


7.有一个文件ip.txt,每行一条ip记录,共若干行,下面哪个命令可以实现“统计出现次数最多的前3个ip及其次数”?( )

A、uniq -c ip.txt

B、sort -nr ip.txt | uniq -c | sort -nr | head -n 3

C、cat ip.txt | count -n | sort -rn | head -n 3

D、cat ip.txt | count -n

参考答案:B

答案解析:

本题利用管道符"|"组合多个命令,uniq -c filename用于去除冗余并统计每一行出现的次数。 sort -r指逆序排序,-n指按数字字符串大小排序 head指定数量。 剩下的二选一交给运气。

正确的命令应该为: sort -nr ip.txt | uniq -c | sort -nr | head -n 3 第一次排序,把ip按顺序排列,因为第二个uniq只会合并相邻项 第二次排序,才是把ip按出现次序大小从大到小排列 最后取前三项结果。


8.Mysql索引使用的B-Tree描述错误的是?

A、每个非叶子结点由n-1个key和n个指针组成,其中d<=n<=2d;

B、每个叶子结点至少包含一个key和两个指针

C、所有叶结点都在同一层,深度等于树高h

D、一个结点中的key从左至右递减排列

参考答案:D

答案解析:

B-Tree是满足条件: d>=2,即B-Tree的度; h为B-Tree的高; 每个非叶子结点由n-1个key和n个指针组成,其中d


9.下列哪个是创建一个每周三01:00~04:00每3分钟执行执行一次的crontab指令?

A、* 1,4 * * 3 /bin/bash /home/sijiaomao/ok.sh

B、*/3 1,4 * * 3 /bin/bash /home/sijiaomao/ok.sh

C、*/3 1-4 * * 3 /bin/bash /home/sijiaomao/ok.sh

D、*/3 1-4 * * * /bin/bash /home/sijiaomao/ok.sh

参考答案:C

答案解析:

A:每周三的1时4时每分钟执行一次 B:每周三的1时4时每3分钟执行一次 C:满足要求 D:每天的1时4时每3分钟执行一次


10.正则的引擎表述错误的是?

A、正则引擎主要可以分为两大类:一种是DFA,一种是NFA。

B、一般而论,NFA引擎则搜索更快一些。但是DFA以表达式为主导,更容易操纵,因此一般程序员更偏爱DFA引擎!

C、NFA表达式主导,DFA文本主导.

D、可以使用是否支持忽略优先量词和分组捕获来判断引擎类型:支持 NFA,不支持 DFA

参考答案:B

答案解析:

正确的说法应该是:一般而论,DFA引擎则搜索更快一些。但是NFA以表达式为主导,更容易操纵,因此一般程序员更偏爱NFA引擎!


二、多选题(共10题,每题5分)

1.HTTP中GET与POST的区别有哪些?

A、GET在浏览器回退时是无害的,而POST会再次提交请求

B、GET请求只能进行url编码,而POST支持多种编码方式

C、GET请求会被浏览器主动cache,而POST不会,除非手动设置。

D、GET产生一个TCP数据包,POST产生两个TCP数据包。

参考答案:A,B,C,D

答案解析:

HTTP中GET与POST的区别如下,注意最后一条

·GET在浏览器回退时是无害的,而POST会再次提交请求。

·GET产生的URL地址可以被Bookmark,而POST不可以。

·GET请求会被浏览器主动cache,而POST不会,除非手动设置。

·GET请求只能进行url编码,而POST支持多种编码方式。

·GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。

·GET请求在URL中传送的参数是有长度限制的,而POST没有。

·对参数的数据类型,GET只接受ASCII字符,而POST没有限制。

·GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

·GET参数通过URL传递,POST放在Request body中。

·GET产生一个TCP数据包,POST产生两个TCP数据包。

关于最后一条,详情参考 https://blog.csdn.net/happy_xiahuixiax/article/details/72859762


2.MySQL有一个复合索引:INDEX(a, b, c),以下查询能用上索引的有?

A、select * from users where a = 1 and b = 2

B、select * from users where b = 2 and a = 1

C、select * from users where a = 2 and c = 1

D、select * from users where b = 2 and c = 1

参考答案:A,B,C

答案解析:

请牢记最左前缀原则

使用方式 能否用上索引

select * from users where a = 1 and b = 2 能用上a、b

select * from users where b = 2 and a = 1 能用上a、b(有MySQL查询优化器)

select * from users where a = 1 and b = 2 能用上a

select * from users where a = 1 and b = 2 不能

选项D 无法用上索引。


3.为什么大型网站要使用消息队列?

A、解耦

B、异步

C、削峰

D、大数据处理

参考答案:A,B,C

答案解析:

消息队列常见的使用场景有很多,但是比较核心的有 3 个:解耦、异步、削峰 大数据处理,有相应的处理办法,和消息队列关系不大。


3.为什么大型网站要使用消息队列?

A、解耦

B、异步

C、削峰

D、大数据处理

参考答案:A,B,C

答案解析:

消息队列常见的使用场景有很多,但是比较核心的有 3 个:解耦、异步、削峰 大数据处理,有相应的处理办法,和消息队列关系不大。


5关于缓存雪崩的事前事中事后的解决方案正确的有?

A、事前:进行系统压力测试,在负载均衡层做限流处理,过载丢弃请求或者进入队列

B、事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。

C、事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。

D、事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

参考答案:A,B,C,D

答案解析:

缓存雪崩的事前事中事后的解决方案如下。

·事前:进行系统压力测试,在负载均衡层做限流处理,过载丢弃请求或者进入队列

·事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。

·事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。

·事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

6.设计一个高并发系统,需要重点考虑的问题有?

A、系统拆分

B、缓存

C、MQ

D、分库分表

E、读写分离

F、ElasticSearch

参考答案:A,B,C,D,E,F

答案解析:

如何设计一个高并发系统?

系统拆分 将一个系统拆分为多个子系统,用 dubbo 来搞。然后每个系统连一个数据库,这样本来就一个库,现在多个数据库,不也可以扛高并发么。

缓存 缓存,必须得用缓存。大部分的高并发场景,都是读多写少,那你完全可以在数据库和缓存里都写一份,然后读的时候大量走缓存不就得了。毕竟人家 redis 轻轻松松单机几万的并发。所以你可以考虑考虑你的项目里,那些承载主要请求的读场景,怎么用缓存来抗高并发。

MQ MQ,必须得用 MQ。可能你还是会出现高并发写的场景,比如说一个业务操作里要频繁搞数据库几十次,增删改增删改,疯了。那高并发绝对搞挂你的系统,你要是用 redis 来承载写那肯定不行,人家是缓存,数据随时就被 LRU 了,数据格式还无比简单,没有事务支持。所以该用 mysql 还得用 mysql 啊。那你咋办?用 MQ 吧,大量的写请求灌入 MQ 里,排队慢慢玩儿,后边系统消费后慢慢写,控制在 mysql 承载范围之内。所以你得考虑考虑你的项目里,那些承载复杂写业务逻辑的场景里,如何用 MQ 来异步写,提升并发性。MQ 单机抗几万并发也是 ok 的,这个之前还特意说过。

分库分表 分库分表,可能到了最后数据库层面还是免不了抗高并发的要求,好吧,那么就将一个数据库拆分为多个库,多个库来扛更高的并发;然后将一个表拆分为多个表,每个表的数据量保持少一点,提高 sql 跑的性能。

读写分离 读写分离,这个就是说大部分时候数据库可能也是读多写少,没必要所有请求都集中在一个库上吧,可以搞个主从架构,主库写入,从库读取,搞一个读写分离。读流量太多的时候,还可以加更多的从库。

ElasticSearch Elasticsearch,简称 es。es 是分布式的,可以随便扩容,分布式天然就可以支撑高并发,因为动不动就可以扩容加机器来扛更高的并发。那么一些比较简单的查询、统计类的操作,可以考虑用 es 来承载,还有一些全文搜索类的操作,也可以考虑用 es 来承载。

上面的 6 点,基本就是高并发系统肯定要干的一些事儿,大家可以仔细结合之前讲过的知识考虑一下,到时候你可以系统的把这块阐述一下,然后每个部分要注意哪些问题,之前都讲过了,你都可以阐述阐述,表明你对这块是有点积累的。

说句实话,毕竟你真正厉害的一点,不是在于弄明白一些技术,或者大概知道一个高并发系统应该长什么样?其实实际上在真正的复杂的业务系统里,做高并发要远远比上面提到的点要复杂几十倍到上百倍。你需要考虑:哪些需要分库分表,哪些不需要分库分表,单库单表跟分库分表如何 join,哪些数据要放到缓存里去,放哪些数据才可以扛住高并发的请求,你需要完成对一个复杂业务系统的分析之后,然后逐步逐步的加入高并发的系统架构的改造,这个过程是无比复杂的,一旦做过一次,并且做好了,你在这个市场上就会非常的吃香。

其实大部分公司,真正看重的,不是说你掌握高并发相关的一些基本的架构知识,架构中的一些技术,RocketMQ、Kafka、Redis、Elasticsearch,高并发这一块,你了解了,也只能是次一等的人才。对一个有几十万行代码的复杂的分布式系统,一步一步架构、设计以及实践过高并发架构的人,这个经验是难能可贵的。


7.关于PHP-FPM子进程数量说法正确的有?

A、PHP-FPM 子进程数量不能太多,太多了增加进程管理的开销以及上下文切换的开销

B、dynamic 方式下,最合适的子进程数量为 在 N + 20% 和 M / m 之间 (N 是 CPU 内核数量,M 是 PHP 能利用的内存数量,m 是每个 PHP 进程平均使用的内存数量)

C、static方式:M / (m * 1.2) (M 是 PHP 能利用的内存数量,m 是每个 PHP 进程平均使用的内存数量)

D、pm.max_requests 可以随便设置 ,但是为了预防内存泄漏的风险,还是设置一个合理的数比较好

参考答案:A,B,C,D

答案解析:

首先,我们关注下 PHP-FPM 的运行方式:

·static :表示在 php-fpm 运行时直接 fork 出 pm.max_chindren 个子进程,

·dynamic:表示,运行时 fork 出 start_servers 个进程,随着负载的情况,动态的调整,最多不超过 max_children 个进程。

一般推荐用 static ,优点是不用动态的判断负载情况,提升性能;缺点是多占用些系统内存资源。

PHP-FPM 子进程数量,是不是越多越好? 当然不是,pm.max_chindren,进程多了,增加进程管理的开销以及上下文切换的开销。更核心的是,能并发执行的 php-fpm 进程不会超过 cpu 个数。如何设置,取决于你的代码。如果代码是 CPU 计算密集型的,pm.max_chindren 不能超过 CPU 的内核数。如果不是,那么将 pm.max_chindren 的值大于 CPU 的内核数,是非常明智的。国外技术大拿给出适用于 dynamic 方式的公式: 在 N + 20% 和 M / m 之间。

.N 是 CPU 内核数量。

.M 是 PHP 能利用的内存数量。

.m 是每个 PHP 进程平均使用的内存数量。

static方式的公式:M / (m 1.2)*

当然,还有一种保险的方式,来配置 max_children。 先把 max_childnren 设置成一个比较大的值。稳定运行一段时间后,观察 php-fpm 的 status 里的 max active processes 是多少,然后把 max_children 配置比它大一些就可以了。

pm.max_requests:指的是每个子进程在处理了多少个请求数量之后就重启。这个参数,理论上可以随便设置,但是为了预防内存泄漏的风险,还是设置一个合理的数比较好。


8.关于Kafka、ActiveMQ、RabbitMQ、RocketMQ说法正确的有?

A、ActiveMQ 基于 erlang 开发,并发能力很强,性能极好,延时很低

B、RocketMQ topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,在同等机器下,可以支撑大量的 topic

C、RabbitMQ时效性是微秒级,这是 RabbitMQ 的一大特点,延迟最低

D、Kafka 单机吞吐量 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景

参考答案:B,C,D

答案解析:

ActiveMQ 基于 Java 开发的, RabbitMQ 是基于 erlang 开发的。 所以选项A 错误。 B、C、D都正确。

特性 ActiveMQ RabbitMQ RocketMQ Kafka
单机吞吐量 万级,比 RocketMQ、Kafka 低一个数量级 同 ActiveMQ 10 万级,支撑高吞吐 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景
topic 数量对吞吐量的影响 topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源
时效性 ms 级 微秒级,这是 RabbitMQ 的一大特点,延迟最低 ms 级 延迟在 ms 级以内
可用性 高,基于主从架构实现高可用 同 ActiveMQ 非常高,分布式架构 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
消息可靠性 有较低的概率丢失数据 基本不丢 经过参数优化配置,可以做到 0 丢失 同 RocketMQ
功能支持 MQ 领域的功能极其完备 基于 erlang 开发,并发能力很强,性能极好,延时很低 MQ 功能较为完善,还是分布式的,扩展性好 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用

9.分库分表之后,id 主键如何处理?

A、单库生成自增 id

B、设置数据库 sequence 或者表自增字段步长

C、UUID

D、snowflake 算法

参考答案:A,B,C,D

答案解析:

snowflake 算法

A、B、C 也是理论可行的,但是都各自有缺点,最好用snowflake 算法。

snowflake 算法是 twitter 开源的分布式 id 生成算法,采用 Scala 语言实现,是把一个 64 位的 long 型的 id,1 个 bit 是不用的,用其中的 41 bit 作为毫秒数,用 10 bit 作为工作机器 id,12 bit 作为序列号。

1 bit:不用,为啥呢?因为二进制里第一个 bit 为如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0。 41 bit:表示的是时间戳,单位是毫秒。41 bit 可以表示的数字多达 2^41 - 1,也就是可以标识 2^41 - 1 个毫秒值,换算成年就是表示69年的时间。 10 bit:记录工作机器 id,代表的是这个服务最多可以部署在 2^10台机器上哪,也就是1024台机器。但是 10 bit 里 5 个 bit 代表机房 id,5 个 bit 代表机器 id。意思就是最多代表 2^5个机房(32个机房),每个机房里可以代表 2^5 个机器(32台机器)。 12 bit:这个是用来记录同一个毫秒内产生的不同 id,12 bit 可以代表的最大正整数是 2^12 - 1 = 4096,也就是说可以用这个 12 bit 代表的数字来区分同一个毫秒内的 4096 个不同的 id。


10.Redis 内存淘汰机制有哪些?

A、noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错

B、allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key

C、volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key

D、allkeys-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。

参考答案:A,B,C

答案解析:

redis 内存淘汰机制有以下几个:

noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了。 allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。 allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。 volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太合适)。 volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。 volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。