面试系列 -- PHP后端面试常见问题总结
Posted 躬匠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试系列 -- PHP后端面试常见问题总结相关的知识,希望对你有一定的参考价值。
开篇:自我介绍
面试官好,我叫XXX,XXX年X月XX毕业于XXX,专业为计算机科学与技术。
毕业X年的时间里一直在从事php 、Go、Python、C#语言的后端开发工作,其中主流开发语言是PHP;对前端的相关知识也有所了解,如Js、Jquery、Bootstrap、Vue、React等;
从事领域主要是XXXX。
对于大流量、高并发类的服务以及中台类的开发都有一定的经验,曾XXX的应用以及服务。
曾先后就职于XXXX、XXXX公司。在目前所在的公司主要负责XXXXXX工作。
在工作之余喜欢写一些博客类的文章,也喜欢专研技术。
最主要的是要描述出自己的优势,以及与该公司招聘职位的匹配度;时间最好控制在两分钟左右的时间(时间刚好够面试官大概看一下你的纸质简历);
接下来面试官会针对你的简历或你上面的自我介绍展开提问。
一、介绍你最得意的一个项目
一定要介绍这个项目的背景,但要尽可能的以最简短的语言描述清楚;重点阐述自己在这个项目中起了什么角色,做了哪些工作,以及遇到一些问题时的解决思路。
不要只停留在描述项目上,面试官想知道的是你在这个项目中遇到什么问题(what)、如何(how)以及为什么(why)这样采用某种技术方案。
这个要数XXX了。
首先,XXXXXX;
其次,XXXXXX
最后,XXXXXX
二、数据结构/算法
基本排序算法要会写,时间复杂度要会推算;主要是冒泡排序、快速排序,、选择排序、插入排序、堆排序、归并排序
查找算法,要会写顺序查找、二分查找法、hash查找、bitmap、布隆过滤器,实际场景要会应用。
注意:现在是大数据的时代,面试官很可能会问你一下大数据量排序、查找类的算法
实例考点:
- 猴子选大王
- 斗地主项目设计
- 实现随机函数
- 字符串中元素各种变形查找
- 123456 六个数放到三角形三个顶点及中点上,使每条边上的数字和相等
- 一个超大文件里面存放关键,统计每个关键的个数, 问如何实现
- 一个10G的文件,里面存放关键字, 但内存只有10M, 问如何实现统计, 出现关键字次数最高的前100个
- 实现单链表与双链表
- 实现有权重的随机算法
三、设计模式
简单说明一下你所了解的设计模式
- 单例模式 :私有构造函数、public获取对象方法
- 工厂模式:创建对象的工厂,每个类会继承一个接口,这样保证了对上层方法的一致性。
- 抽象工厂模式:提供对个对象创建工厂,客户端根据具体的场景选择不同的工厂,再由具体的工厂创建对象
- 原型模式:使用该模式的目的是通过使用克隆以减少实例化对象的开销,常用于创建重复的对象,对于PHP而言,可以使用clone方法
- MVC模式 :Controller-Model-View
四、架构
画一下你们组内的架构图
这个就需要自己平时多关心一下组内的架构,同时,多锻炼自己的画图能力
了解微服务架构吗
注意:架构的选型并不是一步升级到微服务架构的,一定是根据业务的发展逐渐迭代升级的。
- 单体架构
1)各个模块之间共享CPU、内存、磁盘等系统资源以及数据库存储,资源共享会造成模块之间的相互影响,比如可用性
2)架构不容易扩展,即使可以水平扩展,当想要扩展某一个模块时,单体架构是无法支持的
3)开发、测试、维护都很困难
- 微服务架构
微服务架构的原则之一是有效隔离各个模块之间的资源共享问题,将每个模块独立部署,各个模块之间通过RPC框架进行调用。
目前,每个模块都是部署在docker中的。
微服务模块拆分的原则是按照业务进行拆分。
微服务优点:
易于开发和维护:一个微服务只会关注一个特定的业务功能,所以业务清晰、代码量较少。开发和维护单个微服务相对简单。
局部修改容易部署:单体应用只要有修改,就得重新部署整个应用。微服务解决了这样的问题。一般来说,对某个微服务进行修改,只需要重新部署这个服务即可。
技术栈不受限制:在微服务架构中,可以结合项目业务及团队的特点,合理的选择技术栈。
按需伸缩:可根据需求,实现细粒度的扩展
了解Docker以及K8S、etcd的相关知识吗?
Docker是新一代的容器技术,通过将应用程序以及应用程序的依赖项打包放到一个轻量级、可移植的容器里,实现了敏捷开发以及持续交付的能力。而且,容器相较于虚拟机,具有机器资源利用率高、敏捷开发和持续交付和部署和更易扩展的能力。
Docker通过将应用划分为一个一个的容器,做到了应用于应用之间的隔离性。
了解CI、CD吗
敏捷开发(Agile Development)、持续集成(Continuous Integration)、持续交付(Continuous Delivery)和开发运维一体化(DevOps)
五、PHP基础知识
PHP的魔术常量、系统常量
八大魔术常量:__FILE__ __LINE__ __DIR__ __CLASS__ __FUNCTION__ __METHOD__ __TRAIT__ __NAMESPACE__
几大魔术函数【拦截器】:__construct __destruct __get __set __isset __unset __call __callstatic __tostring __clone __autoload,静态函数相当于回调函数,在执行某类操作时便会被触发。
系统常量:PHP_OS PHP_VERSION PHP_SAPI E_NOTICE E_WARNING E_DUBUG E_FATAL E_CTRICAL。
八大超全局变量:$_GET $_POST $_COOKIE $_SESSION $_REQUEST $SERVER $ENV $_FILES
常用数组函数
explode json_encode json_decode implode
array_pop:弹出最后一个元素
array_push :在数组尾部插入一个元素
array_shift:移除头部元素
array_unshift:在头部插入一个或者多个元素)
array_keys :返回数组中部分的或所有的键名并未其建立数字索引
array_values:获取数组中所有的值并给其建立数字索引
array_key_exists :判断某个元素是否存在
array_search:返回某个元素的key
array_diff:两数组差集,判断的是数组元素的值,返回的数组会保持映射关系,具体是关联数组或索引数组,与第一个元素有关
array_intersect:两数组交集,判断的是数组元素的值,返回的数组会保持映射关系,具体是关联数组或索引数组,与第一个元素有关;array_intersect_assoc:会将索引也最为比较的依据,返回索引和数值均一致的数据。
array_column:取出二维数组中的某一列
array_count_values:统计数组中每个值出现的次数
array_unique : 移除数组中重复的值
array_sum:计算数组值的总和,以整数或者浮点数的形式返回
array_reverse:数组元素反转
array_slice($array, $offset, $length) :从数组中取出一段
array_splice($array, $offset, $length, $replace) :将数组中的某一段替换掉
array_rand:从数组中随机取出一段元素,如 array_rand($arr, 2),随机返回两个元素,注意返回的是键,而不是值,所以一定是索引数组。
array_flip:key value 反转
array_map:对于数组使用回调函数
重要的排序函数:排序都是使用的快排算法
常用字符串处理函数
strlen:字符串的长度
strstr:获取当前字段之后的字符串
substr:按照index length截取字符串,其中index length可为负,可从后面取值
如果 start
是非负数,返回的字符串将从 string
的 start
位置开始,从 0 开始计算。
如果 start
是负数,返回的字符串将从 string
结尾处向前数第 start
个字符开始。
如果提供了正数的 length
,返回的字符串将从 start
处开始最多包括 length
个字符(取决于 string
的长度)。
如果提供了负数的 length
,那么 string
末尾处的 length
个字符将会被省略(若 start
是负数则从字符串尾部算起)。
substr_count:计算字串出现的次数
substr_replace : 替换字符串的子串
strcmp:字符串比较
strtotime:转换为时间戳格式
strtolower :字母转小写
strtoupper:字母转大写
ip2long()、long2ip():IP与整形互转
str_split():字符串转换为数组
mysql_escape_string():对字符串特殊处理一下
strpos:字符串首次出现的位置;strrpos:字符串最后一次出现的位置;
stripos:不区分大小写,字符串首次出现的位置;
str_replace($find, $replace, $data)会对find到的数据进行替换操作,如果$find以及$replace均为数组,则会进行一个映射替换;
exit return 区别
exit会直接退出终止执行,退出进程;return 只是会返回上级调用处;
exit是函数,而return是语法;可以执行exit("this is just a test");
echo 是语法而不是函数;print 是函数,输出字符串;print_r打印出变量的信息;var_dump输出变量的信息;
PHP支持多线程吗?
PHP默认不支持多线程,但是可以通过扩展开启多线程。对于多线程而言,会存在线程安全的问题。
PHP采用了TSRM(线程安全资源管理器)的方式来进行线程安全管理工作:具体就是为每个线程复制一份全局变量和静态变量数据,保证他们不会相互影响(很耗费内存空间,特别是线程以及全局变量比较多的时候)。
PHP安全知识
disable_class、 disable_function:设置禁用函数、类,一般是禁用系统函数,如exec()、system()
display_errors (生产环境中关闭)
phpinfo() :打印出PHP相关信息,如扩展信息、PHP版本、系统信息,生产环境中一定不能开启。
memory_limit magic_quotes_gpc
php较低版本(php version <= 5.3.0)会对所有的GET、POST和COOKIE 数据自动运行addslashes()。因为较低版本的php,magic_quotes_gpc默认开启,所以,此时您不应对已转义过的字符串使用 addslashes(),因为这样会导致双层转义。遇到这种情况时,可以使用函数get_magic_quotes_gpc()进行检测。
addslashes(添加斜线)/stripslashes 、strip_tags(移除html标记)
PHP 关键配置项有哪些
max_execution_time:最大执行时间。
max_request:限定每个php-cgi的处理请求数,达到这个数量之后便会被kill掉,防止内存泄露
request_terminate_time:php-cgi处理时间,超过这个时间之后php-cgi便会被kill掉,会导致502错误
display_error / error_log / log_error error_reporting :错误日志相关
request_slowlog_timeout / slowlog :慢查询日志
pm = static;pm.max_children ;pm.start_servers;pm.max_spare_servers:配置php-cgi进程信息
如何防止Sql注入
1)不要使用字符串拼接函数组成sql语句;使用参数类型判断以及类型转换;
2)使用参数绑定方式执行sql;PDO即是使用的这种方式;
3)使用PHP自带的addslashes()对单引号'、双引号"、反斜线\\ 字符进行转义,htmlentities()、htmlspecialchars()、mysql_escape_string() ;还有专门进行shell的过滤函数:escapeshellarg escapeshellcmd 分别对参数以及命令转义。
4)使用PDO
PHP的生命周期/启动流程
完整的生命周期为模块初始化、请求初始化、请求处理、请求关闭、模块关闭五大阶段。
cli模式下,每个脚本都会完整的执行上面的五大阶段;对于fastcgi模式而言,只在启动时会执行模块初始化,之后的请求都走了请求初始化、处理请求、请求关闭三大阶段,在fastcgi关闭时执行模块关闭阶段。
各个扩展的加载也是在模块初始化阶段完成的。
说一下PHP的(内存)垃圾回收机制
每一个变量对应一个zval数据结构,在该结构内还有一个val结构体,该结构体内有一个引用计数(php7而言,对于php5,这个引用计数是保存在zval结构中的),标识该对象的引用数,当对象的引用计数为0时代表这个对象可被回收。
对象的refcount减少的时机:修改变量、函数返回(释放局部变量)、unset变量
对于数组和对象而言,可能存在变量中的成员引用变量本身的情况,也就是循环引用,这样会造成这个变量永远不会被内存回收,而成为垃圾。PHP里对于这种情况给出了垃圾回收机制:
如果数组、对象的引用计数减少而且不为零,则认为他们可能是垃圾,把他们放到垃圾收集器里。等垃圾收集器到了一定的数量之后,进行垃圾处理:对所有可能的垃圾refcount减1,如果为1,说明是垃圾,则进行内存回收;如果不为1,说明还有其他变量在使用,refcount重新加1;
这种对象复用以及垃圾回收机制在其他语言中也有体现:redis中也使用了引用计数表示每个对象的引用数量。
数组的底层实现
数组的低层实现是hash表,php里的数组是有序的,会按照插入的顺序输出。
为了保证元素有序,低层保存了中间映射表。
使用hash表都会有hash冲突的风险,解决冲突的方式是使用链地址法。
redis以及mysql、mongodb中都有使用hash表。
hash表在不同的语言中有不同的称呼:散列表、字典、dictionary。
PHP常见运行模式有几种
常见的有四种:cli cgi fastcgi apache2hander;可以通过php的常量PHP_SAPI得到。
cli:命令行模式
cgi:fork-and-execute模式
fastcgi:cgi的升级版,常驻型cgi模式
模块:mod_php5 + Apache
对于php-fpm而言,它是master-slave模式,master与slave之间是通过共享内存进行通信的。
常见的框架, 以及各自优缺点.
常见的有CI YII ThinkPHP YAF框架
https://www.cnblogs.com/lovele-/p/9228750.html
PHP7与PHP5的区别
7的性能较5的性能有大幅度的提升(5 - 6倍),这个提升主要体现在以下几个方面:
(1)引入了抽象语法树的结构,解耦解析器与执行器;
(2)对于zval结构而言,大小从24byte减少到了16byte;hash表也从72byte减少到了56byte;数组元素Bucket结构也从72byte减少了32byte,降低了对系统资源的占用。
(3)对于有些fatal错误以异常的形式抛出了,可以被捕获,使程序更加健壮了;
(4)限制了方法里一定要指定参数的类型和返回值的类型,这样就减少了对象上下文判断的开销
(5)引入可Native TLS
(6)将execute_data、opline采用寄存器形式存储,访问速度更快了(寄存器的访问速度要快于内存)。
PHP5版本可以使用接口、数组、接口作为函数(方法)的类型提示,但对于标量类型(字符串、整形、bool)不能作为类型提示。
反射
面向对象的编程语言都提供了一种反射的能力,可以通过API或者class name来获取对象或者扩展、类的属性方法信息。
运行出错的时候会显示出反射信息,还可以利用反射来生成文档
位运算
PHP里也存在位运算符,可进行位运算。
六、Web Server
Apache与nginx对比
Apache
优点:
模块众多(确定不需要的话就关闭有些模块),功能丰富,性能稳定;
对于php python语言而言,可以将其作为apache的扩展包含进来,无须与后端服务器交互;
模式丰富,支持prefork worker event三种;
缺点:
对于worker、event两者而言,存在线程安全的问题;
模块众多,但是很多是不需要的(优点反而成了缺点),是属于重量级的web server;
不适合高并发处理,不能作为前端服务器,只可以作为后端服务器。
Nginx:
优点:
Nginx相对于Apache而言是轻量级的;
支持反向代理、负载均衡(七层负载均衡器)、web 缓存,还可以作为邮件服务器。
其异步非阻塞的事件处理模型可以支持很高的并发,适合作为前端服务器;
缺点:
nginx的功能相对于apache而言还是不够稳定的;
需要通过socket或者http与后端服务器进行通信,整个请求的耗时较Apache会稍微多一些;
nginx是采用的master-slave的模式,master与slave之间是通过管道通信的(php的master与slave之间是通过共享内存的方式通信的)。
Nginx的rewrite里都有那几个flag以及各自的区别
redirect:302跳转到rewrtie后面的地址。
permanent:301永久调整到rewrtie后面的地址,即当前地址已经永久迁移到新地址,一般是为了对搜索引擎友好。
last:将rewrite后的地址重新在server标签执行。
break:将rewrite后地址重新在当前的location标签执行
七、网络
常见的http code
100 continue 204/205 服务器未返回内容;301 永久重定向;302 暂时重定向; 304 内容为改变;400 bad request ,请求未携带host信息;403 无权限 ;404 not found 未发现 ;500 服务器错误 ; 502 bad gateway ;503 服务器不可用;504 gateway timeout;
502:php-fpm.conf中配置的request_terminate_time,当脚本执行时间超过这个时间时会产生502;
504:fastcgi_read_timeout:脚本执行时间超过这个时间时会产生504;
http1.0、http1.1、http2.0区别
http 1.0是短连接(connection:closed)、无状态的;http 1.1是支持长链接的(connection:keep-alive),但是对于客户端的多次并行请求,服务器只能顺序返回,而不能并行响应;http 2.0是支持二进制分祯的,解决了不能向客户端并行发送响应数据的问题,而且服务器可以主动向客户端推数据;
常见的请求方式
常见的也就只有get post put delete trace options head等几种方式
PUT:用户用户上传数据,向服务器写入文档;
HEAD:服务器只会返回头部信息,不会返回主体部分,常用于对资源头部检验。
TRACE:请求追踪,根据一个请求的完整调用过程
DELETE:删除服务器上的资源
OPTIONS:获取服务器支持的各种功能
HTTP与HTTPS的区别
HTTP:无状态,提供明文的数据传输,不安全
HTTPS:http + ssl,提供安全的数据加密传输以及身份认证功能;使用非对称加密和对称加密两种方式实现的。
https协议中最耗时的一段是在ssl握手阶段;CA证书是统一放在CA证书中心;
客户端和服务器建立连接之后,服务器会向客户端发送CA证书,之后客户端拿着CA证书去CA中心进行身份验证并获得公钥信息;
之后,客户端和服务器约定密钥生成算法,之后客户端把生成的密钥使用公钥加密,服务器接收到消息之后会拿自己的私钥进行解密,这个时候客户端和服务器便各自拥有一个套相同的公钥、密钥了;
建立SSL链接之后,服务器向客户端发送的数据都会使用公钥进行加密,而客户端会拿私钥对加密的数据进行解密,得到原始的数据。这个过程中,即使加密的数据被第三方获取到,因为他们没有密钥,数据也没法解密。
https://blog.csdn.net/b5694708/article/details/78445027
OSI七层模型
七层模型从上到下依次为应用层(HTTP(80) HTTPS(443) SMTP(25) FTP(21) Telnet(23)、ssh(22))、表示层、会话层、传输层(UDP TCP)、网络层(IP)、数据链路层(MAC)、物理层
TCP与UDP的区别
相同点:
是两者都是OSI七层模型里的传输层协议,都是全双工通信。
区别点:
TCP协议是有连接的,有连接的意思是开始传输实际数据之前TCP的客户端和服务器端必须通过三次握手建立连接,会话结束之后也要结束连接。而UDP是无连接的;
TCP协议保证数据按序发送,按序到达,提供超时重传来保证可靠性,但是UDP不保证按序到达,甚至不保证到达,只是努力交付,即便是按序发送的序列,也不保证按序送到;
TCP协议对系统资源要求比较多了;
TCP有流量控制和拥塞控制,UDP没有,网络拥堵不会影响发送端的发送速率
TCP是一对一的通信,而UDP则可以支持一对一,多对多,一对多的通信。
TCP面向的是字节流的服务,UDP面向的是报文的服务。
简述TCP三次握手以及四次挥手
为何需要三次:避免在网络结点中流转过久的连接请求又重新回到了服务器,而服务器不知道这个请求是无效的,会向客户端发送确认报文,会造成重复的连接建立。
四次放手:首先是客户端主动发送断开连接报文,服务器端发送确认报文;然后服务器发送断开连接报文,客户端回复确认报文
https://blog.csdn.net/qzcsu/article/details/72861891
如何实现浏览器跨域请求.
常见的有jsonp方式、服务器代理、CORS三种方式,其中Jsonp与CORS(跨域资源共享)两者的区别如下。
- JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求
- 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理;而JSONP主要结合Ajax使用
- 老的浏览器支持JSONP却不支持CORS,而绝大多数现代浏览器都已经支持了CORS
常见的负载均衡策略
请求转发策略:
1) 轮询 请求依次交给下一次server处理
2) 加权轮询
3) IP hash 客户端的请求固定会落到某个server上
4) 最少连接数 调度器会记录每个server的连接数 然后选择最少连接数的server提供服务,这样可以保证每个server的负载均衡。
这其中使用到了VIP NAT LVS BVS BGW BFE 等知识
LVS有三种工作模式:NAT、TUN、DR,目前使用最广泛的也就是DR模式。
负载均衡有两层、三层、四层、七层负载均衡几大类。其中,LVS是属于四层负载均衡;nginx以及BFE均属于七层负载均衡。
关于header的各种参数的作用
session与cookie的区别
本质原因是http是无状态的,需要通过session、cookie机制来保持会话信息。
session是将会话数据保存在服务器端,比较安全;session数据可以保存在数据库、文件或者redis中;高并发情况下会产生大量的session数据,对于存储在服务器文件而言,可能会造成服务器资源的消耗紧张;
常常采用的是将session数据存储在一个单独的服务中,例如公司的passport服务。
cookie是将数据保存在客户端,数据不太安全;可以设置cookie的key 、val 、 失效时间 、域、 path;对于未设置失效时间的cookie则认为是会话cookie,它的生命周期与浏览器是同步的,浏览器关闭之后cookie便会被删除
cookie跨域请求携带如何解决
浏览器在发送请求时会根据请求的地址查找与该地址匹配的cookie信息一起发送。
可以设置cookie的domain为根域名,这样在发起所有二级域名的请求时都会携带上该cookie信息。这也是sso单点登录的原理。
浏览器同源策略:不允许访问跨域的Cookie
1)如果是域名归属于同一个根域名,可以设置cookie的domain域为根域名(或者域名交集),无须在做其他操作
2)如果两个域名是完全不同的,则通过ajax方式访问,开启ajax的withCredatials:true选项;
1.ajax会自动带上同源的cookie,不会带上不同源的cookie
2. 可以通过设置ajax的withCredentials为true, 后端设置Header的方式让ajax自动带上不同源的cookie。但是这个属性对同源请求没有任何影响,会被自动忽略。
session跨域共享(分布式session)
session复制:服务器之间同步session信息,对于服务器较多时,不太友好。
数据库存储:将session信息存储在数据库中,问题是有时候数据不需要持久化存储,对于数据库的操作比较频繁,而且数据库的性能也不高,远达不到内存级别的数据存取。
session第三方存储,有一个独立的session服务,session数据都存储在该服务上,常见的是放到缓存中去。问题是会增加一次与session服务的交互。这种比较好。
衍生:sso cas passport 这些内容均用到了session的知识。
常见的网站攻击以及如何防止
DDOS CSRF XSS SQL注入
https://blog.csdn.net/m0_37477061/article/details/80750341
事件驱动模型
常见的事件驱动模型以及相互之间的区别。
八、正则表达式
\\S:匹配任何非空白字符;
\\s:匹配任何空白字符,包括空格、制表符、换页符等等;
\\r :匹配回车
\\n:匹配换行符
\\f :匹配换页
\\t :匹配制表符
. :匹配除换行符 \\n 之外的任何单字符。
\\d:匹配数字字符,与[0-9]相同
\\D:匹配非数字字符,与[^0-9]相同
\\w:匹配任何只包含字母数字字符和下划线的字符组成的字符串,与[0-9a-zA-Z_]相同
\\W:匹配字符串,忽略下划线和字母数字字符串
i:忽略大小写
正则表达式在很多地方都有使用,一定要牢记使用。
九、操作系统
进程与线程的区别与联系
进程是操作系统分配和管理资源的基本单位,每一个进程都有一个自己的地址空间 。进程至少有 5 种基本状态,它们是:创建态,等待态,执行状态,阻塞态,终止状态。
进程之间可以通过信号量、共享地址、管道、信号等多种方式进行通信。
线程是CPU调度的基本单元,一个进程内至少会有一个主线程,一个进程内的多线程之间会共享进程的地址空间,会造成资源的竞争。
进程、线程、协程
https://www.cnblogs.com/lxmhhy/p/6041001.html
进程(线程)间通信的方式
https://www.cnblogs.com/tomato0906/articles/7748134.html
十、分布式
常见的分布式服务框架:
RPC:remote procudure call 远程进程调用。
如阿里巴巴的hsf、dubbo(开源)、Facebook的thrift(开源)、Google grpc(开源)、Twitter的finagle等。
分布式锁
POI配置平台的提测、发布使用到了分布式锁。
有三种实现方式:
1)数据库,又分为插入记录和使用排它锁(select ... for update)
2)redis 的setnx()、expire()
3)zookeeper
可以使用缓存来代替数据库来实现分布式锁,这个可以提供更好的性能,同时,很多缓存服务都是集群部署的,可以避免单点问题。并且很多缓存服务都提供了可以用来实现分布式锁的方法,比如edis的setnx方法等。并且,这些缓存服务也都提供了对数据的过期自动删除的支持,可以直接设置超时时间来控制锁的释放。
使用Zookeeper实现分布式锁的优点
有效的解决单点问题,不可重入问题,非阻塞问题以及锁无法释放的问题。实现起来较为简单。
使用Zookeeper实现分布式锁的缺点
性能上不如使用缓存实现分布式锁。 需要对ZK的原理有所了解。
https://blog.csdn.net/xlgen157387/article/details/79036337
分布式事务
具体有以下几种解决方案:
1)两阶段提交
https://www.cnblogs.com/savorboard/p/distributed-system-transaction-consistency.html
CAP理论
C:强一致性;A:可用性;P:分区容错性
对于一个分布式系统,做多只能满足两个。
分布式系统提供的是最终一致性,而不是强一致性。
分布式锁和分布式事务都是为了保证数据的最终一致性。
分布式系统调用链监控
https://yuandongming.blog.csdn.net/article/details/88583049
十一、MySql
常见的存储引擎
常见的有MyISAM、InnoDB、Memory三大存储引擎,其中,5.5+之后的版本,InnoDB就是默认的存储引擎了。
InnoDB:支持事务、外键、行级锁、热备份、崩溃恢复后安全恢复、支持高并发、支持聚簇索引。
MyISAM:支持表级锁、空间索引、全文索引,对高并发支持不够好。
Memory:支持hash索引(通过链地址法解决hash冲突);不支持blob、Text两种数据类型,当检索这这两种类型的数据时会使用临时表。
表结构的优化
当您的库中删除了大量的数据后,您可能会发现数据文件尺寸并没有减小。这是因为删除操作后在数据文件中留下碎片所致。
可以在删除表记录或者修改表的结构之后进行optisize table 进行表结构的优化
optisize 只对MyISAM InnoDB存储引擎有效,可以利用未使用的空间,释放出来,并整理数据文件的碎片。
删除操作其实是假删除,空间并没有被释放,需要通过optisize table进行一下优化。
事务的特性
而事务的 ACID 是通过 InnoDB 日志和锁来保证。事务的隔离性是通过数据库锁的机制实现的,持久性通过 Redo Log(重做日志)来实现,原子性和一致性通过 Undo Log 来实现。
MySql的事务是两阶段提交。
参与者(所有节点RM)将操作成败通知协调者(事务管理器TM),再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。
事务的隔离级别
事务有四种隔离级别:未提交读、提交读、可重复读、串行化
第一种隔离级别会造成脏读、不可重复读和幻读;第二种隔离级别容易造成不可重复读、幻读;第三种隔离级别会造成幻读;第四种隔离级别会强制的将并行操作改为串行,可避免幻读的出现;
MySql默认的是可重复读。
MySql采用了MVCC和间隙锁来防止幻读的出现。
MVCC通过添加两个隐式的列,在很多情况下都避免了加锁操作,如果需要加锁,也只锁定了必要的行。
常见的锁
常见的有读写锁,也成为共享锁、排它锁;间隙锁;行锁、表锁;
InnoDB存储引擎会根据隔离级别自动加锁,但是也可以主动加锁;
select .. ... for update(排它锁); select ..... lock in share mode(共享锁)
如何解决死锁的问题
将持有最少行级排它锁的事务进行回滚。
MySql 字段类型有那些
int、varchar 、char 、datetime、 timestamp、 text、 blob 、decimal。
其中,数据库建表时应该选择最小、最简单的数据类型为标准。
blob用来存储二进制数据,没有排序规则和字符集;text用来存储字符串数据,有排序规则和字符集;对于Memory而言,他不支持这两种数据类型,会导致某些查询使用临时表。
对于varchar、char,varchar存储时有产生碎片的可能,因为在更新操作时有可能新的字符串长度比原来的更长,如果页内没有更多的空间存储,MyISAM会将行拆成不同的片段存储;而InnoDB会分裂页来使行可以放进页内。
而且需要额外的1或者2个字节(255个字节)来存储字符串的长度;
所以,对于频繁变更的字段以及非常短的列,都适合使用char而不是varchar来存储数据;而且,对于可变长字符串,其在临时表和排序时可能悲观的按照最大长度分配内存。
数据库设计的三范式
完全的遵循三范式会导致执行过多的关联操作,而反范式会导致执行过大的distinct操作。
每个属性不可再分;非主属性依赖于主属性;消除传递依赖
https://www.cnblogs.com/knowledgesea/p/3667395.html
数据库架构
常见的有主从同步、读写分离架构、水平切分架构
读写锁耦合在一起,对读写性能都不高。
-
读写分离,解决“数据库读性能瓶颈”问题
-
水平切分,解决“数据库数据量大”问题
为何使用B+树作为索引的数据结构
1、二叉树与B/B+数具有相同的查找时间复杂度O(log2n),所以时间复杂度不是选择B树的原因
2、二叉树未能很好的应用磁盘的预读功能(每次磁盘预读的数据都是无用的),会造成较多的磁盘I/O操作;B B+树都很好的运用了磁盘的预读功能,磁盘页的大小与节点的大小一致(InnoDB为16kb),减少了磁盘的I/O操作。
3、其中,对于B+树,又通过指针将叶子结点链接了起来,对于范围查找十分有效
4、B+ /B 树节点的个数也较二叉树有所增多,可以减少节点的查找次数
索引的优点
主键索引、唯一索引、一般索引、hash索引(memory存储引擎支持)、聚簇索引、空间索引、全文索引(MyISAM支持)
索引的优点主要有三个:
1)大大减少了服务器需要扫描的数据量
2)可以帮助服务器避免使用临时表和文件排序
3)可以将随机I/O变为顺序I/O
InnoDB支持聚簇索引,MyISAM不支持聚簇索引;
InnoDB的聚簇索引和主键索引是一样的;主键索引的页子结点中存储的都是索引值以及行记录;非聚簇索引页子结点里存储的都是记录的物理地址;
MyISAM主键索引和非主键索引叶子节点中存储的都是记录的物理地址;
为何要使用自增ID作为主键
https://yuandongming.blog.csdn.net/article/details/85610119
MySql性能优化方案
https://yuandongming.blog.csdn.net/article/details/88363031
你知道的几个关键配置项
innodb_buffer_pool_size:InnoDB存储引擎在用,这个缓冲用途很大。InnoDB可以用它还缓存索引、数据、自适应hash索引、锁、以及写入操作。其中,可以将写入操作进行合并,开启一个线程,定期把这种写入操作持久化到磁盘中去。
key_buffer_size:键缓冲大小,主要是MyISAM存储引擎在用
query_cache_size:查询缓存
sort_buffer_size:文件排序大小
thread_cache_size:线程池大小
https://www.cnblogs.com/xu743876685/p/8642027.html
如何查看MySql服务器的状态
show full processlist
show global status
show table status
具体可以还要结合vmstat iostat进行综合分析;查看服务器是否有死锁、表锁等行为。
Hash索引
目前只有Memory存储引擎支持hash索引,hash索引有以下几点限制:
1)会存在hash冲突,可以通过链地址法解决
2)hash索引中只保存hash值和行指针,所以也就不能使用覆盖索引了,一定会需要根据行指针到磁盘中取数据
3)hash索引是无序的,不能用于sort排序操作
4)只支持等值查询
服务器宕机原因
很差的SQL语句;
磁盘空间满;
糟糕的数据库以及索引设计也有可能
主从复制
复制有基于行的复制和基于语句的复制,两者各有优缺点。
MySql内建了异步的复制功能:在主库有更新操作时会写入binlog,同时会开启一个或多个(根据备库数量而定)转储线程,备库也会开启一个IO线程和Sql线程和中继日志,其中IO线程与Sql线程是生产者与消费者模型。
对于每一个新加入的备库,主库可能都要开启一个转储线程进行同步操作;
新加入的备库与主库同步的方式:
1)冷备份,暂停主库服务同步数据,实际中不可行
2)快照 + binlog + 上次快照的偏移量
3)从其他备库同步
主库和备库都有一个类似于游标的标志位,以此来区分主从是否同步一致
SQL语句性能分析
MySql的查询优化器会为我们执行的sql语句选择最优的执行计划,可以通过explain进行分析:
explain中有两列比较关键:type 、extra
type列最好到最差:system > const > eq_ref > ref > range > index > all
extra:最常用的有以下几个值:using index > using where > using filesort using tempory
using tempory和using index 只有在group by/ order by 未利用到索引时才会用到
https://blog.csdn.net/why15732625998/article/details/80388236
数据库分库分表
https://www.cnblogs.com/butterfly100/p/9034281.html
查询年级排名第一的学生(group by用法)
注意点:
1)where条件内不可以使用聚合函数,但可以使用其他函数,聚合函数只能对分组数据有效;
2)where只可以对原始记录进行筛选,对于group by之后的数据进行筛选则只能通过having 实现,having中可以使用聚合函数;
3)结合group by使用时,select的查询条件只能是group by 的字段和聚合函数
where group by、order by 、limit 用法
大数据与算法系列之海量数据查找算法
https://blog.csdn.net/qq_36421826/article/details/80575741
MySql常见日志分类及作用
https://mp.weixin.qq.com/s/H8MyUKXXKL-eqLLatgMbag
十二、NoSql
Redis是如何保证高性能的
redis低层是使用的C语言编写的,而且数据是基于内存,并且使用了I/O多路复用的事件处理模型,是单进程单线程的服务。
Redis有安全认证功能,但是memcache就没有了。
Redis常见的对象类型
Redis常见的有字符串、列表、hash、集合、有序集合五大类。
其中字符串的低层是使用的SDS(简单动态字符串);列表的低层是使用的压缩列表或双端链表;hash的低层是使用的字典或者压缩列表;集合的低层是使用的跳跃表;有序集合的低层可能使用压缩列表或者整数
整数字符串对象类型可以实现对象的复用,而且低层的对象数据结构里记录了对象的引用计数。
Redis的应用场景
字符串:常用与缓存、计数器
hash:缓存对象数据
列表:先进先出,消息队列
集合:利用集合的交差并补,实现共同关注好友列表
有序集合:利用每个元素的优先级,实现排行榜功能
Redis使用注意事项
- 每个key一定要设置失效时间,不要使用系统自带的失效策略,而是要主动删除无效数据。
- Redis对于SDS会维持一个buffer,某些情况下可能造成内存的过度占用而不会被释放。
- 使用setex方法代替set expire方法(因为两个方法是独立的,并不是一个原子性的操作,而且操作两次的话涉及到两次网络交互)
- 尽量使用批量操作,因为redis的性能瓶颈就是在网络通信处,如使用mget() mset()
Redis常见配置项了解
https://yuandongming.blog.csdn.net/article/details/88580984
Memcached与Redis内存回收差异
memcached不会主动进行内存的回收操作,只有在get请求数据时才会判断数据是否已经失效,如果已经失效则会删除数据;
在内存不够时会使用LRU算法进行内存的回收,这个期间可能会造成有些未失效的数据被删掉,造成数据的丢失;
redis的内存回收利用了对象的定期删除 + 惰性删除两种;同时结合了max_memory选项,在内存不够时根据LRU删除数据;
Memcached应用场景
Memcached集群,server端之间并不会进行相互的通信,通信完全由你的客户端来完成,你只需在客户端规定好你的key值,然后 set进行,此时会有一个散列算法,来决定你key会存放在哪台server上。
分布式是在客户端实现的;整个的数据是放在一个大的hash表中的;key可以设置过期时间,删除策略为惰性删除,当内存空间不够时按照LRLU(最近最少使用)的原则删除数据。
Memcached没有安全认证功能,一个普通的telnet客户端就可以连接server。
常常用于页面的静态化;
Memcached主要使用于存储实时性和安全性要求不是很高的数据;由于Memcache不能提供持久化功能,一定不能将其作为主库使用;
MongoDB应用场景
mongodb支持副本集、索引、自动分片,可以保证较高的性能和可用性。
更高的写入负载
默认情况下,MongoDB更侧重高数据写入性能,而非事务安全,MongoDB很适合业务系统中有大量“低价值”数据的场景。但是应当避免在高事务安全性的系统中使用MongoDB,除非能从架构设计上保证事务安全。
高可用性
MongoDB的复副集(Master-Slave)配置非常简洁方便,此外,MongoDB可以快速响应的处理单节点故障,自动、安全的完成故障转移。这些特性使得MongoDB能在一个相对不稳定(如云主机)的环境中,保持高可用性。
数据量很大或者未来会变得很大
依赖数据库(MySQL)自身的特性,完成数据的扩展是较困难的事,在MySQL中,当一个单达表到5-10GB时会出现明显的性能降级,此时需要通过数据的水平和垂直拆分、库的拆分完成扩展,使用MySQL通常需要借助驱动层或代理层完成这类需求。而MongoDB内建了多种数据分片的特性,可以很好的适应大数据量的需求。
基于位置的数据查询
MongoDB支持二维空间索引,因此可以快速及精确的从指定位置获取数据。
表结构不明确
在一些传统RDBMS中,增加一个字段会锁住整个数据库/表,或者在执行一个重负载的请求时会明显造成其它请求的性能降级。通常发生在数据表大于1G的时候(当大于1TB时更甚)。 因MongoDB是文档型数据库,为非结构货的文档增加一个新字段是很快速的操作,并且不会影响到已有数据。另外一个好处当业务数据发生变化时,是将不在需要由DBA修改表结构。
Redis与Memcached的区别
memcache是单进程多线程的网络处理模型,低层是采用的libevent事件库来处理高并发的;支持的数据类型比较单一,而且可以进行的操作也比较少;而且不支持备份、持久化、复制等特性;
缓存和数据库双写不一致
https://blog.csdn.net/hukaijun/article/details/81010475
缓存穿透
https://blog.csdn.net/lby0307/article/details/79680326
缓存穿透:查询key在缓存和数据库中均不存在数据,会造成低层存储系统的负载加载,有可能会压垮服务,造成整个服务的宕机。
解决:
1、缓存空对象
1)这种方式对于恶意攻击而言是没有什么实际意义的,他们完全可以换一批新的key重新攻击
2)缓存空对象也会造成整个内存的占比过高,但可以通过设置较短的失效时间
3)可能会造成缓存和数据库数据的不一致性
2、使用布隆过滤器
缓存雪崩
缓存不可用,造成整个的流量都打到了存储层,造成低层存储的负载加大,会导致存储服务压力过大,出现宕机。
解决方案:
1、前期:通过架构设计保证服务的高可用性
2、发生中:限流、降级、熔断
3、发生后:缓存预热
十三、事件处理模型
select epoll poll libevent
https://www.cnblogs.com/jeakeven/p/5435916.html
十四、Linux系统
top:查看内存 CPU
sudo -iu 切换到某一个用户
w uptime:查看负载信息
iostat:查看IO
vmstat:查看CPU 内存 IO等信息
tree:打印当前目录的树状信息
说说你常用的命令
grep sed awk 三个命令都是以文本记录中的行为操作单元的,其中grep可以选择某一行, awk可以对选中的行进行截取操作, sed可以对选择的行进行前后插入、删除操作;
grep -iconav / sed -nefi / awk -F "" '';
#其中grep -o 输出匹配到的内容 ;
grep -c 可以统计匹配到的行数 ;
grep -n 输出匹配到的文本所在的行;
grep -r 递归查找
grep -v 反选;
grep -Eo '正则表达式' #结合正则表达式使用;同样有grep -P / egrep 两大种;
grep -E == egrep ;
#grep 最强大的地方是和正则表达式一块使用;
#除此之外,还有find tar mv cp mkdir rm cut sort uniq等命令
xargs命令 ps -ef | grep 'php-cgi' | cut -c 9-15 |xargs kill -9
cat access_log | awk -F " " 'print $1' | sort | uniq -c | sort -rn
shell编程
回归一下关键字即可。
if fi elif else test case esac
这篇3万字的Java后端面试总结,面试官看了瑟瑟发抖
一文整理总结常见Java后端面试题系列——Java基础篇(2022最新版)