编程技术分享平台

网站首页 > 技术教程 正文

从一道百度面试题说起(百度面试难么)

xnh888 2024-10-17 17:07:07 技术教程 20 ℃ 0 评论

前言

7月27日,我收到百度的面试邀约(8月1日),兴奋之余,我也做了5天的面试准备。从下午两点开始一直面试到五点半,长达三个半小时的面试也让我知道百度确实是一家重视技术的公司。其中有一道面试题尤其让我印象深刻,因为它分别是一面和二面的 “压轴题”,可以说这道面试题自己回答的好与否直接决定着自己能否进入下一轮面试,今天我主要就从这道面试题开始,一步一步讲解其中涉及到的知识点。这次的面经(四面)会在下一篇文章总结后分享给大家。

面试真题

真题:

PHP 如何解决网站大流量与高并发的问题(一面最后一题)

变体:

网站高并发的优化方法,从浏览器到服务器中间经过所有组件的你能想到的优化点(二面最后一题)

考点分析

  • 高并发架构相关概念
  • 高并发解决方案(优化方案)

说明

由于涉及到的知识点太多,我这里先给出我当时回答的总的结构,之后会对每个分点进行更为详细的分析,如果你有更好的答案,或者有一些遗漏的点还请指出。


高并发架构相关概念

并发(百度百科的定义):

并发,在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一处理机上运行,但任意时刻点上都只有一个程序在处理机上运行

web开发中说的高并发指的是什么?

上面的定义明显不是我们通常所言的并发,在互联网时代,所讲的并发、高并发,通常是指并发访问。也就是在某个时间点,有多少个访问同时到来,通常如果一个系统的日 PV 在干万以上,有可能是一个高并发的系统。

高并发的问题,我们具体该关心什么?

QPS:

每秒钟请求或者査询的数量,在互联网领域,指毎秒响应请求数(指 HTTP 请求),一个页面中可能有多个 HTTP 请求。

吞吐量:

单位时间内处理的请求数量(通常由QPS与并发数决定)

响应时间:

从请求发出到收到响应花费的时间。例如系统处理一个 HTTP 请求需要 100ms,这个 100ms 就是系统的响应时间

PV:

综合浏览量(Pageview),即页面浏览量或者点击量

UV:

独立访客(UniqueVisitor),即一定时间范围内相同访客多次访问网站,只计算为 1 个独立访客

带宽:

计算带宽大小需关注两个指标,峰值流量和页面的平均大小

  • 日网站带宽 = PV/统计时间(换算到秒)* 平均页面大小(单位 KB)* 8

常用的压测工具

ApacheBench、WRK、http_load、Web bench、Siege、Apache Jmeter

ApacheBench 概念

ApacheBench(ab),是 Apache 官方推出的工具刨建多个并发访问线程,模拟多个访问者同时对某一 URL 地址进行访问。它的测试目标是基于 URL 的,因此,它既可以用来测试 Apache 的负载压力,也可以测试 Nginx、 Lighttpd、 Tomcat、IS 等其它 Web 服务器的压力。

ApacheBench 的使用

模拟并发请求 1000次,总共请求 50000次

ab -c 1000 -n 50000 www.test.com

注意事项

  • 测试机器与被测试机器分开
  • 不要对线上服务做压力测试
  • 观察测试工具 ab 所在机器,以及被测试的前端机的 CPU,内存,网络等都不超过最高限度的 75% (top 命令)

高并发解决方案(优化方案)

流量层优化:

  • 防盗链处理

前端层优化:

  • 减少 HTTP 请求
  • 添加异步请求
  • 启用浏览器缓存和文件压缩
  • CDN 加速
  • 建立独立图片服务器

服务器层优化:

  • 页面静态化
  • 并发处理

数据库层优化:

  • 数据库缓存(Memache, Redis)
  • 分库分表(水平、垂直切分)、分区操作
  • 读写分离
  • 负载均衡

Web 服务器架构优化:

  • 负载均衡

流量层优化(防盗链处理)

如果我们发现我们的网站主页的流量很大(服务器负荷很重)但是网站的 PV、UV 却很小,这时候我们就需要考虑我们的网站是不是被盗链了。

盗链概念

盗链是指在自己的页面上展示一些并不在自己服务器上的内容获得他人服务器上的资源地址,绕过别人的资源展示页面,直接在自己的页面上向最终用户提供此内容,常见的是小站盗用大站的图片、音乐、视频、软件等资源,通过盗链的方法可以减轻自己服务器的负担,因为真实的空间和流量均是来自别人的服务器。

防盗链概念

防止别人通过一些技术手段绕过本站的资源展示页面,盗用本站的资源,让绕开本站资源展示页面的资源链接失效,可以大大减轻服务器及带宽的压力。

工作原理

通过 Referer 或者签名,网站可以检测目标网页访问的来源网页,如果是资源文件,则可以跟踪到显示它的网页地址。一旦检测到来源不是本站即进行阻止或者返回指定的页面。

Referer 方式防盗链

Nginx 模块 ngx_http_referer_module 用于阻挡来源非法的域名请求。

配置

location ~ .*\.(gifljpglpnglflvlswfrarlzips)${

valid_referers none blocked test.com * test.com:

if($invalid_referer){

#return 403:

rewrite ^/ http://www.test.com/403.jpg;

}

}

valid_referers none I blocked I server_names,string

解释

  • none: "Referer" 来源头部为空的情况
  • blocked: "Referer" 来源头部不为空,但是里面的值被代理或者防火墙删除了,这些值都不以 http:// 或者 https:// 开头。
  • server names: "Referer" 来源头部包含当前的 server names

加密签名防盗链

Referer 这种方式虽然可以防止一下低中级的防盗链,但是更高级的盗链往往可以伪造 Referer,从而绕过 Referer 检测,更安全的方式是可以采用加密签名解决 。

使用第三方模块 HttpaccesskeyModule 实现 Nginx 防盗链

Nginx 配置:

accesskey on off 模块开关

accesskey_hashmethod md5 | sha-1 签名加密方式

accesskey_arg GET 参数名称

accesskey signature 加密规则

location ~ .*\.(gifljpglpnglflvlswfrarlzips)${

accesskey on;

accesskey_hashmethod md5;

accesskey_arg sign;

accesskey_signature"yourPrefix$remote_addr; # 你的前缀码和客户端 IP

}

PHP 代码段:

$sign= md5('yourPrefix'. $_SERVER['remote_addr'];

echo '<img src=”./youimage.png?sign=. $sign.'">';


前端层优化

减少 HTTP 请求

性能黄金法则:

只有 10% - 20% 的最终用户响应时间花在接收请求的 HTML 文档上,剩下的 80% - 90% 时间花在 HTML 文档所引用的所有组件(图片,脚本,CSS,Flash 等等)进行的 HTTP 请求上。

如何改善:

改善响应时间的最简单途径就是减少组件数量,并由此减少 HTTP 请求的数量。

减少 HTTP 请求的方式

图片地图

图片地址允许你在一个图片上关联多个 URL,目标 URL 的选择取决于用户单击了图片上的哪个未知。

  • 将五张图片合并为一张图片,然后以位置信息定位超链接,把五个 HTTP 请求减少为一个,可以保证设计的完整性和功能的齐全性
  • 使用 标签

CSS 精灵(CSS Sprites)

Sprites 中文翻译为 CSS 精灵,通过使用合并图片,通过指定 CSS 的 background-image 和 background-position 来显示元素。

合并脚本和样式表

  1. 使用外部的 JS 和 CSS 文件引用的方式,因为这要比直接写在页面中性能更好一点
  2. 独立的一个 JS 比用多个 JS 文件组成的页面载入要快 38%
  3. 把多个脚本合并为一个脚本,把多个样式表合并为一个样式表

图片使用 Base64 编码减少页面请求数

采用 Base64 的编码方式将图片直接嵌入到网页中,而不是从外部载入。

添加异步请求

Ajax 可以实现动态不刷新(局部刷新),就是能在不更新整个页面的前提下维护数据。这使得 Web 应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变过的信息。

优势:

  • 通过异步模式,提升了用户体验
  • 优化了浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占用
  • Ajax 引擎在客户端运行,承担了一部分本来由服务器承担的工作,从而减少了大用户量下的服务器负载

启用浏览器缓存和文件压缩

缓存分类:

HTTP 缓存模型中,如果请求成功会有三种请款

  1. 200 from cache:直接从本地缓存中获取响应,最快速,最省流量,因为根本没有向服务器发送请求
  2. 304 Not Modified:协商缓存,浏览器再本地没有命中的情况下请求头中发送一定的校验数据到服务端,如果服务端数据没有改变浏览器从本地缓存响应,返回304,快速,发送的数据很少。只返回一些基本的响应头信息,数据量很小,不发送实际响应体
  3. 200 OK:以上两种缓存全部失败,服务器返回完整响应,没有用到缓存,相对最慢

本地缓存

浏览器认为本地缓存可以使用,不会去请求服务端

相关 Header:

  • Expires:expires 值对应一个形如 Thu,32 Dec 2037 23:55:55 GMT 的格林威治时间,告诉浏览器缓存实现的时刻,如果还没到该时刻,标明缓存有效,无需发送请求(绝对时间)
  • Cache-Control(设置缓存秒数):运用 Cache-Control 告知浏览器缓存过期的时间间隔不是时刻(时间间隔)
  • no-store:禁止浏览器缓存响应
  • no-cache:不允许直接使用本地缓存,先发起请求和服务器协商
  • max-age=delta-seconds:告知浏览器该响应本地缓存有效的最长期限,以秒为单位

协商缓存

当浏览器没有命中本地缓存,如本地缓存过期或者响应中声明不允许直接使用本地缓存,那么浏览器肯定会发起服务端请求,服务端会验证数据是否修改,如果没有通知浏览器使用本地缓存

相关 Header:

  1. Last-Modified:通知浏览器资源的最后修改时间
  2. If-Modified-Since:得到资源的最后修改时间后,会将这个信息通过 If-Modified-Sinc 提交到服务器做检查,如果没有修改,返回 304 状态码
  3. Etag:HTTP1.1 推出,文件的指纹标识符,如果文件内容修改,指纹会改变
  4. If-None-Match:本地缓存失效,会携带此值去请求服务端,服务端判断该资源是否改变,如果没有改变,直接使用本地缓存,返回 304

适合缓存的内容

  1. 不变的图像,如 logo,图标等
  2. JS、CSS 静态文件
  3. 可下载的内容,媒体文件

建议使用协商缓存

  1. HTML 文件
  2. 经常替换的图片
  3. 经常修改的 JS、CSS 文件
  4. JS、CSS 文件的加载可以加入文件的签名来拒绝缓存
  5. index.css? 签名

不建议缓存的内容

  1. 用户隐私等敏感数据
  2. 经常改变的 API 数据接口

前端代码和资源的压缩

优势:

让资源文件更小,加快文件再网络中的传输,让网页更快的展现,降低带宽和流量开销

压缩方式:

JS、CSS、图片、HTML 代码的压缩、Gzip 压缩

JavaScript 代码压缩:

  • JavaScript 压缩的原理一般是去掉多余的空格和回车、替换长变量名、简化一些代码写法等
  • JavaScript 代码压缩工具很多,有在线工具,有应用程序,有编辑器插件

CSS 代码压缩:

  • 原理跟 JavaScript 压缩原理类似,同样是去除空白符、注释并且优化一些 CSS 语义规则等

使用 CDN 加速

CDN 的全称是 Content Delivery Network, 即内容分发网络,尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快,更稳定。

  • 在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络
  • CDN 系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应实践等综合信息将用户的请求重新导向离用户最近的服务器节点上。

使用 CDN 的优势

  • 本地 cache 加速, 提高企业站点(尤其含有大量图片和静态页面站点)的访问速度
  • 跨运营商的网络加速, 保证不同网络的用户都得到良好的访问质量
  • 远程访问用户根据 DNS 负载均衡技术智能自动选择 cache 服务器
  • 自动生成服务器的远程 Mirror(镜像)cache 服务器,远程用户访问时从 cache 服务器上读取数据,减少远程访问的带宽、分担网络流量、减轻原站点 Web 服务器负载等功能。

建立独立的图片服务器

好处

  • 分担 Web 服务器的 I/O 负载将耗费资源的图片服务分离出来,提高服务器的性能和稳定性
  • 能够专门对图片服务器进行优化-为图片服务设置有针对性的缓存方案,减少带宽成本,提高访问速度
  • 提高网站的可扩展性-通过增加图片服务器,提高图片吞吐能力

服务器层优化

页面静态化(动态语言静态化)

将现有 PHP、Python 等动态语言的逻辑代码生成为静态 HTML 文件,用户访问动态脚本重定向到静态 HTML 文件的过程

使用场景

对实时性要求不高的页面

好处

  • 动态脚本通常会做逻辑计算和数据查询,访问量越大,服务器压力越大
  • 访问量大时可能会造成 CPU 负载过高,数据库服务器压力过大

PHP 的并发处理

PHP 的 Swoole 扩展

PHP 的异步、并行、高性能网络通信引擎,使用纯 C 语言编写,提供了 PHP 语言的异步多线程服务器,异步 TCP/UDP 网络客户端,异步 MYSQL,异步 Redis,数据库连接池,Asynctask,消息队列,毫秒定时器,异步文件读写,异步 DNS 查询,除了异步 I/O 的支持之外, Swoole 为 PHP 多进程的模式设计了多个并发数据结构和 IPC 通信机制,可以大大简化多进程并发编程的工作。

消息队列

使用场景

用户注册:

  • 场景说明
  • 用户注册后,需要发注册邮件和注册短信
  • 串行方式
  • 将注册信息写入数据库成功后,发送注册邮件,再发送注册短信
  • 并行方式
  • 将注册信息写入数据库成功后,发送注册郎件的同时发送注册短信
  • 消息队列方式
  • 将注册信息写入数据库成功后,将成功信息写入队列,此时直接返回成功给用户,写入队列的时间非常短,可以忽略不计,然后异步发送邮件和短信

日志处理:

  • 应用场景
  • 解决大量日志的传输,日志采集程序将程序写入消息队列,然后通过日志处理程序的订阅消费日志

常见消息队列产品

Kafka、 Activemq、 Zeros、 Rabbitmq、 Redis 等


数据库层优化

数据库缓存(Memache, Redis)

MySQL 等一些常见的关系型数据库的数据都存储在磁盘当中,在高并发场景下,业务应用对 MySQL 产生的增、删、改、查的操作造成巨大的 I/O 开销和查询压力,这无疑对数据库和服务器都是一种巨大的压力,为了解决此类问题,缓存数据的概念应运而生。

优点:

  • 极大地解决数据库服务器的压力
  • 提高应用数据的响应速度

使用 Memcache 缓存查询数据

对于大型站点,如果没有中间缓存层,当流量打入数据库层时,即便有之前的几层为我们挡住一部分流量,但是在大并发的情况下,还是会有大量请求涌入数据库层,这样对于数据库服务器的压力冲击很大,响应速度也会下降,因此添加中间缓存层很有必要。

工作原理

Memcache 是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的 hash 表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。简单的说就是将数据调用到内存,然后从内存中读取,从而大大提高读取速度

使用 Redis 绶存查询数据

Memcache 的区别:

  • Redis 支持(快照、AOF),依赖快照进行持久化,aof增强了可靠性的同时,对性能有所影响
  • Memcache 不支持持久化,通常做缓存,提升性能
  • Redis 支持多种类的数据类型
  • Redis 用于数据量较小的高性能操作和运算上
  • Memcache 用于在动态系统中减少数据库负载,提升性能,适合做缓存,提高性能

数据表数据类型优化

  • tinyint (0-255) smallint, bigint(考虑空间的问题,考虑范围的问题)
  • char,vachar
  • enum 特定、固定的分类可以使用 enum 存储,效率更快
  • IP 地址的存储,用 PHP 的 ip2long('192.168.1.38'); //3232235814

索引优化

  • 索引不是越多越好,在合适的字段上创建合适的索引
  • 复合索引的前缀原则
  • lke 查询 % 的问题(% 在前如:%name 则索引失败)
  • 全表扫描优化(MySQL 自动识别)
  • or 条件索引使用情况(or 后的索引不会使用)
  • 字符串类型索引失效的问题(如字符串类型的字段必须要加引号查询)

SQL 语句的优化

使用慢查询日志找到需要优化的 SQL 语句,一般会再用 explain 分析。

  • 优化查询过程中的数据访问
  • 使用 Limit
  • 返回列不用 *
  • 优化特定类型的查询语句
  • 优化关联查询
  • 优化子查询
  • 优化 Group by 和 distinct

数据库服务器架构的优化

  • 分库分表
  • 水平切分
  • 垂直切分
  • 读写分离
  • 负载均衡
  • 通过 LVS 的三种基本模式实现负载均衡
  • My Cat 数据库中间件实现负载均衡

Web 服务器架构优化

七层负载均衡的实现

  • 基于 URL 等应用层信息的负载均衡
  • Nginx 的 proxy 是它一个很强大的功能,实现了7层负载均衡

Nginx 的简单配置

Nginx 配置

http{

upstream test_cluster {

server 121.41.68.2:8001 weight=11;// 加权中

server 121.41.69.2:8002 weight=10;

#server 121.41.68.2:8003;

#server 121.41.68.9;

}

server {

listen 80;

location / {

proxy_pass http: //test_cluster;

}

}

}

四层负载均衡的实现

  • 通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器
  • LVS 实现服务器集群负载均衡有三种方式,NAT,DR 和 TUN

参考资料

  • AJAX 笔试面试题汇总
  • 李捷的全面解读 PHP 面试

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表