PHP+前端面试题大合集
- PHP面试题
- 1、为什么SQL语句不要过多的join?⭐
- 2、PHP的垃圾回收机制?⭐
- 3、面向对象七大原则?⭐
- 4、PHP如何解决内存溢出?⭐
- 5、include和require的区别是什么?⭐
- 6、什么是临时表?⭐
- 7、大表数据查询如何优化?⭐
- 8、字段为什么要设置为not null?⭐
- 9、如何优化查询过程中的数据访问?⭐
- 10、如何优化where子句?⭐
- 11、Redis使用场景?⭐
- 12、浏览器访问网址的详细过程?⭐
- 13、如何解决并发问题?
- 14、简单秒杀系统并发考虑?
- 15、redis消息队列先进先出需要注意什么?
- 16、数据库索引的目的是什么?⭐
- 17、索引对数据库系统的负面影响是什么?⭐
- 18、post是两次请求的原因?⭐
- 19、PHP常用函数?⭐
- 20、索引有哪几种类型?⭐
- 21、mysql哪些字段适合建立索引?⭐
- 22、rabbitMQ消息队列的应用场景?⭐
- 23、Redis数据类型有哪些?⭐
- 24、Linux常用命令?⭐
- 25、laravel生命周期?⭐
- 26、找到执行很慢的SQL语句,如何分析它?⭐
- 27、InnoDB与MyISAM的区别?⭐
- 28、如何查看MySQL死锁?⭐
- 29、如何解决发生了的MySQL死锁?⭐
- 30、如何避免MySQL死锁?⭐
- 31、Redis缓存穿透?⭐
- 32、Redis缓存雪崩?⭐
- 33、rabbitMQ常用的的交换机?⭐
- 34、rabbitMQ如何保证消息的可靠性?⭐
- 前端面试题
- 1、移动端适配方案
- 2、常用的vue2生命周期
- 3、v-if和v-show
- 4、如何理解单向数据流
- 5、为什么v-if和v-for不建议同时使用
- 6、vue中css的scoped作用和原理
- 7、虚拟dom和diff算法
- 8、$nextTick
- 9、$route和 $router作用
- 10、项目优化
- 11、实现token无感刷新
- 12、实现大文件上传
- 13、axios取消请求
- 14、websocket
- 15、地址栏输入url过程
- 16、GET和POST请求的区别
- 17、JavaScript中的数据类型和存储上的区别
- 18、深浅拷贝
- 19、讲一下Promise使用
- 20、如何中断forEach循环
- 21、js本地存储的方式
- 22、聊聊异步
- 23、为什么不建议用index索引作为key
PHP面试题
1、为什么SQL语句不要过多的join?⭐
性能问题:每个join操作都需要对两个或多个表进行连接操作,这个操作需要消耗大量的计算资源和时间,如果join操作过多,会导致SQL的执行效率降低,从而影响整个系统的性能。
可读性和维护性问题:join操作会使SQL语句变得复杂,难以理解和维护。
2、PHP的垃圾回收机制?⭐
PHP可以自动进行内存管理,清除掉不需要的对象。
PHP使用了引用计数机制。每个对象都内含一个引用计数器,每个指针指向连接到一个对象 计数器加一。
当指针指向离开或者被设为null,计数器减一。
当某个对象的引用计数器为零时,PHP就会释放其占用的空间。
3、面向对象七大原则?⭐
单一职责原则:一个类应该只有一个职责。
开放封闭原则:在不修改源码的情况下,可以对其扩展,不影响原有功能。
里式替换原则:子类可以扩展父类,而不是覆盖父类或改变父类原有功能。
依赖倒置原则:依赖于抽象。
接口隔离原则:不要把功能写在一个接口中,不要依赖那些用不到的接口。
迪米特原则:降低类与类之间的耦合,没关系的类不要扯在一起。
合成/聚合复用原则:尽量使用trait和use,少使用extends。
4、PHP如何解决内存溢出?⭐
增加PHP可用内存大小。
及时销毁大数组或变量。
数据库读取大量记录时,用limit子句分批查询。
5、include和require的区别是什么?⭐
如果一个流程中加入require,无论条件成立与否都会先执行。
include有返回值,require没有(require速度比include快)。
require报错会终止程序执行,include报错会继续执行下去。
6、什么是临时表?⭐
MySQL在执行SQL语句时会临时创建一些存储中间结果集的表,这种表被称为临时表。
临时表只对当前连接可见,在连接关闭后,临时表会被删除并释放空间。
下面几种情况会使用到临时表:
form中的子查询。
order by和group by结合一些聚合函数时。
使用union查询时。
7、大表数据查询如何优化?⭐
索引优化。
SQL语句优化。
水平拆分。
垂直拆分。
建立中间表。
使用缓存。
选择更合适的字段类型。
列内容越少访问越快。
8、字段为什么要设置为not null?⭐
null值会影响一些函数的统计,如count遇到null值,这条记录就不会统计在内。
使用not in子查询时,有null值的情况下返回的结果都是空值。
MySQL在进行比较时,null会参与字段的比较。因为null是一种比较特殊的数据类型,数据库要做特殊处理,增加了数据库处理数据的复杂性。
9、如何优化查询过程中的数据访问?⭐
避免回表查询,尽量做到返回字段索引覆盖。回表就是在二级索引没找到全部数据,要回到聚集索引中查找。
数据分页处理,可以使用排序加子查询做分页优化。
只返回需要的字段。
10、如何优化where子句?⭐
1、不要在where子句中使用空值判断,字段尽量设置为not null。
2、在where和order by涉及到的列建立索引。
3、避免在where子句中,对字段使用表达式或者函数操作,会导致放弃索引使用全表扫描。
4、字符串类型字段使用时,不加引号,索引也会失效。
5、模糊查询时,如果是尾部模糊匹配,索引不会失效,如果是头部模糊匹配,索引会失效。
11、Redis使用场景?⭐
1、短信验证登录。
手机号为键保存验证码,校验验证码通过后将用户数据也存到Redis,以token为键保存用户数据并设置过期时间。当用户携带token访问接口时,刷新token有效期。
2、数据做缓存。
查询Redis数据是否存在,不存在则直接查询数据库,将查询到的数据写入Redis并设置一个过期时间,在修改数据库时删除缓存。
3、互斥锁。
Redis的setnx当做锁使用,setnx是当key不存在则创建,key存在则无法写入。获取锁时将线程标识存进去,通过锁的方式只让一个请求访问数据库建立缓存。锁要加过期时间。使用del key释放锁,释放锁时通过线程标识判断锁是不是当前线程的。
4、秒杀功能。
类似乐观锁方式:在每次下订单前判断促销商品的数量够不够,不够不允许下单。更改库存数量时加上一个条件,只更改商品库存大于0的商品。一人只能下一单问题要用到Redis的互斥锁。
Redis消息队列方式:Redis中判断库存数量,判断set集合中用户有没有下过单。如果没下过单就去扣减库存然后将用户id存入set集合。将生成的订单信息以阻塞队列的方式存入redis,后面单独有个线程去更新数据。
5、点赞功能。
获取用户id,判断id是否在redis的set集合中。如果不存在,表示未点赞,数据库点赞数加一,保存用户到redis的set集合。反之则将点赞数减一,用户id从set集合移除。
12、浏览器访问网址的详细过程?⭐
1、用户访问域名。
2、域名DNS解析。
3、请求到对应IP服务器和端口。
4、nginx监听到对应端口的请求。
5、nginx对url进行location匹配。
6、nginx将请求转发给php。
13、如何解决并发问题?
1、根据需求及模块功能拆分成多个子系统。
2、应用集群、负载均衡。
3、设置文件存储服务器、图片存储服务器、专用搜索服务器。
4、限流方式:验证码、IP黑名单、nginx控制并发连接数。
5、Redis消息队列。
6、MySQL优化,主从复制、读写分离。
14、简单秒杀系统并发考虑?
1、前端秒杀页面静态化。
2、前端控制提交频率,按钮设置为灰色。
3、nginx控制IP限流。
4、Redis预先将商品入队列,请求时,控制串行处理。
5、防止直接通过链接访问秒杀页面,秒杀链接通过父级页面点击获取唯一标识,没有携带标识的请求返回没抢到。
15、redis消息队列先进先出需要注意什么?
在使用一个list来实现队列操作时,所有任务统一都是先进先出,我们需要一个优先级的概念,这样就可以优先处理高级别的任务了。
1、队列正常操作是左进右出(lpush,rpop),在遇到高级别任务时,可以直接插队放入队列尾部(rpush),这样从队列尾部获取的任务就是高优先级任务了。
备注:(brpop是rpop的阻塞模式,当队列中没有元素时线程会阻塞等待) 语法结构:BRPOP key [key …] timeout 弹出列表尾部第一个元素并返回存储它的key
2、使用两个队列,一个普通队列,一个高级队列。针对任务的级别放入不同的队列,redis的brpop命令可以按顺序从多个队列中取值,brpop会按照给出key的顺序来找到第一个非空list,再从尾部弹出一个元素。
16、数据库索引的目的是什么?⭐
快速访问数据表中特定信息,提高检索速度。
创建唯一索引,保证数据表中每一行数据的唯一性。
加速表与表之间的连接。
使用分组和排序子句进行查询时,可以减少分组和排序的时间。
17、索引对数据库系统的负面影响是什么?⭐
创建和维护索引需要耗费时间,这个时间随着数据量的增加而增加。
索引需要占用物理空间,不光是表需要占用数据空间,每个索引也需要占用物理空间。
当对表进行增、删、改的时候,索引也要动态维护,这样就降低了数据的维护速度。
18、post是两次请求的原因?⭐
第一条为options请求,第二条才是我们预想的请求。
在正式请求之前,先进行一次预检请求。看服务器返回一些信息,浏览器拿到后看后台是否允许进行访问。
只要是带有自定义header的跨域请求,就会发生options请求。只需要避免跨域或者不在header头部加自定义内容。
19、PHP常用函数?⭐
ceil(9.99); 浮点数进一取整
floor(9.99); 浮点数舍去小数部分
round(3.1415, 2); 保留2位小数
intval(); 获取变量的整数值
isset(); 检测变量是否设置
empty(); 检测变量是否为空
strlen(); 获取字符串的长度
strtotime(); 将文本日期时间描述解析为Unix时间戳
str_repeat(’.’, 9); 重复使用指定字符串
str_split(‘hello’); 把字符串分割到数组中
explode(); 使用一个字符串为标志分割另一个字符串
implode(); 将数组值用预定字符连接成字符串
substr(); 截取字符串
strpos(); 查找字符串中某个子串的位置
str_replace(); 替换字符串中的内容
json_decode(); 对json格式的字符串进行解码
json_encode(); 对变量进行json编码
array_merge(); 两个或多个数组合并为一个数组
in_array(‘hello’, data); 数组中搜索给定的值 区分大小写
array_key_exists(); 判断数组中是否存在指定的key
list($a, $b, $c); 用数组中的元素为一组变量赋值
array_shift(); 移出数组中的第一个元素
array_unshift(); 在数组开头插入一个或多个元素
array_push(); 向数组最后压入一个或多个元素
array_pop(); 取出数组中的最后一个元素
count(); 计算数组个数
array_keys(); 返回数组所有的键,组成一个数组
array_values(); 返回数组所有的值,组成一个数组
uasort(); 使用自定义的比较函数对数组进行排序
arsort(); 数组按值从高到低排序
20、索引有哪几种类型?⭐
主键索引:数据列不允许重复,不允许为null,一个表只能有一个主键。
唯一索引:数据列不允许重复。
普通索引:基本的索引类型,可以重复也可以为null。
全文索引:是目前搜索引擎使用的一种关键技术。
21、mysql哪些字段适合建立索引?⭐
1、表的主键、外键必须有索引;
2、数据量较大,且查询比较频繁的表应该有索引;
3、在区分度高的字段建立索引,尽量使用唯一索引;
4、经常出现在where、order by、group by后面的字段,特别是大表的字段,应该建立索引;
5、索引应该建在小字段上,如果是较长的字符串字段,可以针对字段特点建立前缀索引;
6、联合索引的建立需要进行仔细分析,判断几个字段是否经常一起出现?单字段查询是否极少使用?如果是,可以使用联合索引;
7、频繁进行数据操作的表,不要建立太多的索引;
22、rabbitMQ消息队列的应用场景?⭐
1、消息队列可以作为消息中间件使用,例如PHP和Python通过rabbitMQ做数据的互联。
2、可以当做多进程异步操作来使用,将一个任务分配给多个服务同时执行。比如下单成功后的邮件通知和短信通知。
3、防止服务器一下处理过多请求,后端读取队列中数据是一条一条读取,服务器不会超负荷。
4、代码解耦合,拓展性更好。
23、Redis数据类型有哪些?⭐
string 字符串、list 链表、hash 哈希表、set 集合、zset 有序集合
24、Linux常用命令?⭐
du -sh /etc/ 查看目录所占用磁盘空间情况
df -h 查看磁盘剩余空间情况
find / -name [文件名] 根据名字查找文件
ps -ef | grep nginx 查看进程,通常与管道操作连用
kill [PID] 杀掉进程,后面跟进程id
top 查看资源占用情况,一般用来排查高占用的异常进程
25、laravel生命周期?⭐
所有请求的入口是index.php文件,从该文件中加载composer。
实例化app容器,绑定http、console内核。
在http内核中,将中间件注册到路由。
路由将请求转发到绑定的控制器。
通过路由中间件返回一个响应。
调用send方法将响应内容发送到用户。
26、找到执行很慢的SQL语句,如何分析它?⭐
1、慢查询日志找到耗时时间长的SQL来优化某一个SQL语句。
2、分析SQL执行计划,在select语句之前加上关键字explain。
27、InnoDB与MyISAM的区别?⭐
InnoDB支持事物,支持行级锁,支持外键。
MyISAM支持表锁,不支持行锁,访问速度相对更快。
28、如何查看MySQL死锁?⭐
可以通过查看MySQL的错误日志 error.log 来发现死锁信息。
29、如何解决发生了的MySQL死锁?⭐
MySQL的死锁检测机制会发现死锁并选择一个事务进行回滚。被回滚的事务会收到一个错误信息,程序可以重新执行这个事务或者提示用户失败。
30、如何避免MySQL死锁?⭐
1、尽量让所有事务按照相同的顺序访问数据库中的表,这样可以减少死锁的发生概率。
2、确保查询语句都使用了合适的索引,减少锁的范围。因为InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据时,InnoDB将对表中的所有记录加锁,此时会升级为表锁。
31、Redis缓存穿透?⭐
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效。
1、缓存空对象,设置过期时间。
2、Redis前加过滤器。
3、校验查询id的格式。
32、Redis缓存雪崩?⭐
缓存雪崩是指在同一时间段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库。
1、给缓存设置随机过期时间。
2、Redis集群。
3、互斥锁方式,Redis的setnx当做锁使用,setnx是当key不存在则创建,key存在则无法写入。获取锁时将线程标识存进去,通过锁的方式只让一个请求访问数据库建立缓存。锁要加过期时间。使用del key释放锁,释放锁时通过线程标识判断锁是不是当前线程的。
33、rabbitMQ常用的的交换机?⭐
direct交换机绑定队列时需要指定key,一个队列可以指定多个key。发送消息时交换机指定给哪一个key发消息。
34、rabbitMQ如何保证消息的可靠性?⭐
1、通过配置可以让交换机、队列、以及发送的消息都持久化。这样队列中的消息会持久化到磁盘,MQ重启后消息依然存在。
2、LazyQueue会将所有消息都持久化。
前端面试题
1、移动端适配方案
1、rem
2、百分比
3、vw/vh,vmin/vmax
4、flex
5、栅格布局
2、常用的vue2生命周期
created:实例已经创建完成,数据和方法都已存在。常用于发送请求获取数据,页面进入的方法需要立即执行。
mounted:dom已经挂载完成,可以操作真实dom。
updated:数据和视图都变了。
destoryed:组件销毁。常用于清除定时器等行为。
3、v-if和v-show
v-if:移除或添加dom元素。在dom首次判断是否显示还是隐藏,或者组件需要重新触发生命周期时使用。
v-show:通过css的display样式来控制显示隐藏。频繁的显示和隐藏时使用。
4、如何理解单向数据流
数据是单向流动,数据可以从父组件流向子组件,父组件的数据发生变化,会自动同步子组件,反之不允许。
5、为什么v-if和v-for不建议同时使用
v-for比v-if优先级高,每次都需要遍历整个数组,影响性能。
要避免出现这种情况,可以在外层嵌套template,在这一层进行v-if判断,然后在内部进行v-for循环。
如果条件出现在循环内部,可以通过计算属性computed提前过滤掉那些不需要显示的部分。
6、vue中css的scoped作用和原理
作用:实现组件的私有化,表示当前style属性只属于当前模块。
原理:scoped会在dom结构以及css样式加上唯一性标记,也就是css的属性选择器,以此来完成样式私有化。
7、虚拟dom和diff算法
虚拟dom本质就是一个js对象,用来描述真实的dom。
初始化渲染时候,会根据数据和模板生成虚拟dom树,当数据发生变化时,会根据新数据生成新的dom树,将两份虚拟dom树使用diff算法进行对比。
diff算法使用双指针算法,遍历旧的虚拟dom有两个指针,指向开始位置和结束位置。同理新的虚拟dom也有这两个指针,对比完成后前面指针会往后推,后面的往前推。
8、$nextTick
由于在vue中数据驱动视图是异步的,所以就会导致数据发生变化视图没有同步更新。想要获取到最新的视图就可以借助$nextTick。
9、$route和 $router作用
$route:获取当前路由信息。
$router:全局路由实例,可以使用路由方法进行前后跳转等。
10、项目优化
1、移除console.log。
2、webpack的代码分割功能来实现路由懒加载,把不同路由对应的组件分割成不同的代码块,当路由匹配的时候才会加载响应资源。
3、splitChunks提取公共资源,某一个文件被多次引入将他提取出来。
4、image-webpack-loader实现图片资源压缩。
5、使用SSR服务端渲染,首屏服务端直接返回。
6、UI框架按需加载。
7、减少data。
8、图片懒加载。
9、异步加载,导出组件。
10、清除事件定时器。
11、实现token无感刷新
企业上常用的解决方案是token + refresh-token(刷新令牌)来实现无感刷新。登录验证用户凭证时生成两个令牌,一个用于访问资源,一个用于刷新访问令牌,当token过期时,使用刷新令牌来获取新的访问令牌。
12、实现大文件上传
文件切片:使用文件对象原型上的slice方法,从原始文件中提取指定的部分内容,返回一个新的Blob对象。可以通过固定切片大小或者是固定切片数量来对文件进行切割,利用Promise.allSettled批量发送请求,来并行上传所有文件切片。每个切片文件需要一个按顺序的标记位,文件需要按顺序合并。要给切片提供一个和文件内容相关的hash值,用于标记每个文件切片,确保切片与原始文件内容相关。
断点续传:因为标记的hash值是使用文件内容编译的,后端可以记住已经上传过的切片,自行过滤就行了。
13、axios取消请求
axios上有一个CancelToken构造函数,通过该构造函数创建一个实例,将该实例绑定给需要取消请求接口的cancelToken属性上,当要取消请求时调用CancelToken内部返回的回调就可以实现取消接口请求。
14、websocket
websocket是一种网络通信协议,提供了在单个TCP连接上进行全双工通讯的功能。它提供了实时双向通信,非常适合需要实时数据更新的应用。与HTTP相比,websocket在建立连接后可以随时发送消息,减少了每次需要发送HTTP头的开销。我们项目中使用 socket.io-client 来实现客户端代码,它是基于 websocket 的库。
15、地址栏输入url过程
1、检查url是否合法。
2、域名DNS解析。
3、三次握手建立TCP连接:第一次客户端发送网络包,服务端收到了。服务端确定了客户端发送能力和服务端的接收能力是正常的。第二次服务端发包,客户端收到了,客户端确定了服务端接收发送能力和自己的接收发送能力是正常的。第三次客户端发包,服务端收到了。服务端就能确定了客户端和自己的发送接收能力是正常的。
4、发起http请求
5、响应请求
6、页面渲染:html生成dom树,style生成css规则树。
7、tcp终止一个连接,四次挥手:第一次客户端发送一个FIN报文,此时客户端停止发送数据,等待服务端确认。第二次服务端同意,响应服务端收到的消息。此时处于半链接,有可能有数据没发送完,继续发送未发送完的数据。第三次服务端发送完成,服务端要彻底断开了,给客户端发消息并处于等待状态。客户端收到FIN之后,第四次发送一个应答,服务器只要收到客户端的确认就进入关闭状态。
16、GET和POST请求的区别
1、get请求一般用来请求获取数据。post请求一般作为发送数据到后台,传递数据,创建数据。
2、get请求传递的参数显示在地址栏,安全性低,且参数的长度也有限制(2048字符)。post请求则是将传递的参数放在request body中,安全性更高,且参数没有长度限制。
3、get请求刷新浏览器或者回退没有影响,post请求则会重新请求一遍。
4、get请求可以被缓存,也会保留在浏览器的历史记录中。post请求不会被缓存。
5、get请求通常是通过url地址请求,post常见的则是form表单请求。
6、get产生一个tcp数据包,post产生两个tcp数据包。
17、JavaScript中的数据类型和存储上的区别
基本数据类型:Number、String、Boolean、Undefined、null、symbol、bigInt
复杂数据类型:Object、Array、Function
简单类型的值存放在栈中,在栈中存放的是对应的值。引用类型对应的值存储在堆中,在栈中存放的是指向堆内存的地址。所以当简单类型赋值时,是生成相同的值,两个值对应不同的地址。复杂类型赋值,是将保存对象的内存地址赋值给另一个变量,也就是两个变量指向堆内存的同一个地址。
18、深浅拷贝
浅拷贝:指拷贝复制目标数据,生成一个新数据。如果是基本数据类型,则直接拷贝目标数据的值。如果是引用数据类型,第一层数据有引用类型的值则直接拷贝地址。由于拷贝的是地址,指向的是同一个对象,所以数据修改会相互影响。
深拷贝:会拷贝所有的属性,如果这些属性是对象,也会对这些对象进行深拷贝。对于深拷贝后的对象,即使原对象的属性值发生变化,拷贝后的也不会受到影响。
19、讲一下Promise使用
简单说它是一个容器,通常用于表示一个异步操作的最终完成和结果。可以配合async和await将异步代码变为类似同步的,new Promise()传入的函数是会立即执行的是同步的,new Promise().then()是异步的。
20、如何中断forEach循环
使用return、break、continue的方式都不能终止forEach循环,可以使用throw new Error终止循环。
21、js本地存储的方式
cookie:为了辨别用户身份存储在用户本地终端上的数据,解决http无状态导致的问题。
localStorage:持久化本地存储,除非主动删除数据,否则数据永远不会过期。
sessionStorage:与localStorage使用方法一样,但是页面关闭了数据也会删除。
22、聊聊异步
可以使用Promise发起异步。如果是同步任务,主线程自己来执行。如果是异步任务,交给浏览器执行。每个异步任务执行完后,会将回调放进任务队列,等主线程任务全部执行完后,再去取任务队列中的任务。
23、为什么不建议用index索引作为key
使用index作为key和没写基本没区别,因为不管数组的顺序怎么颠倒,index都是0,1,2这样排列,导致vue会复用错误的旧子节点,做很多无意义的额外工作。