一些关于phper的面试题汇总整理

Nginx相关

简介

Nginx 是一个开源的“高性能代理服务器 (可以处理数千个并发且迅速响应)“,采用异步非阻塞的事件驱动模型实现了高可用(高性能、低消耗、可靠稳定)。常用于 Web 服务器、负载均衡、反向代理以及静态资源缓存等。

重要的配置文件

①、nginx.conf:主要的 Nginx 配置文件,包含全局性的设置,例如进程数、工作模式等。该文件位于 /etc/nginx/ 目录下。

②、sites-available/default:默认的虚拟主机配置文件。该文件位于 /etc/nginx/sites-available/ 目录下。

③、sites-available/:存放了用于配置不同虚拟主机的配置文件,每个文件对应一个虚拟主机。可以在该文件夹下创建新的配置文件以配置更多的虚拟主机。

④、sites-enabled/:存放了启用的虚拟主机的配置文件的符号链接。通常使用 ln -s 命令将 sites-available / 目录下的配置文件链接到 sites-enabled / 目录,从而启用该虚拟主机。

⑤、conf.d/:该文件夹下存放了其他 Nginx 配置文件的目录,这些配置文件可以包含在主配置文件中。可以在该文件夹下创建新的配置文件以添加其他配置选项。

常用的命令

①、停止 Nginx: nginx -s stop

②、重启 Nginx: nginx -s restart

③、检查 Nginx 配置文件是否正确: nginx -t

④、关闭 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;
    }
}

检查nginx中的配置是否有错误:nginx -t ,并重新加载nginx的配置文件:nginx -s reload,反向代理服务器配置成功!

负载均衡配置

解决问题: 在并发或服务繁忙等场景下,实现速度和稳定性(高可用)

原理:通过反向代理来实现的负载均衡


负载均衡的 策略 / 算法 如下:

①、轮询(默认)

②、IP_HASH 算法(保持会话,解决会话问题)

③、Weighted 算法(按权重比例分配服务器,解决差异性的服务器性能问题)

④、URL_HASH 算法(通常用于静态资源代理,配合缓存命中来使用,类似 cdn)

IP/IP 段控制

限制特定 IP 地址或 IP 地址段的访问,使用 Nginx 的 ngx_http_access_module 模块提供的 allow 和 deny 指令。

具体配置示例参考:nginx配置系列(二)访问控制(ngx_http_access_module)

HTTP 与 HTTPS 的区别以及 nginx 如何支持?

区别:

①、端口:http 80, https :443

②、状态:http 无状态,https 是有 http + ssl 身份验证

③、传输方式:http 明文传输,https 加密传输

④、连接速度:http 更快(三次握手),https 需要 12 个包(http 的 3 次握手 + 9 个 ssl 握手包)

nginx配置https配置示例参考:nginx开启https配置

静态文件缓存与压缩

①、缓存相关的配置项,proxy_cache_*

②、gzip 压缩的配置项,gzip_*

nginx日志

①、配置日志: access_log,error_log

②、自定义日志格式(可配置变量参考手册),如自定义 testnginxlog格式:

log_format testnginxlog '$time_local $remote_addr $request_method $request_uri';
	
#定义日志使用自定义的格式
access_log /var/log/nginx/access.log testnginxlog;

限流与优化

①、反向代理服务器,可以隐藏真实服务器的 IP 地址,有一定的保护作用。

②、HTTP 限制:使用 limit_req 和 limit_conn,对客户端进行合理的请求限制。

③、访问限制:使用 GeoIP 和 IP 黑名单等,对恶意 IP 进行拦截或限制。可以基于地理位置、IP 地址段等限制。

④、负载均衡:通过反向代理功能,将请求分散到多个后端服务器上,从而分摊请求压力。

⑤、动静分离:将动态和静态资源分开,静态可用 Nginx 的缓存功能缓存,减轻后端服务器压力。

⑥、配置防火墙:结合 Nginx 与防火墙工具(如 iptables),对流量进行控制,只允许合法的,阻止恶意流量。

⑦、监控分析:监控(Nginx Stub Status 模块 + 第三方工具),分析(ELK)。问题及时发现处理

PHP相关

cgi

cgi(通用网关接口)是一个标准,它定义了web服务器与外部程序之间的通信接口,允许服务器将客户端请求转发给外部程序进行处理,并将处理结果返回给客户端。


用在php和nginx之间的话那就是:cgi 是 nginx 和 php 通讯的协议。nginx 服务器在接受请求后,如果是静态请求(图片,文件等无需 php 处理的)则会直接返回给浏览器。如果是一个动态的 php 请求,nginx 就会通过 cgi 协议与 php 通信,将请求数据转换成 php 能理解的信息,php 处理完成也通过 cgi 协议返给 nginx,最后 nginx 再返回给浏览器。


注意:任何支持cgi协议的web server服务器都可以和php进行数据通讯,不仅仅是上面说的nginx。因为cgi协议定义的是一个标准接口,只要满足这个接口规定的数据输入输出格式,就可以进行通讯。在php和nginx当中 CGI其实相当于是php和nginx之间进行沟通的一座桥梁,php和nginx进行数据交互通讯的话 需要借助于 这座桥梁(nginx) 来实现数据交互通讯。



fast-cgi

传统的 cgi 协议在每次连接请求时,会开启一个进程进行处理,处理完毕会关闭该进程。下次请求又重复开启与关闭。频繁的进程启动与关闭,消耗大量的资源和内存。而 fast-cgi 每次处理完请求后,不会 kill 掉这个进程,而是保留进程,使进程可以处理多次请求,不用重新 fork 一个进程,大大提高效率。


注意:fast-cgi由于使用的是多进程模型,因为它能更有效地处理并发请求。不过,由于fast-cgi是多进程的,so 他会消耗更多的服务器内存。



php-cgi

php-cgi 是 php 提供给 web serve 的 cgi 协议接口程序,每次请求都会开启一个 php-cgi 进程进行处理,而且开启 php-cgi 会先重载配置,数据结构以及初始化运行环境,如果更新了 php 配置,那么就需要重启 php-cgi 才能生效。

所以 这也就是为什么 每次改了php的配置文件之后 我们都需要重启web server的原因 就是这种原因。(这种时候 php的运行模式 应是非cli模式下运行的。注意 并不是说 php在cli命令行模式下运行php脚本后,如果更改了php的配置文件就不需要重启php进程了,这个不是绝对的,但是大多数情况下 不需要重启php的! 只需要重新在cli模式下运行php脚本文件即可使更改的配置文件生效)



php-fpm

①、php-fpm(FastCGI Process Manager)是一个PHP fast-cgi管理器,它专门用于管理php-cgi进程。与php-cgi不同,php-fpm是一个进程管理器,而php-cgi只是一个解释器。这意味着php-fpm可以管理多个php-cgi进程,而不仅仅是一个。此外,当使用FastCGI协议时,php-cgi的启动不受Web Server的控制,而是直接受php-fpm的调度。

②、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 平滑过渡。



cgi、fast-cgi、php-cgi、php-fpm之间的关系:cgi是一个通信标准,php-cgi是php语言的cgi协议的具体实现,fast-cgi是cgi的进阶版本并使用多进程模型,而php-fpm是一个专门用于管理php-cgi进程的管理器。在搭建高性能的服务器时,理解这些概念之间的关系是非常重要的。

php-fpm优化

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的三次握手和四次挥手

三次握手:

第一次:客户端发送 SYN = 1,seq = client_isn

第二次:服务端发送 SYN = 1,seq = server_isn,ACK =client_isn +1

第三次:客户端发送 SYN = 0, ACK = server_isn+1,seq =client_isn+1


四次挥手:

第一次:客户端发送 FIN

第二次:服务端发送 ACK

第三次:服务端发送 FIN

第四次:客户端发送 ACK

HTTP 状态码

1xx:请求进行中,服务器收到请求,需要请求者继续操作

2xx:成功

3xx:重定向

4xx:客户端错误

5xx:服务端错误

oop 是什么?

面向对象编程是一种计算机编程架构思想,指程序由一个个能够起到子程序作用的单元或对象组成。 面向对象 3 大特性:继承、封装、多态。

php 垃圾回收机制

“引用计数方式(is_ref_gc=0 时回收)” + “标记清除方式(解决循环引用时回收)”来进行垃圾回收。

什么是引用传递?

引用传递:函数内对值的任何改变在函数外部也生效(传递地址)

值传递:函数内对值的任何改变在函数外部不生效(复制值) 对大型字符串和对象来说,复制操作代价很大。引用传递会有不错的性能提升(对象默认为引用传递)。

设计模式五大原则

①、单一职责原则:单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。


②、开放封闭原则:核心理念 “对扩展开放,对修改封闭”。设计时要允许现有代码进行扩展,而不是通过修改现有代码来实现新的功能。这样做可以避免对现有代码造成不必要的破坏和风险,同时也可以提高代码的复用性和可维护性,从而降低开发和维护成本。


③、接口隔离原则:不要强迫客户使用它们不用的方法,如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。


④、依赖倒置原则:面向对象而不是面向过程。程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。


⑤、里氏替换原则:子类可以扩展父类的功能,但不能改变父类原有的功能。


附:迪米特原则:一个对象应该对其他对象保持最少的了解。

常见的设计模式

单例模式、工厂模式、策略模式、观察者模式、外观模式、代理模式…...


①、创建型模式共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

②、结构型模式共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

③、行为型模式共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。


单例模式:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

public static function getInstance()
{
    return new static();
}

观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并且有所作为。即出版者 + 订阅者 = 观察者模式。


工厂模式 :调用者和创建者分离,调用者直接向工厂类请求获取对象,减少代码耦合,提高系统的维护性和扩展性。


适配器模式:把对某些相似的类的操作转化为一个统一的 “接口”(比喻的说法)–适配器,或者比喻为一个 “界面”,统一或屏蔽了那些类的细节。适配器模式还构造了一种 “机制”,使 “适配” 的类可以很容易的增减,而不用修改与适配器交互的代码,符合 “减少代码间耦合” 的设计原则。

冒泡排序 / 快速排序

冒泡排序:是一种交换排序。对数组进行多轮冒泡,每一轮对数组中的元素两两比较,调整位置,冒出一个最大的数来。

/**
 * 冒泡排序
 * @param array $arr
 */
function bubbleSort(array $arr)
{
    $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)
{
    $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 的堆与栈

①、heap 是堆,stack 是栈。

②、heap 上的空间手动分配 / 释放(程序运行时分配的内存),stack 的空间由操作系统自动分配 / 释放(程序执行时为函数、局部变量等分配的内存空间)。

③、zendVM 中的 malloc 函数分配的内存空间即在堆上 (mm_heap)

在 PHP 中,Zend 引擎通过内存分配器来管理堆内存的分配和释放。当 PHP 执行过程中需要分配更多内存时,内存分配器会从堆中分配一块足够大的空间,供变量和数据结构使用。而当某个变量或数据结构不再使用时,内存分配器会将相应的内存空间返回给堆。

php 底层相关理解

通常就是问:符号表(hashTable)、zvalue 结构体、zend vm 的内存分配等。

php的几种运行模式

①、CGI 模式(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()函数

抽象类和接口分别是什么?

抽象类:就是一种特殊的类,不能被实例。可以定义方法,属性。类似于模版,规范后让子类去实现具体的详细功能。

接口:主要基于方法 / 对象的规范。可以让某个类通过实现多个接口来形成新的类。


抽象类与接口的相同点:

①、都是用于声明某一种事物,规范名称、参数,形成模块,未有详细的实现细节。

②、都是通过类来实现相关的细节。

③、语法上,抽象类的抽象方法与接口一样,不能有方法体,即{}符号。

④、都可以用继承,接口继承接口形成新的接口,抽象类继承抽象类形成新的抽象类。


抽象类与接口的不同点:

①、抽象类可以有属性、普通方法、抽象方法,但接口不能有属性、普通方法(可以有常量)。

②、抽象类内未必有方法定义(可以都是普通方法),但接口内一定会有 “方法定义”。

③、抽象类用 abstract 关键字,class 声明为类,接口用 interface 声明。

④、抽象类的抽象方法一定要用 abstract 来声明,而接口不需要。

⑤、抽象类是用 extends 关键字让子类继承,接口用 implements 关键字来实现接口

网站性能优化 / 高并发解决方法

前端优化

①、减少 HTTP 请求 [将 css、js 等合并]

②、添加异步请求 (非必须数据先不展示,用户触发某个事件才会异步请求数据)

③、CDN 加速,建立独立的文件 / 图片服务器 (减少 I/O)


web 服务器优化

①、防盗链处理 (去除恶意请求)

②、反向代理实现负载均衡

③、静态资源缓存和 gzip 压缩

④、流量过滤,ip限制及黑白名单


应用程序优化

①、业务代码逻辑检查优化,sql 查询优化

②、常驻内存 swoole,opcache 缓存技术,连接池技术


数据库优化

①、读写分离,负载均衡

②、redis 缓存、数据库缓存策略

③、分表分区分库,数据拆分

④、表结构优化,索引优化

防止 sql 注入

①、验证数据,可以根据相应类型进行严格的验证。比如 int 类型直接同过 intval 进行转换。

②、参数化绑定,PDO 参数绑定。

③、禁止使用 sql 拼接。

④、mysql 开启严格模式。

大魔术常量

①、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

php中的魔术方法

__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 () 打印对象时被调用(当你不想打印所有属性)。

一次http请求的生命周期

构建请求->查找本地缓存->域名解析->与服务器建立连接(TCP 的三次握手)->发起 HTTP 请求->服务器处理请求->服务器响应 HTTP 请求->客户端解析返回数据->与服务器断开连接(TCP 的四次挥手)

网站打开慢怎么排查?

①、打不开,则 ping 域名,看是否能请求成功。

②、慢,说明能打开,直接 free/top 命令查看服务器内存和 CPU 使用情况,iftop 等工具查看带宽

③、浏览器的network 查看响应慢的请求

④、排查响应慢的接口代码,看 php,mysql,redis 等的日志看错误信息(mysql 的慢查询日志,php-fpm 慢日志,需要配置开启)

什么是 MQ?

mq 是一个消息队列,通常是解决传统的消息传输上管理困难,效率不高的问题。

mq 有三大优点:解耦,异步,削峰。

缺点: 增加了中间件,就提高了系统复杂度,增加了维护的成本。比如:要保证消息不丢失 (一致性) 和消息幂等性问题,还要保证 mq 的高可用等。

MySQL相关

数据库三大范式是什么?

第一范式:1NF 要求每个数据表的每个属性都是原子性的,即 不能包含多个值或重复的数据。

第二范式:要求在满足第一范式的基础上,非主键属性必须完全依赖于候选键(候选键是能够唯一标识一条记录的属性或属性组合)。如果存在部分依赖,需要将其独立出来作为一个新的表。

第三范式:要求在满足第二范式的基础上,非主键属性不能相互依赖,即不能存在传递依赖。如果存在传递依赖,需要将其独立出来作为一个新的表。


总结:第一范式(1NF)要求原子性,第二范式(2NF)要求非主键属性完全依赖于候选键,第三范式(3NF)要求消除非主键属性之间的传递依赖。这三个范式是在数据库设计中用于规范化数据结构的原则。

MyISAM存储引擎 与 InnoDB 存储引擎的区别

①、MyISAM 不支持事务与外键,InnoDB 支持。

②、MyISAM 是表锁,InnoDB 行锁 (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)这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

MyISAM表锁

①、共享读锁(S)之间是兼容的,但共享读锁(S)和排他写锁(X)之间,以及排他写锁之间(X)是互斥的,也就是说读和写是串行的。

②、在一定条件下,MyISAM 允许查询和插入并发执行,利用这一点来解决应用中对同一表和插入的锁竞争问题。

③、MyISAM 默认锁调度机制是写优先,不一定适合所有应用,可以通过设置 LOW_PRIPORITY_UPDATES 参数,或在 INSERT、UPDATE、DELETE 语句中指定 LOW_PRIORITY 选项来调节读写锁的争用。

④、由于表锁的锁定粒度大,读写之间又是串行的,因此,如果更新操作较多,MyISAM 表可能会出现严重的锁等待,可以考虑采用 InnoDB 表来减少锁冲突。

InnoDB表

①、InnoDB 的行锁是基于索引实现的,如果不通过索引访问数据,InnoDB 会使用表锁。

②、在不同的隔离级别下,InnoDB 的锁机制和一致性读策略不同。

③、MySQL 的恢复和复制对 InnoDB 锁机制和一致性读策略也有较大影响。

④、注意死锁,锁冲突甚至死锁很难完全避免。

减少锁冲突和死锁

①、尽量使用较低的隔离级别。

②、精心设计索引,尽量使用索引访问数据,使加锁更精确,从而减少锁冲突的机会。

③、选择合理的事务大小,小事务发生锁冲突的几率也更小。

④、给记录集显示加锁时,最好一次性请求足够级别的锁。比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时再请求排他锁,这样容易产生死锁。

⑤、不同程序访问一组表时,应尽量约定以相同的顺序访问,这样可以减少死锁的机会。

⑥、尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响。

⑦、不要申请超过实际需要的锁级别;除非必须,查询时不要显示加锁。

分表 (分库) 的策略

根据业务数据量和业务复杂度来拆分。

业务拆分可参考:取余、hash、range 范围。

表结构拆分:垂直、水平。

binlog日志

通常用与:数据恢复、主从复制

可设置格式: statement 、 row 、 mixed(通常使用 row 或者 mixed )

主从同步

可用于: 实现读写分离 、 数据备份高可用、负载均衡提高性能、版本升级测试


流程:

①、从节点开启 start slave 命令之后,创建一个 IO 进程连接到主节点。

②、连接成功之后,主节点创建一个 log dump 线程 (主节点会为每一个从节点创一个 log dump 线程)。

③、当 binlog 发生变化时,主节点的 dump log 线程会读取 bin-log 内容并发送给从节点。

④、主节点 dump log 线程读取 bin-log 的内容时会对主节点的 bin-log 加锁,读取完成在发送给从节点之前释放锁。

⑤、从节点的 IO 线程接收主节点发送的 binlog 内容,并将其写入本地 relay log 文件中。

⑥、主从节点通过 binlog 文件 + position 偏移量定位主从同步的位置,从节点会保存接收到的 position 偏移量,如果从节点发生宕机重启,自动从 postion 位置发起同步。

⑦、从节点的 SQL 线程复制读取本地 relay log 的内容,解析成具体的操作并执行,保证主从数据一致性。


模式: 异步模式(默认方式) 、 全同步模式 、 半同步模式 (几乎不用)

limit优化

①、主键阈值法: 通过条件推算出符合条件的主键最大值 & 最小值来避免

②、配合 es 来分页

redis 缓存 和 mysql 数据保持一致性

①、先更新 redis 再更新数据库。

②、先更新数据库,再更新redis。

③、先删除缓存再更新数据库。


问题:以上解决方案在遇到并发同时请求时,都有可能出现数据不一致的情况。

解决: 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的数据结构

常见的数据结构类型有:String、List、Hash、Set、ZSet

redis为什么快?

Redis采用的是基于内存的单线程 / 多路 IO 复用模型的 KV 数据库, 由 C 语言编写,官方数据 QPS 达到 100000+。

完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,查找和操作的时间复杂度都是 O (1); Redis 中的数据结构简单,对数据操作也简单; 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或多线程的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,因为没有可能出现死锁而导致的性能消耗; 使用多路 I/O 复用模型,非阻塞 IO;多路 I/O 复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力。在空闲时,会阻塞当前线程,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。 这里 “多路” 指的是多个网络连接,“复用” 指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存的操作不会成为影响 Redis 性能的瓶颈, 所以造就了 Redis 具有很高的吞吐量。

redis的持久化

RDB 快照(每隔一段时间写入快照的方式)

AOF( 将每一个收到的写命令都通过 write 函数追加到 appendonly.aof 文件)

redis内存满了的解决方法

增加内存,内存淘汰策略,搭集群

缓存穿透,雪崩,预热 / 更新

①、缓存穿透:是指用户查询数据库没有的数据,缓存中也不会有。缓存中找不到,都要去数据库再查一遍,然后返回空(进行了两次无用的查询)。绕过缓存直接查数据库,也是缓存命中率的问题。 

解决:拦截查询或空结果缓存


②、缓存雪崩:同一时间大量缓存失效,从而数据库查询压力暴增导致服务不可用或宕机。 

解决:缓存过期策略、做多级缓存、缓存标记等等,总之就是避免同一时间出现大量缓存数据过期。


③、预热 / 更新:手动或定时对缓存数据进行清理、加载或更新操作。

redis的主从哨兵和集群的区别?

一、架构不同

redis主从:一主多从。

redis集群:多主多从。


二、存储不同

redis主从:主节点和从节点都是存储所有数据。

redis集群:数据的存储是通过一定的hash算法 计算16384 的槽位,算出要将数据存储的节点,然后进行存储。


三、选举不同

redis主从:通过启动 redis 自带的哨兵(sentinel)集群进行选举,也可以是一个哨兵。

选举流程:先发现主节点 fail 的哨兵,将成为哨兵中的 leader,之后的主节点选举将通过这个 leader 进行故障转移操作,从存活的 slave 中选举新的 master,新的 master 选举同集群的 master 节点选举类似。


redis集群:集群可以自己进行选举。

选举流程:

①、当主节点挂掉,从节点就会广播该主节点 fail。

②、延迟时间后进行选举(延迟的时间算法为:延迟时间 + 随机数 + rank*1000,从节点数据越多,rank 越小,因为主从数据复制是异步进行的,所以  所有的从节点的数据可能会不同),延迟的原因是等待主节点 fail 广播到所有存活的主节点,否则主节点会拒绝参加选举。

③、参加选举的从节点向所有的存活的节点发送 ack 请求,但只有主节点会回复它,并且主节点只会回复第一个到达参加选举的从节点,一半以上的主节点回复,该节点就会成为主节点,广播告诉其他节点该节点成为主节点。


四、节点扩容不同

redis主从:只能扩容从节点,无法对主节点进行扩容。

redis集群:可以扩容整个主从节点,但是扩容后需要进行槽位的分片,否则无法进行数据写入。

ElasticSearch相关

es简介

一个高扩展、生态化、开源的全文检索引擎,它可以准实时地快速存储、搜索、分析海量的数据。全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置。当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户。

es查询流程

①、客户端请求发给某个节点

②、节点转发给每个分片,查询每个分片上的前 n 条

③、结果返回给节点,合并整合数据,提取前 n 条

④、最后返回给请求客户端

es为什么比mysql快

es 索引存于内存之中,采用倒序索引,结构为“分词 <=>id” 的方式,能通过分词快速定位文档(分词索引 vs b+tree)



声明:禁止任何非法用途使用,凡因违规使用而引起的任何法律纠纷,本站概不负责。

小周博客
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

精彩评论

全部回复 0人评论 7,777人参与

loading