php面试简单整理
写在前面
休息几个月,感受了下今年求职的环境到底有多糟糕,后面有时间再吐槽当前环境的各种坑吧。。。
顺手记录下自己简单整理的php面试知识点,希望对你多有帮助!
仅供参考!!!
Nginx相关
Nginx
Nginx是一个开源的”高性能代理服务器(可以处理数千个并发且迅速响应)”,采用异步非阻塞的事件驱动模型实现了高可用(高性能、低消耗、可靠稳定)。常用于Web服务器、负载均衡、反向代理以及静态资源缓存等。
Nginx重要的配置文件
1.nginx.conf:主要的Nginx配置文件,包含全局性的设置,例如进程数、工作模式等。该文件位于/etc/nginx/目录下。
2.sites-available/default:默认的虚拟主机配置文件。该文件位于/etc/nginx/sites-available/目录下。
3.sites-available/:存放了用于配置不同虚拟主机的配置文件,每个文件对应一个虚拟主机。可以在该文件夹下创建新的配置文件以配置更多的虚拟主机。
4.sites-enabled/:存放了启用的虚拟主机的配置文件的符号链接。通常使用ln -s命令将sites-available/目录下的配置文件链接到sites-enabled/目录,从而启用该虚拟主机。
5.conf.d/:该文件夹下存放了其他Nginx配置文件的目录,这些配置文件可以包含在主配置文件中。可以在该文件夹下创建新的配置文件以添加其他配置选项。
Nginx常用的命令
启动Nginx:nginx -s start
停止Nginx: nginx -s stop
重启Nginx: nginx -s restart
检查Nginx配置文件是否正确: nginx -t
打开Nginx主进程PID文件: nginx -s reopen
关闭Nginx,并在处理完当前请求后退出: nginx -s quit
重新加载配置文件:nginx -s reload
配置反向代理服务器
打开Nginx的配置文件: /etc/nginx/nginx.conf
配置反向代理:在 Nginx 配置文件中找到 http 部分,并配置反向代理:
http {
server {
listen 80;
# 域名
server_name demo.com;
# 代理的后端服务器地址
location / {
proxy_pass http://backend-server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwardeed_for;
}
}
# 后端服务器地址,可以按需添加多个
upstream backend-server {
server backend1.demo.com;
server backend2.demo.com;
}
}
检查配置: sudo nginx -t ,并重启:sudo service nginx restart,反向代理服务器配置成功!
负载均衡配置
解决问题: 在并发或服务繁忙等场景下,实现速度和稳定性(高可用)
原理:通过反向代理来实现的负载均衡
策略/算法:
- 轮询(默认)
- IP_HASH算法(保持会话,解决会话问题)
- Weighted算法(按权重比例分配服务器,解决差异性的服务器性能问题)
- URL_HASH算法(通常用于静态资源代理,配合缓存命中来使用,类似cdn)
IP/IP段控制
限制特定IP地址或IP地址段的访问,使用 Nginx 的 ngx_http_access_module 模块提供的 allow 和 deny 指令
# 注意allow, deny顺序配置
location / {
# 允许访问
allow 192.168.1.0/24;
allow 10.0.0.0/16;
# 拒绝访问
deny all;
}
HTTP与HTTPS的区别以及nginx如何支持?
区别:
1.端口:http 80, https :443
2.状态:http无状态,https是有http + ssl身份验证
3.传输方式:http明文传输,https加密传输
4.连接速度:http更快(三次握手),https 需要12个包(http的3次握手+9个ssl握手包)
支持:nginx 直接配置证书即可。
# 通过rewrite将所有HTTP请求重定向HTTPS
server {
listen 80;
server_name demo.com;
rewrite ^(.*)$ https://$server_name$1; 。
}
# ssl证书配置有关。
server {
# 端口为443。
listen 443 ssl;
# 证书绑定的域名。
server_name demo.com;
charset utf-8;
# 证书路径配置
ssl_certificate /home/you_path/demo.com.pem;
ssl_certificate_key /home/you_path/demo.com.key;
# ssl算法相关配置
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# 其他配置
...
}
静态文件缓存与压缩
缓存相关的配置项,proxy_cache_*
gzip压缩的配置项,gzip_*
nginx日志
1.配置日志: access_log,error_log
2.自定义日志格式(可配置变量参考手册),如自定义mylog格式:
# 时间、客户端IP、请求方法和URL
log_format mylog ‘$time_local $remote_addr $request_method $request_uri’;
# 定义日志使用自定义的格式
access_log /var/log/nginx/access.log mylog;
限流与优化
- 反向代理服务器,可以隐藏真实服务器的IP地址,有一定的保护作用。
- HTTP限制:使用limit_req和limit_conn,对客户端进行合理的请求限制。
- 访问限制:使用GeoIP和IP黑名单等,对恶意IP进行拦截或限制。可以基于地理位置、IP地址段等限制。
- 负载均衡:通过反向代理功能,将请求分散到多个后端服务器上,从而分摊请求压力。
- 动静分离:将动态和静态资源分开,静态可用Nginx的缓存功能缓存,减轻后端服务器压力。
- 配置防火墙:结合Nginx与防火墙工具(如iptables),对流量进行控制,只允许合法的,阻止恶意流量。
- 监控分析:监控(Nginx Stub Status模块 + 第三方工具),分析(ELK)。问题及时发现处理
PHP相关
cgi
CGI 是 nginx 和 php 通讯的协议。nginx 服务器在接受请求后,如果是静态请求(图片,文件等无需php处理的)则会直接返回给浏览器。如果是一个动态的php请求,nginx 就会通过cgi协议与 php 通信,将请求数据转换成 php 能理解的信息,php 处理完成也通过 cgi 协议返给 nginx,最后 nginx 再返回给浏览器。
fast-cgi
传统的 cgi 协议在每次连接请求时,会开启一个进程进行处理,处理完毕会关闭该进程。下次请求又重复开启与关闭。频繁的进程启动与关闭,消耗大量的资源和内存。而 fast-cgi 每次处理完请求后,不会 kill 掉这个进程,而是保留进程,使进程可以处理多次请求,不用重新 fork 一个进程,大大提高效率。
php-cgi
php-cgi 是 php 提供给 web serve 的 cgi 协议接口程序,每次请求都会开启一个 php-cgi 进程进行处理,而且开启php-cgi 会先重载配置,数据结构以及初始化运行环境,如果更新了 php 配置,那么就需要重启 php-cgi 才能生效,例如 phpstudy 就是这种情况。
php-fpm
php-fpm 是 php 提供给 web serve 的 fastcgi 协议接口程序,是 php-cgi 的一个管理程序。
php-fpm 常驻内存,会开启多个 php-cgi 进程,请求来的时候,php-fpm 将连接分配给一个 php-cgi 子进程处理,处理完毕后 php-cgi 并不会关闭,而是等待处理下一个连接,这也是 fast-cgi 加速的原理。
php-fpm 是多进程的,一个 php-cgi 大概消耗 7-25M 内存,需注意连接过多导致内存消耗过大的问题。
php-fpm支持平滑启动,如果更新了 php 配置可使用 /you_path/php-fpm reload 平滑过渡。
php-fpm优化
PHP-FPM默认使用静态进程管理模式(启动时创建固定数量的进程)
在高负载环境下,推荐使用动态进程管理模式(动态创建/销毁进程以提高性能)
# 打开配置文件
vim /etc/php-fpm.conf
# 增加进程数
pm.max_children = 50 //pm.max_children表示最大的进程数
pm.start_servers = 20 //pm.start_servers表示启动时的进程数
# 调整进程空闲时间
pm.max_spare_servers = 10 //pm.max_spare_servers表示空闲进程的最大数量
pm.min_spare_servers = 5 //pm.min_spare_servers表示空闲进程的最小数量
# 每个进程允许处理的最大请求次数。到该次数后进程重启,释放资源(大了可能会内存溢出,小了频繁重启)
pm.max_requests = 1000
# 使用动态进程管理模式(static和dynamic)
pm = dynamic
pm.max_children = 50
pm.start_servers = 20
pm.min_spare_servers = 5
pm.max_spare_servers = 10
Nginx 和 php 之间的通信
tcp socket:面向连接的协议,更好的保证通信的正确性和完整性
unix socket:不需要网络协议打包拆包,开销小效率高,但高并发时候不稳定,可能返回异常。
网络7层协议模型
应用层、表示层、会话层、传输层、网络层、(数据)链路层、物理层。快记:应表会传(物链网)
TCP 和 UDP 的特点和区别
都是属于传输层协议
TCP,面向连接,一对一,数据可靠不丢失
UDP,无连接,一对多/多对多,速度更快但不可靠
TCP 的三次握手和四次挥手
三次握手:
1. 第一次:客户端发送SYN = 1,seq = client_isn
2. 第二次:服务端发送SYN = 1,seq = server_isn,ACK =client_isn +1
3. 第三次:客户端发送SYN = 0, ACK = server_isn+1,seq =client_isn+1
四次挥手:
1. 第一次:客户端发送FIN
2. 第二次:服务端发送ACK
3. 第三次:服务端发送FIN
4. 第四次:客户端发送ACK
HTTP 状态码
1xx:请求进行中,服务器收到请求,需要请求者继续操作
2xx:成功
3xx:重定向
4xx:客户端错误
5xx:服务端错误
oop是什么?
面向对象编程是一种计算机编程架构思想,指程序由一个个能够起到子程序作用的单元或对象组成。 面向对象3大特性:继承、封装、多态。
php垃圾回收机制
引用计数方式:is_ref_gc=0 时回收
标记清除方式:解决循环引用时进行垃圾回收
什么是引用传递?
引用传递:函数内对值的任何改变在函数外部也生效(传递地址)
值传递:函数内对值的任何改变在函数外部不生效(复制值),对大型字符串和对象来说,复制操作代价很大。引用传递会有不错的性能提升(对象默认为引用传递)。
设计模式五大原则
单一职责原则:单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。
开放封闭原则:核心理念“对扩展开放,对修改封闭”。设计时要允许现有代码进行扩展,而不是通过修改现有代码来实现新的功能。这样做可以避免对现有代码造成不必要的破坏和风险,同时也可以提高代码的复用性和可维护性,从而降低开发和维护成本。
接口隔离原则:不要强迫客户使用它们不用的方法,如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。
依赖倒置原则:面向对象而不是面向过程。程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
里氏替换原则:子类可以扩展父类的功能,但不能改变父类原有的功能 。
附:迪米特原则:一个对象应该对其他对象保持最少的了解。
常见的设计模式
单例模式、工厂模式、观察者模式、外观模式、代理模式…
1.创建型模式共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
2.结构型模式共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
3.行为型模式共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
单例模式:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
public static function getInstance()
{
return new static();
}
观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并且有所作为。即出版者+订阅者=观察者模式。
工厂模式 :调用者和创建者分离,调用者直接向工厂类请求获取对象,减少代码耦合,提高系统的维护性和扩展性。
适配器模式:把对某些相似的类的操作转化为一个统一的“接口”(比喻的说法)–适配器,或者比喻为一个“界面”,统一或屏蔽了那些类的细节。适配器模式还构造了一种“机制”,使“适配”的类可以很容易的增减,而不用修改与适配器交互的代码,符合“减少代码间耦合”的设计原则。
冒泡排序/快速排序
冒泡排序是一种交换排序。对数组进行多轮冒泡,每一轮对数组中的元素两两比较,调整位置,冒出一个最大的数来。
/**
* 冒泡排序
* @param array $arr
*/
function bubbleSort(array $arr) : array
{
$length = count($arr);
// 外层循环,从数组首部开始,每完成一次循环,可确定 $arr[$i] 位置的元素
for ($i = 0; $i < $length; $i++){
// 内层循环,$j 从后往前循环
for ($j = $length - 1; $j > $i; $j--) {
// 若前面的值大于后面的值,则互换位置
if ($arr[$j] < $arr[$j - 1]) {
[$arr[$j], $arr[$j - 1]] = [$arr[$j - 1], $arr[$j]];
}
}
}
return $arr;
}
快速排序:递归算法。先选择数组的第一个元素作为标准,然后把小于或等于它和大于它的数分别放入两个数组中,对这两个数组也进行相同的处理,最后合并这两个数组和第一个元素。
/**
* 快速排序
* @param $arr
*/
function quickSort(&$arr) : void
{
$length = count($arr);
// 若数组为空,则不需要运行
if ($length <= 1) {
return;
}
$middle = $arr[0]; // 选定一个中间值
$left = []; // 接收小于中间值
$right = [];// 接收大于中间值
// 循环比较
for ($i = 1; $i < $length; $i++) {
if ($middle < $arr[$i]) {
$right[] = $arr[$i]; // 大于中间值
} else {
$left[] = $arr[$i]; // 小于或等于中间值
}
}
// 递归排序划分好的左右两边
quickSort($left);
quickSort($right);
$arr = array_merge($left, [$middle], $right);
}
单点登录
单点登录(Single Sign On, SSO)是指一次登录即可访问所有相互信任的应用系统。本质是多个应用系统共享登录状态。
实现方式一:父域 Cookie。
- Cookie 的作用域由 domain 属性和 path 属性共同决定。所以可以通过把登录状态的seesion_id 存在主域名域下(当前域的父域),所有子域名就都可以共享了。
- 此种实现方式比较简单,但不支持跨主域名。
实现方式二:认证中心。
- 部署一个认证中心,专门负责处理登录请求的独立的 Web 服务。
- 用户统一在认证中心进行登录,登录成功后,认证中心记录用户的登录状态,并将 Token 写入 Cookie。(注意这个 Cookie 是认证中心的,应用系统是访问不到的)
- 应用系统检查当前请求有没有 Token,如果没有,那么就跳转至认证中心。而认证中心通过自动携带的 Cookie 判断是否已经登录。
- 如果认证中心发现用户尚未登录,则返回登录页面,等待用户登录。如果发现用户已经登录过了,就跳转回目标 URL (目标应用系统),并携带回传一个认证中心的 Token。
- 应用系统拿到 Token 之后,后端向认证中心确认下 Token 的合法性,防止用户伪造的同时check用户登录状态。确认无误后,应用系统记录用户的登录状态,并将 Token 写入 Cookie,访问放行。(此时Cookie 是当前应用系统的)当用户再次访问当前应用系统时,就会自动带上这个 Token,应用系统验证 Token,就实现了单点登录。
- 此种实现方式相对复杂,但支持跨域,扩展性好,是单点登录的标准做法。
实现方式三:LocalStorage 跨域。
- 在这样的场景下,单点登录完全可以在前端实现。前端拿到 Session ID (或 Token )后,除了将它写入自己的 LocalStorage 中之外,还可以通过特殊手段将它写入多个其他域下的 LocalStorage 中。
- 前端将同一份 Token 写入到了多个域下的 LocalStorage 中,前端每次在向后端发送请求之前,都会主动从 LocalStorage 中读取 Token 并在请求中携带,这样就实现了同一份 Token 被多个域所共享。
- 此种实现方式完全由前端控制,几乎不需要后端参与,同样支持跨域。但扩展性不好,不利于维护。
php的堆与栈
1.heap是堆,stack是栈;
2.heap上的空间手动分配/释放(程序运行时分配的内存),stack的空间由操作系统自动分配/释放(程序执行时为函数、局部变量等分配的内存空间);
3.zendVM中的malloc函数分配的内存空间即在堆上(mm_heap)
在PHP中,Zend引擎通过内存分配器来管理堆内存的分配和释放。当PHP执行过程中需要分配更多内存时,内存分配器会从堆中分配一块足够大的空间,供变量和数据结构使用。而当某个变量或数据结构不再使用时,内存分配器会将相应的内存空间返回给堆。
php底层相关理解
通常就是问:符号表(hashTable)、zvalue结构体、zend vm的内存分配等
符号表( Symbol Table )
php 符号表( Symbol Table )是通过hashTable实现的一种数据结构,用于存储变量、函数和类等标识符的信息。使用符号表来跟踪和管理脚本中的变量、函数和类等。 符号表包括全局符号表、局部符号表等。全局符号表存储全局变量,而局部符号表用于存储函数或方法内的局部变量。
手写hashTable结构体的实现?
# 定义桶,如zval结构体
typedef struct {
char *key;
void *value;
struct Bucket *next; // 链地址法解决冲突
} Bucket;
# 定义hashTable
typedef struct {
uint32_t size; // 哈希表大小
uint32_t count; // 元素数量
Bucket **buckets; // 存储桶的数组
} HashTable;
zvalue结构体
在Zend VM中,变量的值是通过zval 结构体来表示的。zval 结构体包含一个值的实际数据(例如整数、字符串、对象等)以及一些元信息,如变量的引用计数和类型信息。
php的内存分配
1.Zend引擎内部对于变量、符号表等结构的内存分配,就是常说的堆。
2.PHP脚本运行时/执行时动态分配的内存,就是常说的栈。
php的垃圾回收
使用引用计数和标记清除(循环垃圾回收)来管理变量的内存。 引用计数:跟踪变量的引用情况,当引用计数为零时,相应的内存会被释放。
php如何实现多线程
PHP官方并没有提供多线程的功能,但提供了多进程 pcntl_fork() 的支持,可以使用多进程来做一些并行的任务执行。通常提到多线程,建议使用swoole,连接池等相关技术来提高并发性能。
php的类自动加载原理
基于注册自定义的类加载器函数(class loader)并使用 spl_autoload_register 函数。当试图使用一个未定义的类时,PHP会调用已注册的类加载器,尝试加载这个类的定义。如:Composer依赖管理工具,它通过PSR4规范,自动生成的自动加载器来管理项目中使用的所有类(无论是命名空间映射还是类文件的加载)
php的运行模式
CGI 模式(phpstudy、wnmp)
FastCGI 模式(lnmp最常见的环境)
CLI 模式(php命令行)
web模块模式( Apache )
PHP实现静态化
PHP的静态化分为:纯静态和伪静态。其中纯静态又分为:局部纯静态和全部纯静态。
PHP伪静态:利用Apache mod_rewrite,niginx rewrite 实现URL重写的方法;
PHP纯静态:生成HTML文件的方式,须开启PHP自带的缓存机制,即ob_start来开启缓存。
session和cookie
session和cookie是一种会话管理技术,通常把session_id存在cookie来匹配管理会话。
存储位置:cookie在客户端,session在服务器
存储容量:单个cookie数据<=4KB,一个站点最多保存20个Cookie; session理论没有限制(存重要信息即可)
安全性与其他:cookie在客户端不安全(占用客户端资源),session在服务器安全(占用服务器资源)
CSRF攻击,XSS攻击
CSRF(Cross-site request forgery)跨站请求伪造,黑客建立一个伪造网站或发送邮箱带了一个正常URL链接来让正常用户访问,来让用户通过自己浏览器里的COOKIE权限来执行一些非法请求
防范方法有:验证 HTTP Referer 字段、添加 token 并验证;
XSS攻击
主要将XSS代码提交存储在服务器端(数据库,内存,文件系统等),下次请求目标页面时不用再提交XSS代码。当目标用户访问该页面获取数据时,XSS代码会从服务器解析之后加载出来,返回到浏览器做正常的HTML和JS解析执行,XSS攻击就发生了。
防范方法:通过过滤是针对非法的HTML代码包括单双引号等,使用htmlspecialchars()函数
抽象类和接口分别是什么?
抽象类:就是一种特殊的类,不能被实例。可以定义方法,属性。类似于模版,规范后让子类实现详细功能。
接口:主要基于方法/对象的规范。可让某个类通过实现多个接口来形成新的类。
抽象类与接口的相同点:
1.都是用于声明某一种事物,规范名称、参数,形成模块,未有详细的实现细节。
2.都是通过类来实现相关的细节
3.语法上,抽象类的抽象方法与接口一样,不能有方法体,即{}符号
4.都可以用继承,接口继承接口形成新的接口,抽象类继承抽象类形成新的抽象类
抽象类与接口的不同点:
1.抽象类可以有属性、普通方法、抽象方法,但接口不能有属性、普通方法(可以有常量)
2.抽象类内未必有方法定义(可以都是普通方法),但接口内一定会有“方法定义”
3.抽象类用abstract关键字,class声明为类,接口用interface声明
4.抽象类的抽象方法一定要用abstract来声明,而接口不需要
5.抽象类是用extends关键字让子类继承,接口用implements实现接口
网站性能优化/高并发解决方法
- 前端优化
减少HTTP请求[将css,js等合并]
添加异步请求(非必须数据先不展示,用户触发某个事件才会异步请求数据)
CDN加速,建立独立的文件/图片服务器(减少I/O) - web服务器优化
防盗链处理(去除恶意请求)
反向代理实现负载均衡
静态资源缓存和gzip压缩
流量过滤,ip限制及黑白名单 - 应用程序优化
业务代码逻辑检查优化,sql查询优化
常驻内存swoole,opcache缓存技术,连接池技术 - 数据库优化
读写分离,负载均衡
redis缓存、数据库缓存策略
分表分区分库,数据拆分
表结构优化,索引优化
防止sql注入
验证数据,可以根据相应类型进行严格的验证。比如 int 类型直接同过 intval 进行转换;
参数化绑定,PDO参数绑定,禁止使用sql拼接;
mysql开启严格模式;
8大魔术常量
LINE:文件中 本常量所在行的 行号(即处于第几行)。
FELE:本文件的完整路径和文件名。如果被用在 被包含文件中,则返回被包含文件的文件名。本常量总是包含一个绝对路径(如果是符号链接,则是解析后的绝对路径)
DIR:本文件所在目录。如果被用在 被包含文件中,则返回被包含文件的所在目录。它等价于 dirname(FILE)。除非是根目录,否则目录名中不包含末尾的斜杠。
FUNCTION:函数名称。自PHP5起本常量返回函数被定义时的名称(区分大小写)。
CLASS:类名称。自PHP5起本常量 返回类被定义时的名称(区分大小写,包括其命名空间。如:Foo\Bar)。
TRAIT:trait 的名称。自 PHP5.4 起本常量返回 trait 被定义时的名称(区分大小写)。
METHOD:类的方法名。返回该方法被定义时的名称(区分大小写)。
NAMESPACE:当前命名空间的名称(区分大小写)。本常量是在编译时定义的。
9个超全局变量
$_GLOBALS :储存全局作用域中的变量
$_SERVER :获取服务器相关信息
$_REQUEST:获取POST和GET请求的参数
$_POST : 获取表单的POST请求参数
$_GET: 获取表单的GET请求参数
$_FILES :获取上传文件的的变量
$_ENV : 获取服务器端环境变量的数组
$_COOKIE:获取浏览器的cookie
$_SESSION : 获取session
魔术方法
__constuct构建对象的时被调用;
__destruct明确销毁对象或脚本结束时被调用;
__set当给不可访问或不存在属性赋值时被调用
__get读取不可访问或不存在属性时被调用
__isset对不可访问或不存在的属性调用isset()或empty()时被调用
__unset对不可访问或不存在的属性进行unset时被调用
__call调用不可访问或不存在的方法时被调用
__callStatic调用不可访问或不存在的静态方法时被调用
__sleep当使用serialize时被调用,当你不需要保存大对象的所有数据时很有用
__wakeup当使用unserialize时被调用,可用于做些对象的初始化操作
__clone进行对象clone时被调用,用来调整对象的克隆行为
__toString当一个类被转换成字符串时被调用
__invoke当以函数方式调用对象时被调用
__set_state当调用var_export()导出类时,此静态方法被调用。用 __set_state 的返回值做为var_export的返回值。
__debuginfo当调用var_dump()打印对象时被调用(当你不想打印所有属性)
请求的生命周期
- 构建请求
- 查找本地缓存
- 域名解析
- 与服务器建立连接(TCP的三次握手)
- 发起HTTP请求
- 服务器处理请求
- 服务器响应HTTP请求
- 客户端解析返回数据
- 与服务器断开连接(TCP的四次挥手)
网站打开慢怎么排查
- 打不开,则ping域名,看是否能请求成功。
- 慢,说明能打开,直接 free/top命令查看服务器内存和CPU使用情况,iftop等工具查看带宽
- chrome的debug->network查看响应慢的请求
- 排查响应慢的接口代码,看php,mysql,redis等的日志看错误信息(mysql的慢查询日志,php-fpm慢日志,需要配置开启)
什么是MQ?
mq 是一个消息队列,通常是解决传统的消息传输上管理困难,效率不高的问题。
组成:【生产者】 + 【消费者】 + 【消息队列和代理】
- 生产者(Producer):产生消息并发送到消息队列。
- 消费者(Consumer):从消息队列接收并处理消息。
- 消息队列(Message Queue):存储和传递消息;代理(Broker):协调消息的发送和接收。
优点:解耦,异步,削峰
缺点:增加了中间件,就提高了系统复杂度,增加了维护的成本。比如:保证消息不丢失(一致性),消息幂等性问题,mq的高可用等
系统容灾的难点与设计?
- 风险评估和业务影响分析
- 分布式/多中心部署,以及应用的高可用(如:自动监控自动扩容)
- 数据的备份与恢复,到达冗余+高可用
- 监控报警及应急措施和恢复计划
- 定期测试演练容灾方案,规范与执行使其流程记忆化
- 网络延迟和带宽,数据同步和一致性的挑战
- 不同机房/不同供应商的硬件设施及技术支撑的差异化
- 不同地区可能会有不同的法律法规约定
- 人员协同与支持,如:时间差,人员管理流程等
如何看待并解决幂等性问题
幂等性问题是指在进行多次操作时,结果与一次操作的结果相同。最简单的方法就是加入唯一标识符来校验,避免多次执行来规避产生不一致的结果。
UUID实现:时间戳+节点+随机数。
Mysql相关
数据库三大范式是什么?
第一范式:1NF是对属性的原子性,要求属性具有原子性,不可再分解;
第二范式:2NF是对记录的唯一性,要求记录有唯一标识,即实体的唯一性,即不存在部分依赖;
第三范式:3NF是对字段的冗余性,要求任何字段不能由其他字段派生出来,它要求字段没有冗余,即不存
MyISAM与InnoDB的区别
MyISAM不支持事务与外键,InnoDB 支持。
MyISAM是表锁,InnoDB行锁(提高了并发性能)。
MyISAM表是保存成独立文件的形式,在数据转移、备份MyISAM更具有优势。
AUTO_INCREMENT:MyISAM 可以和其他字段一起建立联合索引,InnoDB中必须是只有该字段的索引
MyISAM支持 FULLTEXT类型的全文索引,InnoDB不支持,但插件sphinx支持全文索引(效果更好)。
MyISAM允许没有任何索引和主键的表存在,InnoDB没有主键会选择非空唯一索引,或自动生成一个6位的uuid主键
MyISAM的SELECT性能更好,InnoDB的INSERT、UPDATE、DELETE 更好(删除全表使用truncate table)
索引区别:
聚簇索引:索引和数据存储在一个节点
非聚簇索引:索引和数据分开存储,通过索引找到数据实际存储的地址
innodb 使用的聚簇索引,主键索引为聚簇索引(没有主键索引会选择一个非空唯一索引或隐式的创建一个主键索引),辅助索引指向主键索引,然后再找到实际存储地址
myisam 使用非聚簇索引(辅助索引和主键索引一样),都只需要查询一次就能找到数据
聚簇索引的优势和略势:- 索引和数据在一起,同一页的数据会被缓存到(buffer)内存中,所以查看同一页数据的时候只需要从内存中取出
- 数据更新之后之只需要维护主键索引即可,辅助索引不受影响
- 辅助索引存的是主键索引的值,容易产生回表
- 使用隐式的uuid主键时,数据分布不均匀,导致聚簇索引可能扫全表,效率低下(强制使用自增主键id )
备注:b-tree(mongodb) 和 b+tree (mysql)的区别:
- b+tree 的数据全部存储在叶子节点,主节点只存key,一次磁盘IO能获取到更多的节点
- b+tree 增加了叶子节点到相邻节点的指针,数据是有序双向链表 ,方便返回查询范围(顺序性)
- b-tree 的主节点和叶子节点都存储key和数据,查找数据不需要找到叶子节点,主节点可以直接返回数据
事务的ACID与隔离级别
事务遵循包括原子性在内的 ACID 四大特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability);
原子性 :数据提交工作时,要么保证所有的修改都能够提交,要么就所有的修改全部回滚。
一致性:数据库从一个一致性状态变换到另一个一致性状态。
隔离性: 如果多个事务并发执行,应像各个事务独立执行一样。
持久性:一个成功执行得事务对数据库得作用是持久的,即使数据库发生故障出错,也应该能够恢复
四种隔离级别:
未提交读(read-uncommitted)所有事务都可以看到其他未提交事务的执行结果。很少用于实际应用,因为它的性能也不比其他级别好多少。会产生脏读,不可重复读以及幻读
已提交读(read-committed)这是大多数数据库系统的默认隔离级别(但不是 MySQL 默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。会产生幻读
可重复读(repeatable-read)这是 MySQL 的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,也会导致幻读 (Phantom Read)。幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的 “幻影” 行。InnoDB 存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
串行化(serializable)这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
mysql like %
在前为什么不走索引?
因为通配符 %
在开头表示匹配任意字符(包括零个字符),这使得 MySQL 难以有效地利用索引进行搜索。索引通常是根据值的前缀进行排序匹配的,而匹配任意字符时,MySQL就无法做出匹配只能全表扫描,因此无法使用索引。
mysql 分区和分表的区别?
分区(Partitioning)和分表(Sharding)都是用于优化大型数据表性能和管理的技术 。mysql自带 PARTITION BY
分区支持,而分表通常需要配合业务层来实现,更加灵活复杂。(可以把分区简单理解为:以某字段为基础的简单的分表)
MyISAM 表锁
1.共享读锁(S)之间是兼容的,但共享读锁(S)和排他写锁(X)之间,以及排他写锁之间(X)是互斥的,也就是说读和写是串行的。
# 共享锁(就是读锁,针对同一份数据,多个读操作可以同时进行而不会互相影响(select))
select * from tableName lock in share mode; //读锁
# 排他锁(就是写锁,当前操作没完成之前,会阻塞其它读和写操作(update、insert、delete))
select * from tableName for update; //写锁
2.在一定条件下,MyISAM 允许查询和插入并发执行,利用这一点来解决应用中对同一表和插入的锁竞争问题。
3.MyISAM 默认锁调度机制是写优先,不一定适合所有应用,可以通过设置 LOW_PRIPORITY_UPDATES 参数,或在 INSERT、UPDATE、DELETE 语句中指定 LOW_PRIORITY 选项来调节读写锁的争用。
4.由于表锁的锁定粒度大,读写之间又是串行的,因此,如果更新操作较多,MyISAM 表可能会出现严重的锁等待,可以考虑采用 InnoDB 表来减少锁冲突。
InnoDB 表
1.InnoDB 的行锁是基于索引实现的,如果不通过索引访问数据,InnoDB 会使用表锁。
2.在不同的隔离级别下,InnoDB 的锁机制和一致性读策略不同。
3.MySQL 的恢复和复制对 InnoDB 锁机制和一致性读策略也有较大影响。
4.注意死锁,锁冲突甚至死锁很难完全避免。
减少锁冲突和死锁
1.尽量使用较低的隔离级别
2.精心设计索引,尽量使用索引访问数据,使加锁更精确,从而减少锁冲突的机会。
3.选择合理的事务大小,小事务发生锁冲突的几率也更小。
4.给记录集显示加锁时,最好一次性请求足够级别的锁。比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时再请求排他锁,这样容易产生死锁。
5.不同程序访问一组表时,应尽量约定以相同的顺序访问,这样可以减少死锁的机会。
6.尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响。
7.不要申请超过实际需要的锁级别;除非必须,查询时不要显示加锁。
分表 (分库) 的策略
根据业务数据量和业务复杂度来拆分。
业务拆分可参考:取余、hash、range范围;
表结构拆分:垂直、水平;
binlog日志
通常用与:数据恢复、主从复制
可设置格式: statement 、 row 、 mixed(通常使用 row 或者 mixed )
主从同步
用于: 实现读写分离、数据备份高可用、负载均衡提高性能、版本升级测试
流程:
1. 从节点开启start slave 命令之后,创建一个IO进程连接到主节点
2. 连接成功之后,主节点创建一个 log dump线程(主节点会为每一个从节点创一个log dump线程)
3. 当binlog发生变化时,主节点的dump log线程会读取bin-log内容并发送给从节点
4. 主节点dump log 线程读取bin-log 的内容时会对主节点的bin-log加锁,读取完成在发送给从节点之前释放锁
5. 从节点的IO线程接收主节点发送的binlog内容,并将其写入本地relay log 文件中
6. 主从节点通过binlog文件+position偏移量定位主从同步的位置,从节点会保存接收到的position偏移量,如果从节点发生宕机重启,自动从postion位置发起同步
7. 从节点的SQL线程复制读取本地relay log的内容,解析成具体的操作并执行,保证主从数据一致性;
模式: 异步模式(默认方式) 、 全同步模式 、 半同步模式 (几乎不用)
limit 优化
1.主键阈值法: 通过条件推算出符合条件的主键最大值&最小值来避免
2.配合es来分页
redis 缓存和 mysql 数据一致性
1.先更新redis 再更新数据库
2.先更新数据库,再更新redis
3.先删除缓存再更新数据库
问题:以上在并发同时请求时候都有可能出现数据不一致的情况。
解决:1. 延时双删除;2.串行获取(队列化),如:查询队列+更新队列
A字段分组且B字段最大的数据?
# 表结构:name,type,age
select * from users where (type, age) in (select type, max(age) from users group by type)
order by age desc;
# join操作
SELECT u.* FROM users u
JOIN (
SELECT type, Max(age) AS max_age FROM users GROUP BY type
) t
ON u.type = t.type AND u.age = t.max_age
order by age desc;
字段值置换更新
对字段值置换更新,如:字段status 为0、1,需把1更新为0,0更新为1。
# 数学置换 x = 0 + 1
update `table` set status = 1 - status where 1=1;
# 语法 case when then
UPDATE users
SET type = CASE
WHEN type = 1 THEN 2
WHEN type = 2 THEN 1
ELSE type
END
WHERE 1=1;
Redis相关
redis 数据结构
redis数据结构类型:String、List、Set、 Hash、ZSet
redis 为什么快
Redis 是基于内存的单线程/多路IO复用模型的 KV 数据库, 由 C 语言编写,具有很高的吞吐量,官方 QPS 100000+。
1. 基于内存的设计,内存的操作不会影响 Redis 的性能瓶颈。Redis 的数据结构简单,对数据操作也简单,且绝大部分请求是纯粹的内存操作,查找和操作的时间复杂度都是 O (1);
2. 单线程:避免了上下文切换和竞争,也不存在多进程/线程的切换消耗CPU,不存在各种锁的问题,也就不可能因锁而导致性能消耗;
3. 多路IO复用模型:多路 I/O 复用模型(非阻塞 IO)是利用 select、poll、epoll 同时监察多个流的 I/O 事件。空闲时阻塞当前线程,有 I/O 事件时就从阻塞态中唤醒,轮询一遍所有的流(epoll 只轮询那些真正发出了事件的流),依次顺序处理就绪的流,避免大量无用操作。“多路” 指多个网络连接,“复用” 指复用同一个线程,极致的减少网络 IO 消耗。
redis 的持久化
RDB 快照(snapshot)(每隔一段时间写入快照的方式)
AOF( 将每一个收到的写命令都通过 write 函数追加到 appendonly.aof 文件),通过追加记录 Redis 服务器所执行的写命令来实现。
Redis 内存满了的解决方法
增加内存,内存淘汰策略,搭集群
缓存穿透,雪崩,预热/更新
缓存穿透:是指用户查询数据库没有的数据,缓存中也不会有。缓存中找不到,都要去数据库再查一遍,然后返回空(进行了两次无用的查询)。绕过缓存直接查数据库,也是缓存命中率的问题。 解决:拦截查询或空结果缓存
缓存雪崩:同一时间大量缓存失效,从而数据库查询压力暴增导致服务不可用或宕机。 解决:缓存过期策略、做多级缓存、缓存标记等等,总之就是避免同一时间出现大量缓存数据过期;
预热/更新:手动或定时对缓存数据进行清理、加载或更新操作。
redis主从同步过程是如何实现的
主节点负责处理写操作,而从节点负责复制主节点的数据并处理读操作。一般就是指redis的哨兵模式。
redis主从哨兵和集群的区别
一、架构不同
redis主从:一主多从;
redis集群:多主多从;
二、存储不同
redis主从:主节点和从节点都是存储所有数据;
redis集群:数据的存储是通过hash计算16384的槽位,算出要将数据存储的节点,然后进行存储;
三、选举不同
redis主从:通过启动redis自带的哨兵(sentinel)集群进行选举,也可以是一个哨兵
选举流程:先发现主节点fail的哨兵,将成为哨兵中的leader,之后的主节点选举将通过这个leader进行故障转移操作,从存活的slave中选举新的master,新的master选举同集群的master节点选举类似;
redis集群:集群可以自己进行选举
选举流程:
1. 当主节点挂掉,从节点就会广播该主节点fail;
2. 延迟时间后进行选举(延迟的时间算法为:延迟时间+随机数+rank*1000,从节点数据越多,rank越小,因为主从数据复制是异步进行的,所以 所有的从节点的数据可能会不同),延迟的原因是等待主节点fail广播到所有存活的主节点,否则主节点会拒绝参加选举;
3. 参加选举的从节点向所有的存活的节点发送ack请求,但只有主节点会回复它,并且主节点只会回复第一个到达参加选举的从节点,一半以上的主节点回复,该节点就会成为主节点,广播告诉其他节点该节点成为主节点。
四、节点扩容不同
- redis主从:只能扩容从节点,无法对主节点进行扩容;
- redis集群:可以扩容整个主从节点,但是扩容后需要进行槽位的分片,否则无法进行数据写入,命令为:
# 其中的192.168.0.61:8001为新加入的主从节点; /usr/local/redis-5.0.3/src/redis-cli -a zhuge --cluster reshard 192.168.0.61:8001
MongoDB相关
mongodb是什么?
MongoDB 是由 C++语言编写,是一个基于分布式文件存储的开源NoSQL数据库(文档数据库)。 再高负载的情况下,添加更多的节点,可以保证服务器性能。 旨在提供可扩展的高性能数据存储解决方案。
mongodb有哪些特点?
- MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。
- 可以在 MongoDB 记录中设置任何属性的索引(如:FirstName=”Sameer”,Address=”8 Gandhi Road”)实现更快排序。
- 可以通过本地或者网络创建数据镜像,这使得 MongoDB 有更强的扩展性。
- 负载的增加(更多的存储空间和更强的处理能力),它可以分布在网络中的其他节点上这就是所谓的分片。
- MongoDb 支持丰富的查询表达式。查询指令使用 JSON 形式的标记,可轻易查询文档中内嵌的对象及数组。
- MongoDb 使用 update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。
- Mongodb 中的 Map/reduce 主要是用来对数据进行批量处理和聚合操作。
- Map 和 Reduce,Map 函数调用 emit(key,value)遍历集合中的所有记录,将key与value传给Reduce函数处理。
- Map和Reduce函数是用Javascript编写的,可以通过db.runCommand或mapreduce命令来执行MapReduce操作。
- GridFS 是 MongoDB 中的一个内置功能,可以用于存放大量小文件。
- MongoDB 允许在服务端执行脚本, 可以用 Javascript 编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
MySQL与MongoDB之间最基本的差别是什么?
MySQL和MongoDB两者都是免费开源的数据库。MySQL和MongoDB有许多基本差别包括数据的表示(data representation),查询,关系,事务,schema的设计和定义,标准化(normalization),速度和性能。通过比较MySQL和MongoDB,实际上我们是在比较关系型和非关系型数据库(数据存储结构不同)。
MongoDB的特点/为啥是最好的Nosql数据库?
面向文件的、高可用(稳定可靠性能)、易扩展性、丰富的查询语言
MongoDB的集合collection
集合就是一组 MongoDB 文档。它相当于关系型数据库中的表概念。集合位于单独的一个数据库中。一个集合内的多个文档可以有多个不同的字段。一般来说,集合中的文档都有着相同或相关的目的。
MongoDB的文档Document
文档由一组key value组成(json)。是动态模式:同一集合里的文档可能是不同的字段和结构。文档相当于关系型数据库中table的一条记录。
什么是mongod?
mongod是处理MongoDB系统的主要进程。它处理数据请求,管理数据存储,和执行后台管理操作。当我们运行mongod命令意味着正在启动MongoDB进程,并且在后台运行。
默认端口: “27017”、默认存储路径:”/data/db”
MongoDB中的副本集
在MongoDB中副本集由一组MongoDB实例组成,包括一个主节点多个次节点,MongoDB客户端的所有数据都写入主节点(Primary),副节点从主节点同步写入数据,以保持所有复制集内存储相同的数据,提高数据可用性。
Elasticsearch相关
es简介
一个高扩展、生态化、开源的全文检索引擎,它可以准实时地快速存储、搜索、分析海量的数据。全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置。当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户。
ES的索引结构
Elasticsearch 中的分词索引采用倒排索引的底层结构。倒排索引(Inverted Index)是一种用于支持全文搜索的索引结构,它将文档中的词汇映射到文档的位置,简单记做:【词item】——— 【文档ID】;在索引结构上还存储了词汇频率,位置等相关信息,可根据出现的频率位置信息来得到一个评分用以检索结果的排序。
es查询流程
1.客户端请求发给某个节点
2.节点转发给每个分片,查询每个分片上的前n条
3.结果返回给节点,合并整合数据,提取前n条
4.最后返回给请求客户端
ES 为什么比 mysql 快
es索引存于内存之中,采用倒序索引,结构为”分词<=>id”的方式,能通过分词快速定位文档(分词索引 vs b+tree)
本作品采用《CC 协议》,转载必须注明作者和本文链接
现在休息都是按半年起步,几个月不够哦 :grin:
招聘往boss一挂,简历不断
谢谢分享 正好需要
谢谢分享 正好需要
老哥牛逼,加个好友呗
今年的就业情况,一言难尽
单点登出呢 有好的 方案么
请问老哥php多少年了