PHP考察点
一、PHP基础知识
二、JavaScrpit,jQuery,AJAX基础知识
三、Linux基础知识
四、MySql数据库基础知识与优化
五、程序设计题
六、PHP框架基础知识
七、算法逻辑思维
八、高并发解决方案
一、PHP基础知识
引用变量,常量及数据类型,运算符及流程控制,自定义函数及内部函数,正则表达式,文件及目录处理,会话控制,面向对象,网络协议,开发环境相关。
二、Mysql基础知识考察点
MySQL基础知识,MySQL如何创建高性能的索引,MySQL的SQL语句编写和优化,MySQL的高可扩展和高可用及安全性考察点。
三、常见算法、逻辑思维考察点
空间复杂度,其他逻辑算法,PHP内置函数实现
四、高并发解决方案考查点
如何理解高并发:
- PV
- UV
- QPS
优化时机----QPS阶段性优化
优化案例:
- 防盗链
- 减少http请求
- 浏览器缓存
- CDN
- 数据库缓存(Memcache/MySQL)
- MySQL的读写分离
- 分区以及分库分表
- LVS负载均衡
五、常见面试题
- 什么事引用变量?在PHP当中用什么符号定义引用变量?
- 要求写书Jquery中可以处理AJAX的几种方法
- 写出尽可能多的LINUX命令
- 写出三种以上MySQL数据库存储引擎的名称
- 编写一个在线留言本,实现用户在线留言功能
- 谈谈你对MVC的认识,介绍几种目前比较流行的MVC框架
- 请写出常见排序算法
- PHP如何解决网站大流量与高并发问题。
PHP基础知识细节
一、常量及数据类型
问题:PHP中字符串可以额使用哪三种定义方法,以及各自的区别是什么?
答:单引号、双引号、heredoc(等同于双引号)和newdoc(等同于单引号)
- 单引号
- 单引号不解析变量、转义字符串,只解析单引号和反斜线本身
- 变量和变量、变量和字符串、字符串和字符串之间可用 “.” 连接
- 双引号
- 双引号可解析变量、变量可以使用特殊字符和{}包含
- 双引号可解析所有转义字符
- 使用“.”连接
最重要区别:单引号效率 > 双引号效率
如下为newdoc示例:
$a = <<< 'EOT'
字符串
EOT;
echo $a;
如下为heredoc示例(无单引号):
$a = <<< EOT
字符串
EOT;
echo $a;
数据类型(8类型,3大类)
3类:标量、符合、特殊
标量:
- 浮点:不能用于比较,不能用于==判断
- 布尔:false七种情况(0,0.0,'','0',false,array(),null)
- 数组
- 超全局数组($_GLOBALS,$_GET,$_POST,$_REQUEST,$_SESSION,$_COOKIE,$_FILES,$_ENV,$_SERVER)
- null:直接赋值为null、未定义的变量、unset销毁的变量
常量:
- const:语言结构,可定义类
- define():函数,不可定义类
预定义常量:
- __FILE__
- __LINE__
- __DIF__
- __FUNCTION__
- __CLASS__
- __TRAIT__
- __METHOD__
- __NAMESPACE__
$_SERVER补充:
- $_SERVER['SERVER_ADDR']
- $_SERVER['SERVER_NAME']
- $_SERVER['REQUEST_TIME']
- $_SERVER['QUERY_STRING']
- $_SERVER['HTTP_REFERER']
- $_SERVER['HTTP_USER_AGENT']
- $_SERVER['REMOTE_ADDR']
- $_SERVER['REQUEST_URI']
- $_SERVER['PATH_INFO']
二、运算符
- PHP运算符的错误控制符 “@”
- 延伸:
- PHP所有运算符考点
- 运算符的优先级
- 比较运算符
- 递增递减运算符
- 逻辑运算符
- PHP支持一个错误运算符:“@” 。当将其放在一个PHP表达式前,该表达式可能产生的任何错误信息将被忽略。
- 运算符优先级:
- 递增/递减>!>算数运算符>大小比较>(不)相等比较>引用>位运算符(^)>位运算符(|)>逻辑与>逻辑或>三目>赋值>and>xor>or
- 括号使用可增加代码可读性,推荐使用
- ==和===的区别
- 等值判断(false的七种情况):0,0.0,'0','',null,array(),false
- 递增/递减运算符不影响布尔值
- 递减null值没有效果,递增null为1
- 递增/递减在前先运算,在后后运算
- 逻辑短路
- ||和&&和or,and
- $a = true || $b == 3 -->1
三、流程控制
题:PHP遍历数组的三种方式及各自区别?
答:for,foreach,联合使用list(),each(),while()
- for循环只能遍历索引数组
- foreach()可以遍历索引和关联数组
- 联合使用List(),each(),while()循环同样可以遍历索引和关联数组
- while(),list(),each()组合不会reset()
- foreach遍历会对数组进行reset()操作
- elseif分支考点
- 使用elseif语句有一个基本原则,总把优先范围小的条件放到前面处理
- switch……case和if不同,switch后面的控制表达式的数据类型只能是整型、浮点类型或字符串。
- countinue在switch中的作用等价于break
- 效率:如果条件比一个简单的比较更复杂的多或者在一个很多次的循环中,那么用switch语句会快一些
真题:PHP如何优化多个if……elseif语句的情况?
答:使用switch……case流程控制语句
四、自定义函数
引用函数:从函数返回一个引用,必须在函数声明和指派返回值给一个变量时都使用运算符&
function &myFunc(){
static $b = 10;
return $b;
}
$a = myFunc(); -->10
$a = &myFunc();
$ = 100;
echo myFunc(); -->100
- 外部文件导入:
- include/require语句包含并运行指定文件
- 如果给出路径名按照路径来找,否则从include_path中查找,如果include_path中没有,从调用脚本文件所在的目录和当前工作目录下寻找。
- 当一个文件被包含是,其中所包含的代码继承了include所在行的变量范围
- include报出警告(E_WARNING),require报出致命错误(E_COMPILE_ERROR)
- require(include)/require_once(include_once)唯一区别是后者判断是否包含过,有则不再包含
- 系统内置函数:
- 时间日期函数
- date()
- strtotime()
- mktime()
- time()
- microtime()
- date_timezone_set()
-
- IP处理函数
- ip2Long(),long2Ip()
-
- 打印处理
- print()
- printf()
- print_r()
- echo
- sprintf()
- var_dump()
- var_export()
-
- 序列化和反序列化函数
- serialize()
- unserialize()
-
- 字符串处理函数
- implode()
- explode()
- join()
- strrev()
- trim()
- ltrim()
- rtrim()
- strstr()
- number_format()
-
- 数组处理函数
- array_keys()
- array_values()
- array_diff()
- array_intersect()
- array_merge()
- array_shift()
- array_unshift()
- array_pop()
- array_push()
- sort()
五、正则表达式
题:139开头的手机号?
- 正则表达式的作用:分割、查找、匹配、替换字符串
- 分隔符:正斜线(/),hash符号(#),取反符号(~)
- 通用原子:\d,\D,\w,\W,\s,\S
- 元字符:*,?,$,+,{n},{n,},{n,m},[],(),[^],|,[-]
- 模式修正符:i,m,e,s,U,x,A,D,u
后向引用:
例:
$str = '<b>absdfec</b>';
$pattern = '/<b>(.*)<\/b>/';
echo preg_replace($pattern, '\1', $str);
preg_match($pattern, $str, $match);
var_dump($match);
贪婪模式:
例:
$str = '<b>abc</b><b>def</b>';
$pattern = '/<b>(.*)<\/b>/U';
preg_match_all($pattern, $str, $match);
var_dump($match);
echo preg_replace($pattern, '\1', $str);
正则表达式:PCRE函数:
- preg_match()
- preg_match_all()
- preg_replace()
- preg_split()
中文匹配:UTF-8汉子编码范围是0x4e00 -- 0x9fa5,在ANSII(gb2312)环境下,0xb0 -- 0xf7,0xa1 -- 0xfe
UTF-8要用u模式修正符,使模式字符串被当成UTF-8,在ANSII(gb2312)环境下,要使用chr将ANSII码转换为字符
下面是一个匹配UTF-8格式的程序:
$pattern = '/[\x{4e00}-\x{9fa5}]+/u';
preg_match($pattern, $str, $match);
var_dump($match);
下面是一个匹配GBK格式的程序:
$pattern = '/['.chr(0xb0).'-'.chr(0xf7).']['.chr(0xa1).'-'.chr(0xfe).']+/';
preg_match($pattern, $str, $match);
var_dump($match);
以139开头的手机号:
$str = '13988888888';
$pattern = '/^139\d{8}$/';
preg_match($pattern, $str, $match);
var_dump($match);
正则匹配img标签中的src值
$str = '<img alt="高清视频" src="logo.png" name="战狼2">';
$pattern = '/<img.*src="(.*?)".*\/?>/';
preg_match($pattern, $str, $match);
var_dump($match);
六、文件及目录处理
七、会话控制
八、面向对象
- PHP类权限控制修饰符
- 延伸:面向对象的封装、继承和多态
- 单一继承
- 方法重写
- 抽象类定义
- 接口定义
- 延伸:魔术方法
- __construct()
- __destruct()
- __call()
- __callStatic()
- __get()
- __set()
- __isset()
- __unset()
- __sleep()
- __wakeup()
- __toString()
- __clone()
- public(类内部,类外部,子类使用),protected(类内部,子类使用),private(类内部使用)
- 常见设计模式
- 工厂模式
- 单例模式
- 注册树模式
- 适配器模式
- 观察者模式
- 策略模式
九、网络协议
题:我们常见的HTTP协议、TCP协议分别位于OSI网络模型的第几层?
HTTP协议位于第七层(应用层),TCP协议位于第四层(网络层)
HTTP/1.1中,状态码200,301,304,403,404,500
- OSI七层模型
- 物理层: 建立、维护、断开物理连接
- 数据链路层:建立逻辑链接、进行硬件地址寻址、差错校验等功能
- 网络层:进行逻辑地址寻址、实现不同网络之间的路径选择
- 传输层:定义传输数据的协议端口号,以及流控和差错校验(协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层)
- 会话层:建立、管理、终止会话
- 表示层:数据的表示、安全、压缩
- 应用层:网络服务与最终用户的一个接口(协议有:HTTP 、FTP、TFTP、SMTP、SNMP、DNS、TELNET、HTTPS、POP3、DHCP)
- HTTP协议的工作特点和工作原理
- 工作特点
- 基于B/S模式
- 通信开销小、简单快速、传输成本低
- 使用灵活、可使用超文本传输协议
- 节省传输时间
- 无状态
-
- 工作原理
- 客户端发送请求给服务器,创建一个TCP连接,指定端口号,默认80,连接到服务器,服务器监听浏览器请求,一旦监听到客户端请求,分析请求类型后,服务器会向客户端返回状态信息和数据内容
- HTTP协议常见请求/响应头和请求方法
- 请求/响应头
- Content-type:请求的与实体对应的MIME信息
- Accept:指定客户端能接受的内容类型
- Origin:主要是用来说明最初请求是从哪里发起的,只用于Post请求,而Referer则用于所有类型的请求
- Referrer:Referer则用于所有类型的请求
- Cookie:Cookie值
- Cache-Control:缓存机制
- User-Agent:用户信息
- X-Forwarded-For:请求端真实IP,做代理时可以获取该IP
- Access-Control-Allow-Origin:允许特定的域名访问,一般做跨域使用
- Last-Modified:最后响应时间
-
- 请求方法
- GET:get请求会显示请求的资源,一般get方法是用于数据读取(密等)
- POST:向指定资源提交数据(非密等)
- HEAD:请求只会回传头信心
- OPTIONS:请求服务器,返回该资源所支持的所有http请求方法,该方法会用 “*” 来代替资源名称,测试服务器功能是否正常
- PUT:上传最新资源,修改用PUT,上传用POST
- DELETE:请求服务器删除指定资源
- TRACE:请求服务器回显其收到的信心,主要用于http的测试和诊断
- HTTPS协议的工作原理
- HTTPS是一种基于SSL/TLS的HTTP协议,所有HTTP数据都是在SSL/TLS协议封装之上传输的
- HTTPS在HTTP协议的基础之上,添加了SSL/TLS握手以及数据加密传输,也属于应用层协议
- GET和POST区别(常见考点)
- 做后退按钮和刷新时,get没有太大变化,post数据被重新提交
- get可以收藏为书签,post不可收藏为书签
- get可以被缓存,post不可被缓存
- get请求编码类型是 x-www-form-urlencoded,post不仅有该编码类型,还有Multipart/form-data(二进制类型)
- get在历史记录中参数会保留在浏览器历史中,post参数不会保留在历史中
- get长度限制在2048字符(2kb),post没有限制
- get只允许ASCII,post可以允许二进制
- get参数在url中暴露,post不会
- 常见网络协议及端口
- FTP:文件传输协议,默认端口21
- Telnet:远程登陆端口,用户通过自己的身份远程连接到计算机上,可以完成DOS模式下的通信服务,默认端口23
- SMTP:简单邮件传输协议,用于发送邮件, 默认端口25
- POP3:用于接收邮件,默认端口110
- HTTP:超文本传输协议,默认端口80
- DNS:域名解析服务,在WINDOWS-NT系统应用最多,默认端口53
- HTTP状态码:
- 五类响应:1xx,2xx(请求成功),3xx(页面重定向),4xx(客户端错误),5xx(服务端错误)
十、开发环境及配置
十一、JavaScript和jQuery基础考察点
十二、AJAX基础内容考察点
十三、Linux基础考察点
题:写出尽可能多的linux命令?
- linux常用命令:
- 系统安全
- sudo
- su
- chmod
- setfacl
-
- 进程管理
- w
- top
- ps
- kill
- pkill
- pstree
- killall
-
- 用户管理
- id
- usermod
- useradd
- groupadd
- userdel
-
- 文件系统
- mount
- umount
- fsck
- df
- du
-
- 系统关机和重启
- shutdown
- reboot
-
- 网络应用
- curl
- telnet
- elinks
-
- 网络测试
- ping
- netstat
- host
-
- 网络配置
- hostname
- ifconfig
-
- 常用工具
- ssh
- screen
- clear
- who
- date
-
- 软件包管理
- yum
- rpm
- apt-get
-
- 文件查找和比较
- locate
- find
-
- 文件内容查看
- head
- hail
- less
- more
-
- 文件处理
- touch
- unlink
- rename
- ln
- cat
-
- 目录操作
- cd
- mv
- rm
- pwd
- tree
- cp
- ls
-
- 文件权限属性
- setfacl
- chmod
- chown
- chgrp
-
- 压缩/解压
- bzip2/bunzip2
- gzip/gunzip
- zip/unzip
- tar
-
- 文件传输
- ftp
- scp
-
- 定时命令
- crontab命令
- crontab -e
- * * * * * 命令(分 时 日 月 周)
- at命令(明天亮点执行do_job)
- at 2:00 tomorrow
- at>home/Json/do_job
- at>Ctrl+D
- vi/vim编辑器
- 模式
- 一般模式、编辑模式和命令行模式
- 一般模式:删除、复制、粘贴
- 切换编辑模式:i、I、o、O、a、A、r、R
- 切换命令行模式::、/、?
-
- 移动光标
- ctrl+f
- ctrl+b
- 0或者功能键Home
- $或者功能键End、G、gg
- N+enter
-
- 查找和替换
- /word 查找
- ?word 查找
- :n1,n2s/word1/word2/g 替换
- :1,$s/word/word2/g 替换
- :1,$s/word1/word2/gc 替换
-
- 删除、复制和粘贴
- x,X
- dd
- ndd
- yy
- nyy
- p
- P
- ctrl+r
-
- 保存和退出
- w 保存
- q 退出
- wq 保存并退出
-
- 视图模式(vim)
- v
- V
- ctrl+v
- y
- d
-
- 配置
- :setnu 显示行号
- :setnonu 不显示行号
- shell基础
- 脚本执行方式
- 赋予权限,直接执行,例:chmod + x test.sh; ./test.sh
- 调用解释器使得脚本执行,例:bash、csh、ash、bah、ksh等
- 使用source命令,例:source test.sh
-
- 编写基础
- 开头用 #! 制定脚本解释器,例如:#!/bin/sh
- 编写具体功能
真题:如何实现每天0点钟重新启动服务器?
答:crontab -e
* * * * * reboot
分 时 日 月 周 reboot
0 0 * * * reboot
十四、MySQL基础
真题:请写出下面MySQL数据类型表达的意义(int(0),char(16),varchar(16),datetime,text)?
考点:
- MySQL数据类型
- 整数类型:TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT 配合属性 UNSIGNED(非负数)
- 注意:长度,可以为整数类型指定宽度,例如:INT(11)、对大多数应用是没有意义的,它不会限制值的合法范围,只会影响显示字符的个数。
- zefofill进行 “0” 补充,例如:INT(5),存入数据是12,实际显示内容为00012
- 实数类型:FLOAT、DOUBLE、DECIMAL
- DECIMAL可存储比BIGINT还大的整数;可以用于存储精确的小数
- FLOAT和DOUBLE类型支持使用标准浮点进行近似计算
- 字符串类型:VARCHAR、CHAR、TEXT、BLOB
- VARCHAR类型用于存储可变长字符串,它比定长类型更节省空间
- VARCHAR使用1或2个额外字节记录字符串的长度,列长度小于255字节,使用1个字节表示,否则用2个
- VARCHAR长度,如果存储内容超出指定长度,会被截断
- CHAR是定长的,根据定义的字符串长度分配足够空间
- CHAR会根据需要采用空格进行填充以方便比较
- CHAR适合存储很短的字符串,或者所有值都接近同一个长度
- CHAR,超出设定的长度,会被截断
- 对于经常变更的数据,CHAR比VARCHAR更好,CHAR不容易产生碎片
- 对于非常短的列,CHAR比VARCHAR在存储空间上更有效率
- 只分配真正需要的空间,更长的列会消耗更多内存
- 尽量避免使用BLOB/TEXT类型,查询会使用临时表,导致严重性能开销
- 枚举类型:有时可以使用枚举代替常用的字符串类型,把不重复的集合存储成一个预定义集合,非常紧凑,把列表值压缩到一个或两个字节,内部存储是整数
- 尽量避免使用数字作为ENUM枚举常量,易混乱
- 排序是按照内部存储的整数进行排序
- 枚举表会使表大小大大减小
- 日期和时间类型:尽量使用TIMESTAMP,比DATETIME空间效率高
- 用整数保存时间戳的格式通常不方便处理
- 如果需要存储微妙,可以使用BIGINT
- 列属性
- auto_increment
- default
- not null
- zerofill
- MySQL基础操作
- MySQL的连接和关闭:mysql -u(用户名) -p(密码) -h(主机) -P(端口)
- 其他:\G(打印的结果垂直显示),\c(取消当前mysql命令),\q(退出mysql),\s(显示mysql服务器状态),\h(显示帮助信息),\d(改变执行符号)
- MySQL存储引擎
- InnoDB表引擎
- 默认事务型引擎,最重要最广泛的存储引擎,性能非常优秀
- 数据存储在共享表空间,可以通过配置分开
- 对主键查询的性能高于其他类型的存储引擎
- 内部做了很多优化,从磁盘读取数据时自动在内存构建hash索引,插入数据时自动构建插入缓冲区
- 通过一些机制和工具支持真正的热备份
- 支持崩溃后的安全恢复
- 支持行级锁
- 支持外键
- MyISAM表引擎
- 5.1版本前,MyISAM是默认的存储引擎
- 拥有全文索引、压缩、空间函数
- 不支持事务和行级锁,不支持崩溃后的安全恢复
- 表存储在两个文件,MYD和MYI
- 设计简答,某些场景下性能很好
- 其他表引擎:Archive,Blackhole,CSV,Memory
- 优先选择InnoDB
- MySQL锁机制
- 基础概念:表锁是日常开发当中常见的问题,因此也是面试当中最常见的考察点,当多个查询同一时刻进行数据修改时,就会产生并发控制的问题。
- 共享锁和排他锁,其实就是读锁和写锁
- 读锁:共享的、不堵塞、多个用户可以同时读取一个资源,互不干扰
- 写锁:排他的,一个写锁会阻塞其他的写锁和读锁,这样可以只允许一个人进行写入,防止其他用户读取正在写入的资源
- 锁粒度:
- 表锁,系统性能开销最小,会锁定整张表,MyISAM使用表锁
- 行锁:最大程度的支持并发处理,但是也带来了最大的锁开销,InnoDB实现行级锁
- MySQL事务处理、触发器
- MySQL提供事务处理的表引擎,InnoDB
- 服务器层不管事务,由下层的引擎实现,所以同一个事务中,使用多种存储引擎不靠谱
- 在非事务的表上执行事务操作MySQL不会发出提醒,也不会报错
- 存储过程
- 为以后的使用而保存的一条或多条MySQL语句的集合
- 存储过程就是有业务逻辑和流程的集合
- 可以在存储过程中创建表,更新数据,删除等等
- 使用场景:
- 通过把处理封装在容易使用的单元中,简化复杂的操作
- 保证数据一致性
- 简化对变动的管理
- 触发器
- 提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程
- 使用场景
- 可通过数据库中的相关表实现级联更改
- 实时监控某张表中的某个字段的更改而需要做出相应的处理
- 某些业务编号的生成等
- 滥用会造成数据库及应用程序的维护困难
- 牢记以上基础知识点,理解数据类型CHAR和VACHAR的差别,表存储引擎InnoDB和MyISAM的区别
十五、创建高性能索引
真题:简单描述MySQL中,索引,主键,唯一索引,联合索引的区别,对数据库的性能有什么影响?
- MySQL索引的基础和类型
- 索引类似于书籍的目录,要想找到一本书的某个特定主题,需要先查找书的目录,定位对应的页码
- 存储引擎使用类似的方式进行数据查询,先去索引当中找到对应的值,然后根据匹配的索引找到对应的数据行
- 索引对性能的影响
- 大大减少服务器需要扫描的数据量
- 帮助服务器避免排序和临时表
- 将随机I/O变顺序I/O
- 大大提高查询速度,降低写的速度,占用磁盘
- 索引的使用场景
- 对于非常小的表,大部分情况下全表扫描效率更高
- 中到大型表,索引非常有效
- 特大型的表,建立和使用索引的代价将随之增大,可以使用分区技术来解决
- 索引类型
- 索引有很多种类型,都是实现在存储引擎层的
- 普通索引:最基本的索引,没有任何约束限制
- 唯一索引:与普通索引类似,但是具有唯一约束
- 主键索引:特殊的唯一索引,不允许有空值
- 组合索引:将多个列组合在一起创建索引,可以覆盖多个列
- 外键索引:只有InnoDB类型的表才可以使用外键索引,保证数据的一致性、完整性和实现级联操作
- 全文索引:MySQL自带的全文索引只能用于MyISAM,并且只能对英文进行全文检索
- 索引类型的区别
- 一个表只能有一个主键索引,可以有多个唯一索引
- 主键索引一定是唯一索引,唯一索引不是主键索引
- 主键可以与外键构成参照完整性约束,防止数据不一致
- MySQL索引的创建原则
- 最适合索引的列是出现在WHERE子句中的列,或连接子句中的列而不是出现在SELECT关键字后的列
- 索引列的基数越大,索引的效果越好
- 对字符串进行索引,应该制定一个前缀长度,可以节省大量的索引空间
- 根据情况创建复合索引,复合索引可以提高查询效率
- 避免创建过多索引,索引会额外占用磁盘空间,降低写操作效率
- 主键尽可能选择较短的数据类型,可以有效减少索引的磁盘占用提高查询效率
- MySQL索引的注意事项
- 复合索引遵循前缀原则
- like查询,%不能在前,可以使用全文索引
- column is null 可以使用索引
- 如果MySQL估计使用索引比全表扫描更慢,会放弃使用索引
- 如果or前的条件中的列有索引,后面的没有,索引都不会被用到
- 列类型是字符串,查询时一定要给值加引号,否则索引失效
- 解题方法
- 在一些MySQL索引基础考题中,我们可以轻松的通过索引基础和类型来解决此类问题,对于一些索引创建注意事项方面的考点,我们可以通过索引创建原则和注意事项类解决
十六、MySQL语句编写
真题:有A(id,sex,par,c1,c2),B(id,c1,c2)两张表,其中A.id与B.id关联,现在要求写出一条SQL语句,将B中的age>50的记录的c1,c2更新到A表中统一记录中的c1,c2字段中
- MySQL关联UPDATE语句
- 关联更新:
- UPDATE A,B SET A.c1 = B.c1, A.c2 = B.c2 WHERE A.id = B.id
- UPDATE A INNER JOIN B ON A.id = B.id SET A.c1 = B.c1, A.c2 = B.c2 WHERE ...
- MySQL关联查询语句(六种关联查询)
- 交叉连接(CROSS JOIN)
- SELECT * FROM A,B(,C)
- SELECT FROM A CROSS JOIN B (CROSS JOIN C)
- 没有任何关联条件,结果是笛卡儿积,结果集会很大,没有意义,很少使用
- 内连接(INNER JOIN)
- SELECT * FROM A,B WHERE A.id = B.id
- SELECT * FROM A INNER JOIN B ON A.id = B.id
- 内连接分为三类:
- 等值连接:ON A.id = B.id
- 不等值连接:ON A.id > B.id
- 自连接:SELECT * FROM A T1 INNER JOIN A T2 ON T1.id = T2.pid
- INNER JOIN 可缩写成 join
- 外链接(LEFT JOIN/RIGHT JOIN)
- 左外连接:LEFT OUTER JOIN,以左表为主,先查询出左表,按照ON后的关联条件匹配右表,没有匹配到的用NULL填充,可以简写成LEFT JOIN
- 右外连接:RIGHT OUTER JOIN,以右表为主,先查询出右表,按照ON后的关联条件匹配左表,没有匹配到的用NULL填充,可以简写成RIGHT JOIN
- 联合查询(UNION与UNION ALL)
- SELECT * FROM A UNION SELECT * FROM B UNION ...
- 就是把多个结果集集中在一起,UNION前的结果为基准,需要注意的是联合查询的列数要相等,相同的记录行会合并
- 如果使用UNION ALL,不会合并重复的记录行
- 全连接(FULL JOIN)
- MySQL不支持全链接
- 可以使用LEFT JOIN 和 UNION 和 RIGHT JOIN 联合使用
- SELECT * FROM A LEFT JOIN B ON A.id = B.id UNION SELECT * FROM A RIGHT JOIN B ON A.id = B.id
- 嵌套查询
- 用一条SQL语句的结果作为另外一条SQL语句的条件
- SELECT * FROM A WHERE id IN (SELECT id FROM B)
- 解题方法:
根据考题要搞清楚表的结构和多表之间的关系,根据想要的结果思考使用哪种关联方式,通常把要查询的列先写出来,然后分析这些列都属于哪些表,才考虑使用关联查询
十七、MySQL查询优化
真题:请简述项目中优化SQL语句执行效率的方法,从哪些方面,SQL语句性能如何分析?
- 查找分析查找速度慢的原因
- 记录慢查询日志:分析查询日志,不要直接打开慢查询日志进行分析,这样比较浪费时间和精力,可以使用pt-query-digest工具进行分析
- 使用show profile:
- set profiling = 1:开启,服务器上执行的所有语句会检测消耗的时间,存到临时表中
- show profiles:查看临时表查询所用时间
- show profile for query 临时表ID:查询一个查询语句的时间花费在何处
- 使用show status:
- show status 会返回一些计数器
- show global status 查看服务器级别的所有计数
- 有时候根据这些计数,可以猜测书哪些操作代价较高或者消耗时间多
- 使用show processlist:观察是否有大量线程处于不正常的状态或者特征
- 使用explain(重要):分析单条SQL语句
- 示例:explain select * from user\G;
- 优化查询过程中的数据访问
- 访问数据太多导致查询性能下降
- 确定应用程序是否在检索大量超过需要的数据,可能是太多行或列
- 确认MySQL服务器是否在分析大量不必要的数据行
- 避免使用如下SQL语句
- 查询不需要的记录,使用Limit解决
- 多表关联返回全部列,制定A.id,A.name,B.age
- 总是取出全部列,SELECT * 会让优化器无法完成索引覆盖扫描的优化
- 重复查询相同的数据,可以缓存数据,下次直接读取缓存
- 是否在扫描额外的记录
- 使用explain来进行分析,如果发现查询需要扫描大量的数据但只返回少数的行,可以通过如下技巧去优化
- 使用索引覆盖扫描,把所有用的列都放到索引中,这样存储引擎不需要回表获取对应行就可以返回结果
- 改变数据库和表的结构,修改数据表范式
- 重写SQL语句,让优化器可以更优的方式执行查询
- 优化长难的查询语句
- 一个复杂的查询还是多个简单的查询
- MySQL内部每秒能扫描内存中上百万行数据,相比之下,响应数据给客户端就要慢很多
- 使用尽可能少的查询是好的,但是有时将一个大的查询分解为多个小的查询时很有必要的
- 切分查询
- 将一个大的查询分为多个小的相同的查询
- 一次性删除1000万的数据要比一次删除1万,暂停一会的方案更加损耗服务器开销
- 分解关联查询
- 可以将一条关联语句分解成多条SQL来执行
- 让缓存的效率更高
- 执行单个查询可以减少锁的竞争
- 在应用层做关联可以更容易对数据库进行拆分
- 查询效率会有大幅提升
- 较少冗余记录的查询
- 优化特定类型的查询语句
- 优化count()查询
- count(*)中的*会忽略所有列,直接统计所有列数,因此不要使用count(列名)
- MyISAM中,没有任务WHERE条件的count(*)非常快
- 当有WHERE条件,MyISAM的count统计不一定比其他表引擎快
- 优化方法:
- 可以使用explain查询近似值,用近似值替代count(*)
- 增加汇总表
- 使用缓存
- 优化关联查询
- 确定ON或者USING子句的列上有索引
- 却表GROUT BY和ORDER BY中只有一个表中的列,这样MySQL才有可能使用索引
- 优化子查询(嵌套查询)
- 尽可能使用关联查询来替代
- 优化GROUT BY和DISTINCT
- 这两种查询均可使用索引来优化,是最有效的优化方法
- 关联查询中,使用标识列进行分组的效率会更高
- 如果不需要ORDER BY,进行GROUP BY时使用ORDER BY NULL,MySQL不会再进行文件排序
- WITH ROLLUP超级聚合,可以挪到应用程序处理
- 优化LIMIT分页
- LIMIT偏移量大的时候,查询效率低
- 可以记录上一次查询的最大ID,下次查询时直接根据该ID来查询
- 优化UNION查询
- UNION ALL的效率高于UNION
解题方法:对于此类考题,先说明如何定位低效SQL语句,然后根据SQL语句可能低效的原因做排查,先从索引着手,如果索引没有问题,考虑以上几个方面,数据访问的问题,长难查询句的问题还是一些特定类型优化的问题,逐一回答。
十八、MySQL高可扩展和高可用
真题:简述MySQL分表操作和分区的工作原理,分别说说分区和分表的使用场景和各自优缺点
- 分区表的原理
- 工作原理:对用户而言,分区表是一个独立的逻辑表,但是底层MySQL将其分成多个物理子表,这对用户来说是透明的,每一个分区表都会使用一个独立的表文件
- 创建表时使用partition by子句定义每一个分区存放的数据,执行查询时,优化器会根据分区定义过滤那些没有我们需要数据的分区,这样查询只需要查询所需数据在的分区即可
- 分区的主要目的是将数据按照一个较粗的粒度分在不同的表中,这样可以将相关的数据存放在一起,而且如果想一次性删除整个分区的数据也很方便
- 分区表适用场景
- 表非常大,无法全部存在内存,或者只在表的最后有热点数据,其他都是历史数据
- 分区表的数据更易维护,可以对独立的分区进行独立的操作
- 分区表的数据可以分布在不同的机器上,从而高效使用资源
- 可以使用分区表来避免某些特殊的瓶颈
- 可以备份和回复独立的分区
- 分区表限制
- 一个表最多只能有1024个分区
- 5.1版本中,分区表表达式必须是整数,5.5可以使用列分区
- 分区字段中如果有主键和唯一索引列,那么主键列和唯一列都必须包含进来
- 分区表中无法使用外键约束
- 需要对现有表结构进行修改
- 所有分区都必须使用相同的存储引擎
- 分区函数中可以使用的函数和表达式会有一些限制
- 某些存储引擎不支持分区(InnoDB、MyISAM支持)
- 对于MyISAM的分区表,不能使用 load index into cache(把索引放到缓存中)
- 对于MyISAM表,使用分区表时需要打开更多的文件描述符
- 分库分表的原理
- 工作原理:通过一些HASH算法或者工具实现将一张数据表垂直或者水平进行物理切分
- 分表方式
- 水平分割:表很大,分割后可以降低在查询时需要读的数据和索引的页数,同时也降低了索引的层数,提高查询速度
- 缺点:
- 给应用增加复杂度,通常查询时需要多个表名,查询所有数据都需要UNION操作
- 在许多数据库应用中,这种复杂性会超过它带来的优点,查询时会增加读一个索引层的磁盘次数
- 使用场景
- 单表记录条数达到百万到千万级别时
- 解决表锁问题
- 垂直分表:把主键和一些列放在一个表,然后把主键和另外的列放在另一个表中
- 使用场景:
- 如果一个表中某些列常用,而另外一些列不常用
- 可以使数据行变小,一个数据页能存储更多数据,查询时减少I/O次数
- 缺点
- 管理冗余列,查询所有数据需要JOIN操作
- 分表缺点
- 有些分表的策略基于应用层的逻辑算法,一旦逻辑算法改变,整个分表逻辑都会改变,扩展性较差
- 对于应用层来说,逻辑算法无疑增加开发成本
- MySQL的复制原理及负载均衡
- MySQL主从复制工作原理:
- 在主库上把数据更改记录到二进制日志
- 从库将主库的日志复制到自己的中继日志
- 从库读取中继日志中的事件,将其重放到从库数据中
-
- MySQL主从复制解决的问题
- 数据分布:随意停止或开始复制,并在不同地理位置分布数据备份
- 均在均衡:降低单个服务器的压力
- 高可用和故障切换:帮助应用程序避免单点失败
- 升级测试:可以使用更高版本的MySQL作为从库
- 解题方法:充分掌握分区和分表的工作原理和适用场景,在面试中,此类题通常比较灵活,会给一些现有公司遇到问题的场景,大家可以根据分区和分表以及MySQL复制,负载均衡的适用场景来根据情况进行回答。
真题:设定网站的用户数量在千万级,但是活跃用户的数量只有1%,如何通过优化数据库提高活跃用户的访问速度
十九、MySQL安全性
真题:SQL语句应该考虑哪些安全性
- SQL查询的安全方案
- 使用预处理语句防SQL注入
- 写入数据库的数据要进行特殊字符的转义
- 查询错误信息不要返回给用户,将错误记录到日志
- 注意:PHP端尽量使用PDO对数据库进行相关操作,PDO拥有对预处理语句很好的支持的方法,MySQLi也有,但是可扩展性不如PDO,效率略高于PDO,MySQL函数在新版本中已经趋向淘汰,所以不建议使用,而且它没有很好的支持预处理方法
- 延伸:MySQL的其他安全设置
- 定期做数据备份
- 不给查询用户root权限,合理分配权限
- 关闭远程访问数据库权限
- 修改root口令,不用默认口令,使用较复杂的口令
- 删除多余的用户
- 改变root用户的名称
- 限制一般用户浏览其他库
- 限制用户对数据文件的访问权限
解题方法:SQL安全的考点都在防SQL注入的问题,因此只要遇到此类考点,优先考虑SQL注入的防护手段
真题:为什么使用PDO和MySQLi连接数据库会比mysql函数库更加安全
二十、程序设计题
真题:编写一个在线留言本,实现用户的在线留言功能,留言信息存储到数据库,要求设计数据表内容以及使用php编码完成?
- 数据表设计
- 分析数据接口
- 留言板有哪些信息需要存储
- 留言信息:ID、留言标题、留言内容、留言时间、留言人
- 数据表创建语句
- 选择PHP连接数据库的方式
- 编码能力
二十一、MVC框架基本工作原理
真题:谈谈你对MVC的认识,介绍几种目前比较流行的MVC框架?
- MVC工作原理
- 工作原理
- Model:模型层,数据操作
- View:视图层,交互界面
- Controller:控制层,业务逻辑
- 常见MVC框架
- ThinkPHP
- Yii2
- CI
- Yaf
- PhaLcon
- 延伸:单一入口的工作原理
- 用一个处理程序文件处理所有的HTTP请求,根据请求时的参数的不同区分不同模块和操作的请求
- 优势:可以进行统一的安全性检查,集中处理程序
- 劣势:URL不美观,使用URL重写机制,处理效率会低一些
- 延伸:模板引擎的理解
- PHP是一种HTML内嵌式的在服务器端执行的脚本语言,但是PHP有很多可以使用的PHP代码和HTML代码分开的模板引擎,例如:Smarty、Twig、Haml、Liquid等
- 工作原理:模板引擎就是庞大的完善的正则表达式替换库
解题方法:
- 牢记MVC思想内容,至少能背出来
- 尝试使用PHP的一个MVC框架完成一个小功能,例如:简单的用户管理系统,完成用户的增删改查即可
- 参考《Yii2框架打造完美电商平台》
真题:框架中申码是单一入口多入口,单一入口的优缺点?
二十二、常见框架特性
真题:PHP框架有哪些,你用过哪些?各自的优缺点是什么?
- PHP框架的差异和优缺点
- Yaf框架
- Yar使用PHP扩展的形式写的一个PHP框架,也就是以C语言为底层编写的,性能上要比PHP代码写的框架快一个数量级
- 有点:执行效率高、轻量级框架、可扩展性强
- 缺点:高版本兼容性差、底层代码可读性差、需要安装扩展、功能单一、开发需要编写大量的插件
- Yii2框架
- Yii2是一款非常优秀的通用Web后端框架,结构简单优雅、实用功能丰富、扩展性强、性能高是它最突出的优点
- 缺点:学习成本较高
- 相比Yaf,量级较重
- 常见框架特性:根据自己框架的使用经验,理解所使用框架的特性,例如Yii2框架的路由配置
解题方法:此类题目中,说明的最好是自己真正用过的框架,最好结合自己的简历,简历当中提到的框架,要着重准备,可以说说在开发过程中,你使用该框架最让你印象深刻的功能和特性,最好多进行框架间对比,展现你对PHP框架的理解,侧面说明你的开发经验
真题:Yii2框架如何实现数据的自动验证?
二十三、常见算法
真题:请写出常见的排序算法,并用PHP实现冒泡排序,将数据按照从小到大的方式进行排序?
- 冒泡排序原理和实现
- 延伸:算法的概念
- 1+2+3+4+……+n的值是多少
- 解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作
- 一个问题可以有多种算法,每种算法都有不同的效率
- 一个算法具有五个特征:有穷性、确切性、输入项、输出项、可行性
- 延伸:时间复杂度和空间复杂度的概念
- 算法评定
- 算法分析的目的在于选择合适算法和改进算法
- 一个算法的评价主要从时间复杂度和空间复杂度来考虑
- 时间复杂度
- 执行算法所需要的计算工作量
- 一般来说,计算机算法是问题规模 n 的函数f(n),算法的时间复杂度也因此记做T(n) = O(f(n))
- 问题的规模n越大,算法执行的时间的增长率f(n)的增长率正相关,称作渐进时间复杂度(Asymptotic Time Complexity)
- 时间复杂度计算方式:O(n^2)、O(1)、O(n)
- 得出算法的计算次数公式
- 用常数1来取代所有时间中的所有加法常数
- 在修改后的运行次数函数中,只保留最高阶项
- 如果最高阶存在且不是1, 则去除与这个项相乘的常数
- 举例
- 常数阶:O(1)
- 线性阶:O(n)
- 平(立)方阶:O(n^2)/O(n^3)
- 特殊平方阶:O(n^2/2+n/2)->O(n^2)
- 对数阶:O(log2n)
- 常见时间复杂度:常数阶、线性阶、平方阶、立方阶、对数阶、nlog2n阶、指数阶
- 效率关系:O(1)>O(log2n)>O(n)>O(nlog2n)>O(n^2)>O(n^3)>O(2^n)>O(n!)>O(n^n)
- 时间复杂度其他概念
- 最坏情况:最坏情况的运行时间,一种保证,如果没有特别说明,说的时间复杂度即为最坏情况的时间复杂度
- 平均情况:期望的运行时间
- 空间复杂度:
- 算法需要消耗内存空间,记作S(n) = O(f(n))
- 包括程序代码所占用的空间,输入数据所占用的空间和辅助变量所占用的空间这三个方面
- 计算和表示方法与时间复杂度类似,一般用复杂度的渐近性来表示
- 空间复杂度计算方式
- 有时用空间换取时间
- 冒泡排序的元素交换,空间复杂度O(1)
- 常见排序算法
- 冒泡排序、直接插入排序、希尔排序、选择排序、快速排序、堆排序、归并排序
-
- 冒泡排序
- 原理:两两相邻的数进行比较,如果反序就交换,否则不交换
- 时间复杂度:最坏(O(n^2)),平均(O(n^2))
- 空间复杂度:O(1)
- 直接插入排序
- 原理:每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序
- 时间复杂度:最坏(O(n^2)),平均(O(n^2))
- 空间复杂度:O(1)
- 希尔排序
- 原理:把待排序的数据根据增量分成几个子序列,对子序列进行插入排序,直接增量为1,直接进行插入排序,增量的排序,一般是数组的长度的一半,再变为原来增量的一半,直到增量为1
- 时间复杂度:最差(O(n^2)),平均(O(n*log2n))
- 空间复杂度:O(1)
- 选择排序
- 原理:每次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完
- 时间复杂度:最坏(O(n^2)),平均(O(n^2))
- 空间复杂度:O(1)
- 快速排序
- 原理:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按照此方法对这两部分数据分别进行快速排序,整个排序过程可以递归完成
- 时间复杂度:最差(O(n^2)),平均(O(nlog2n))
- 空间复杂度:最差(O(n)),平均(O(log2n))
- 堆排序
- 把待排序的元素按照大小再二叉树位置上排列,排序好的元素要满足:父节点的元素要大于等于子节点;这个过程叫做堆化过程,如果根节点存放的是最大的数,则叫做大根堆,如果是最小,就叫小根堆,可以把根节点拿出来,然后再堆化,循环到最后一个节点
- 时间复杂度:最差(O(nlog2n)),平均(O(nlog2n))
- 空间复杂度:O(1)
- 归并排序
- 原理:将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个有序的子序列,再把有序的子序列合并为整体有序序列
- 时间复杂度:最差(O(nlog2n)),平均(O(nlog2n))
- 空间复杂度:O(n)
- 总结:快速排序、归并排序的理想时间复杂度都是O(nlog2n),但是快速排序的时间复杂度并不稳定,最坏情况下复杂度为O(n^2),所以最理想的算法还是归并排序
- 常见查找算法
- 二分查找
- 原理:从数组的中间元素开始,如果中间元素正好是要查找的元素,搜索结束,如果某一个特定元素大于或者小于中间元素,则在数组大于或者小于中间元素的那一半中查找,而且跟开始从中间开始比较,如果某一步骤为空,代表找不到。
- 时间复杂度:最差(O(log2n)),平均(O(log2n))
- 空间复杂度:迭代(O(1)),递归(O(log2n))
-
- 顺序查找
- 原理:按照一定的顺序检查数组中的每一个元素,直到找到所要寻找的特定值为止
- 时间复杂度:最差(O(n)),平均(O(n))
- 空间复杂度:O(1)
- 总结:二分查找算法的时间复杂度最差是O(log2n),顺序查找的时间复杂度最差为O(n),所以二分查找更快,但是递归情况下,二分查找更消耗内容,时间复杂度为O(log2n)
解题方法:此类考点非常重要也较为复杂,需要考生充分理解各种排序算法和查找算法的原理以及实现方式,另外还需要理解时间复杂度和空间复杂度的计算方式和概念,此类考点毋庸置疑是考察考生的逻辑思维能力,因此需要大家仔细研究各种算法的实现方式
真题1:请简述时间复杂度和空间复杂度概念?
答:描述算法时间的消耗量,空间复杂度是内存的使用量
真题2:对无序数据排序,最优的时间复杂度是什么,用PHP或者JavaScrpit写出一个实际的例子(如了解,给出算法的名称),该算法的空间复杂度是什么?
答:归并排序 > 快速排序
真题3:一个有序数组中,查询特定item是否存在的最优算法是什么,时间复杂度是什么?
答:二分法查找,时间复杂度O(log2n)
二十四、数据结构
真题:请描述一下数据结构的特征(stack、heap、list、doubly-linked-list、queue、array(vector))?
- Array
- 数组,最简单而且应用最广泛的数据结构之一
- 特性:使用连续的内存来存储、数组中的所有元素必须是相同的类型或类型的衍生(同质数据结构)、元素可以通过下标直接访问
- LinkedList
- 链表:线性表的一种,最基本、最简单、也是最常用的数据结构
- 特性:元素之间的关系是一对一的关系,(除了第一个和最后一个元素,其他元素都是首尾相接)、顺序存储结构和链式存储结构两种存储方式
- Stack
- 栈:和队列相似,一个带有数据存储特性的数据结构
- 特性:存储数据是先进后出的,栈只有一个出口,只能从栈顶部增加和移除元素
- Heap
- 堆:一般情况下,堆叫二叉堆,近似完全二叉树的数据机构
- 特性:子节点的键值或者索引总是小于它的父节点、每个节点的左右子树又是一个二叉堆、根节点最大的堆叫最大堆或者大根堆、最小的叫最小堆或者小根堆
- list
- 线性表:由零个或多个数据元素组成的有限序列
- 特性:线性表是一个序列、0个元素构成的。线性表是空表、第一个元素无先驱、最后一个元素无后继、其他元素都只有一个先驱和后继、有长度,长度是元素个数,长度有限
- doubly-linked-list
- 双向链表
- 特性:每个元素都是一个对象,每个对象有一个关键字key和两个指针(next和prev)
- queue
- 队列
- 特性:先进先出(FIFO),并发中使用、可以安全将对象从一个任务传给另一个任务
- set
- 集合
- 特性:保存不重复元素
- map
- 字典
- 特性:关联数组、也被叫做字典或者键值对
- graph
- 图
- 图:通常使用邻接矩阵和邻接表表示,前者易实现但是对于稀疏矩阵会浪费较多空间、后者使用链表的方式存储信息但是对于图搜索时间复杂度较高
解题方法:先理解每种数据结构的特征,有时会遇到让用php代码实现其中的数据结构,遇到此类题,先要理解该种数据结构的特性是什么,然后再实现,通常可以使用PHP的数组来模拟
真题:用PHP实现一个双向队列?
二十五、其他逻辑算法
真题:1,1,2,3,5,8,13,21,32……求第30位的数是多少,请用伪代码描述其实现方法?
解题方法:找要处理的数组或者字符串的规律,也可能是一个数列的规律,如果要求使用伪代码实现,建议先说明规律,然后白话说明解题步骤即可。
真题:请写一个函数,实现以下功能:字符串“open_door”转换成“OpenDoor”、“make_by_id”转换成“MakeById”?
二十六、模拟内置函数
真题:不使用PHP函数,用方法写要给反转字符串的函数
//实现字符串反转
function str_rev($str){
for($i = 0; true; $i++){
if(!isset($str[$i])){
break;
}
}
$result = '';
for($j = $i - 1; $j > 0; $j--){
$result .= $str[$j];
}
echo $result;
}
str_rev("abcdefg");
- 逻辑思维能力
- 对PHP内置函数的熟悉程度
- 字符串和数组的处理能力
- 此类考点会分为两类,一类是直接说明让考生实现什么样的操作,一类是让考生实现某函数的功能,因此不仅需要考生具备一定的数据处理能力,还要熟悉PHP内置函数的功能是什么,常见的处理主要对字符串和数组的处理,因此建议大家多学习PHP字符串和数组的内置函数,尝试模拟其功能,有助于逻辑思维和熟悉函数功能
真题:写一个函数,要求不使用array_merge完成多个数组的合并
//array_merge_my(arr1, arr2, arr3, ......);
function array_mer(){
$result = '';
$arrs = func_get_args();
foreach($arrs as $arr){
if(is_array($arr)){
foreach($arr as $val){
$result .= $val;
}
}
}
return $result;
}
print_r(array_mer([1], [1, 2], ['a', 'b']));
二十七、高并发和大流量解决方案考点
真题:PHP如何解决网站大流量和高并发的问题?
- 高并发架构相关概念:QPS、并发连接数、吞吐量、高并发代表什么、QPS如何进行测试
- 并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行
-
- 我们说的高并发是什么?
- 上面的定义明显不是我们通常所言的并发,在互联网时代,所讲的并发、高并发,通常是指并发访问。也就是在某个时间点,有多少个访问同时到来。
- 通常如果一个系统的日PV在千万以上,有可能是一个高并发的系统
- 有的公司完全不走技术路线,全靠机器堆,这不在我们的讨论范围
- 高并发的问题,我们具体该关心什么?
- QPS:每秒钟请求或者查询的数量,在互联网领域,指每秒响应请求数(指HTTP请求)
- 吞吐量:单位时间内处理的请求数量(通常由QPS与并发数决定)
- 响应时间:从请求发出到收到响应花费的时间。例如系统处理一个HTTP请求需要100ms,这个100ms就是系统的响应时间
- PV:综合浏览量(Page View),即页面浏览量或者点击量,一个访客在24小时内访问的页面数量,同一个人浏览你的网站同一页面,只记作一次PV
- UV:独立访客(UniQue Visitor),即一定时间范围内相同访客多次访问网站,只计算为1个独立访客
- 带宽:计算带宽大小需关注两个指标,峰值流量和页面的平均大小
- 日网站带宽 = PV / 统计时间(换算到秒) * 评卷页面大小(单位KB) * 8
- 峰值一般是平均值的倍数,根据实际情况来定
- QPS不等于并发连接数
- QPS是每秒HTTP请求数量,并发连接数是系统同时处理的请求数量
- (总PV数 * 80%) / (6小时秒数 * 20%) = 峰值每秒请求数(QPS),80%的访问量集中在 20%的时间
- 压力测试,测试能承受的最大并发,测试最大承受QPS值
- 常用性能测试工具
- ab、wrk、http_load、Web Bench、Siege、Apache JMeter
- ab
- 全程时apahce benchmark,是apache官方退出的工具
- 创建多个并发访问线程,模拟多个访问者同时对某一URL地址进行访问。它的测试目标是基于URL的,因此,它既可以用来测试apahce的负载压力,也可以测试nginx、lighthttp、tomcat、IIS等其他Web服务器的压力
- ab的使用
- 模拟并发请求100次,总共请求5000次
- ab -c 100 -n 5000 待测试网站
- ab注意事项
- 测试机器与被测试机器分开
- 不要对线上服务做压力测试
- 观察测试工具ab所在机器,以及被测试的前端机的CPU,内存,网络等都不超过最高限度的75%
- QPS达到极限
- 随着QPS的增长,每个阶段需要根据实际情况来进行优化,优化的方案也与硬件条件、网络带宽息息相关。
-
- QPS达到50:
- 可以称之为小型网站,一般的服务器就可以应付
- QPS达到100:
- 假设关系型数据库的每次请求在0.01秒完成
- 假设单个页面只有一个SQL查询,那么100QPS意味着1秒钟完成100次请求,但是此时我们并不能保证数据库查询能完成100次
- 方案:数据库缓存层,数据库的负载均衡
- QPS达到800:
- 假设我们使用百兆带宽,意味着网站出口的实际带宽是8M左右
- 假设每个页面只有10K,在这个并发条件下,百兆带宽已经吃完
- 方案:CDN加速、负载均衡
- QPS达到1000:
- 假设使用Memcache缓存数据库查询数据
- 每个页面对Memcache的请求远大于直接对DB的请求
- Memcache的悲观并发数在2W左右,但有可能在之前内网带宽已经吃光,表现出不稳定
- 方案:静态HTML缓存
- QPS达到2000
- 这个级别下,文件系统访问锁都成了灾难
- 方案:作业务分离,分布式存储
- 高并发解决方案案例
- 优化流量
- 防盗链处理
- 前端优化
- 减少HTTP请求
- 添加异步请求
- 启用浏览器缓存和文件压缩
- CDN加速
- 建立独立图片服务器
- 服务端优化
- 页面静态化
- 并发处理
- 队列处理
- 数据库优化
- 数据库缓存
- 分库分表,分区操作
- 读写分离
- 负载均衡
- Web服务器优化
- 负载均衡(nginx反向代理)
二十八、Web资源防盗链
- 什么是防盗链
- 盗链概念:盗链是指在自己的页面上展示一些并不在自己的服务器上的内容
- 获得他人服务器上的资源地址,绕过别人的资源展示页面,直接在自己的页面上向最终用户提供此内容
- 常见的是小站盗用大站的图片、音乐、视频、软件等资源
- 通过盗链的方法可以减轻自己服务器的负担,因为真实的空间和流量均是来自别人的服务器
- 防盗链概念:防止别人通过一些技术手段绕过本站的资源展示页面,盗用本站的资源,让绕开本站资源展示页面的资源链接失效
- 可以大大减轻服务器及带宽的压力
- 防盗链的工作原理:通过Refrer或者签名,网站可以检测目标网页访问的来源网页,如果是资源文件,则可以追踪到显示它的网页地址,一旦检测到来源不是本站即进行阻止或者返回指定的页面
- 通过计算签名的方式,判断请求是否合法,如果合法则显示,否则返回错误信息
- 防盗链的实现方法
- Referer
- Nginx模块 ngx_http_referer_module用于阻挡来源非法的域名请求
- Nginx指令valid_referers,全局变量$invalid_referer
- valid_referers none(判断referer是否为空) | blocked (代理或防火墙)| server_names(信任的地址,可用正则匹配) | string ...
- none:“Referer”来源头部为空的情况
- blocked:“Referer”来源头部不为空,但是里面的值被代理或者防火墙删除了,这些值都不以http://或者https://开头
- server_names:“Referer”来源头部包含当前的server_names
- 传统防盗链遇到的问题
- 伪造Referer:可以使用加密签名解决
- 加密签名
- 使用第三方模块HttpAccessKeyModule实现Nginx防盗链
- accesskey on | off 模块开关
- accesskey_hashmethod md5 | sha-1 签名加密方式
- accesskey_arg GET 参数名称
- accesskey_signature 加密规则
二十九、减少HTTP的请求次数
- 为什么要减少HTTP请求
- 性能黄金法则:只有10%--20%的最终用户响应时间花在接收请求的HTML文档上,剩下的80%--90%时间花在HTML文档所引用的所有组件(图片,script,css,flash等等)进行的HTTP请求上
- 如何改善:
- 改善响应时间的最简单途径就是减少组件数量,并由此减少HTTP请求的数量
- HTTP连接产生的开销
- 域名解析--TCP连接--发送请求--等待--下载资源--解析时间
- 疑问:
- DNS缓存
- Keep-Alive
- 打破谣言
- 查找DNS缓存也需要时间,多个缓存就要查找多次有可能缓存被清除
- HTTP1.1协议规定请求只能串行发送,也就是说一百个请求必须依次逐个发送,前面的一个请求完成才能开始下个请求
- 减少HTTP请求的方式
- 图片地图:图片地址允许你在一个图片上关联多个URL,目标URL的选择取决于用户单击了图片上的哪个未知
- 我们可以通过使用五个分开的图片,然后每个图片对应一个超链接,产生5个HTTP请求,我们的目标是减少HTTP请求
- 解决上面的办法:将五张图片合并为一张图片,然后以位置信息定位超链接,把HTTP请求减少为一个,可以保证设计的完整性和功能的齐全性
- 使用<map><area></area><map>标签
- CSS Sprites:CSS Sprites中文翻译为CSS精灵,通过使用合并图片,通过指定css的background-image 和 background-position来显示元素
- background-position属性:background-position:x y; x和y可以写负值,我们可以想象图片的左上方为(0,0),以(0,0)坐标向右是为负数的x轴,以(0,0)坐标向下是为负数的y轴
- 性能影响:图片地图与CSS精灵的响应时间基本相同,但比使用各自独立图片的方式要快50%以上
- 合并脚本和样式表
- 使用外部的js和css文件引用的方式,因为这要比直接写在页面中性能更好一点
- 独立的一个js比用多个js文件组成的页面载入要快38%
- 把多个脚本合并为一个脚本,把多个样式表合并为一个样式表
- 图片使用Base64编码减少页面请求数:采用Base64的编码方式将图片直接嵌入到网页中,而不是从外部载入
三十、浏览器缓存和数据压缩
- HTTP缓存机制
- 高并发下只能通过提升服务器负载解决?
- 缓存只能做数据库缓存吗?
- 启用浏览器缓存
- 缓存分类:HTTP缓存模型中,如果请求成功会有三种请款
- 200 from cache:直接从本地缓存中获取响应,最快速,最省流量,因为根本没有向服务器发送请求
- 304 Not Modified:协商缓存,浏览器再本地没有命中的情况下请求头中发送一定的校验数据到服务端,如果服务端数据没有改变浏览器从本地缓存响应,返回304,快速,发送的数据很少。只返回一些基本的响应头信息,数据量很小,不发送实际响应体
- 200 OK:以上两种缓存全部失败,服务器返回完整响应,没有用到缓存,相对最慢
- 本地缓存
- 相关Header
- Pragma:HTTP1.0时代的遗留产物,该字段被设置为no-cache时,会告知浏览器禁用本地缓存,即每次都向服务器发送请求
- Expires:HTTP1.0时代用来启用本地缓存的字段,expires值对应一个形如Thu,32 Dec 2037 23:55:55 GMT的格林威治时间,告诉浏览器缓存实现的时刻,如果还没到该时刻,标明缓存有效,无需发送请求,浏览器与服务器的时间无法保持一致,如果时间差距大,就会影响缓存结果
- Cache-Control(设置缓存秒数):HTTP1.1针对Expires时间不一致的解决方案,运用Cache-Control告知浏览器缓存过期的时间间隔不是时刻,即使具体时间不一致,也不影响缓存的管理
- no-store:禁止浏览器缓存响应
- no-cache:不允许直接使用本地缓存,先发起请求和服务器协商
- max-age=delta-seconds:告知浏览器该响应本地缓存有效的最长期限,以秒为单位
- 优先级:Pragma > Cache-Control > Expires
- 协商缓存:当浏览器没有命中本地缓存,如本地缓存过期或者响应中声明不允许直接使用本地缓存,那么浏览器肯定会发起服务端请求,服务端会验证数据是否修改,如果没有通知浏览器使用本地缓存
- 相关Header
- Last-Modified:通知浏览器资源的最后修改时间
- If-Modified-Since:得到资源的最后修改时间后,会将这个信息通过If-Modified-Sinc提交到服务器做检查,如果没有修改,返回304状态码
- Etag:HTTP1.1推出,文件的指纹标识符,如果文件内容修改,指纹会改变
- If-None-Match:本地缓存失效,会携带此值去请求服务端,服务端判断该资源是否改变,如果没有改变,直接使用本地缓存,返回304
- 适合缓存的内容
- 不变的图像,如logo,图标等
- js、CSS静态文件
- 可下载的内容,媒体文件
- 建议使用协商缓存
- HTML文件
- 经常替换的图片
- 经常修改的js、css文件
- js、css文件的加载可以加入文件的签名来拒绝缓存
- index.css?签名
- 不建议缓存的内容
- 用户隐私等敏感数据
- 经常改变的api数据接口
- Nginx配置缓存策略
- 本地缓存配置
- add_header命令,添加状态码为2xx和3xx的响应头
- add_header name value[always];
- 可以设置Pragma/Expires/Cache-Control,可以继承
- expires指令(设置缓存时间):通过浏览器过期时长
- expires time;
- 为负值表示 Cache-Control:no-cache
- 当为正或者0时,就表示Cache-Control:max-age = 指定的时间
- 当设置为max时,会把Expires设置为 “Thu, 31 Dec 2037 23:55:55 GMT”, Cache-Control 设置到10年;
- 协商缓存
- Etag指令:指定签名
- etag on | off; 默认是on
- 前端代码和资源的压缩
- 优势:让资源文件更小,加快文件再网络中的传输,让网页更快的展现,降低带宽和流量开销
- 压缩方式:JS、CSS、图片、HTML代码的压缩、Gzip压缩
- JavaScript代码压缩
- JavaScript压缩的原理一般是去掉多余的空格和回车、替换长变量名、简化一些代码写法等
- JavaScript代码压缩工具很多,有在线工具,有应用程序,有编辑器插件
- 常用压缩工具:UglifyJs、YUI Compressor(来自Yahoo、只有压缩功能)、CIosure Compiler(来自Goole)
- CSS代码压
- 原理跟JavaScript压缩原理类似,同样是去除空白符、注释并且优化一些CSS语义规则等
- 常见工具:YUI Compressor、CSS Compressor
- CSS Compressor:压缩时可以选择模式
三十一、CDN加速
- 什么是CDN
- CDN的全程是Content Delivery Network,即内容分发网络
- 尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定
- 在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层只能虚拟网络
- CDN系统能够实时地根据网络流量和各个节点地连接、负载状况以及到用户地距离和响应时间等综合信息将用户地请求重新导向离用户最近地服务节点上
- 使用CDN的优势
- 本地Cache加速,提高了企业站点(尤其含有大量图片和静态页面站点)的访问速度
- 跨运营商的网络加速,保证不同网络的用户都得到良好的访问质量
- 远程访问用户根据DNS负载均衡技术智能自动选择cache服务器
- 自动生成服务器的远程Mirror(镜像)cache服务器,远程用户访问时从cache服务器上读取数据,减少远程访问的带宽、分担网络流量、减轻原站点WEB服务器负载等功能
- 广泛分布的CDN节点加上节点之间的智能冗余机制,可以有效地预防黑客入侵
- CDN的工作原理
- 传统访问:用户在浏览器输入域名发起请求-->解析域名获取服务器IP地址-->根据IP地址找到对应地服务器-->服务器响应并返回数据
- 适用CDN访问:用户发起请求-->智能DNS的解析(根据IP判断地理位置、接入网类型、选择路由最短和负载最轻的服务器)-->取得缓存服务器IP-->把内容返回给用户(如果缓存中有)-->向源站发起请求-->将结果返回给用户-->将结果存入缓存服务器
- CDN的适用场景
- 站点或者应用中大量静态资源的加速分发:例如,CSS,JS,图片,和HTML
- 大文件下载
- 直播网站等
- CDN的实现
- BAT等都有提供CDN服务
- 可用LVS做4层负载均衡
- 可用Nginx,Varnish,Squid,Apache TrafficServer做7层负载均衡和apache
- 适用squid反向代理,或者Nginx等的反向代理
三十二、独立图片服务器的部署
- 独立的必要性
- 分担Web服务器的I/O负载,将耗费资源的图片服务分离出来,提高服务器的性能和稳定性
- 能够专门对图片服务器进行优化,为图片服务器设置有针对性的缓存方案,减少带宽成本,提高访问速度
- 提高网站的可扩展性,通过增加图片服务器,增加图片吞吐能力
- 采用独立域名
- 原因:
- 同一域名下浏览器的并发连接数(一般1--6个)有限制,突破浏览器连接数的限制
- 由于cookie的原因,对缓存不利,大部分Web cache都只缓存不带cookie的请求,导致每次的图片请求都不能命中cache
- 独立后的问题
- 如何进行图片上传和图片同步
- NFS共享方式
- 利用FTP同步
三十三、动态语言静态化
- 什么是动态语言静态化:将现有PHP等动态语言的逻辑代码生成为静态的HTML文件,用户访问动态脚本重定向到静态HTML文件的过程
- 适用对实时性要求不高的页面
- 为什么要静态化
- 动态脚本通常会做逻辑计算和数据查询,访问量大,服务器压力越大
- 访问量大时可能会造成CPU负载过高,数据库服务器压力过大
- 静态化可以减低逻辑处理压力,降低数据库服务器查询压力
- 静态化的实现方式
- 使用模板引擎
- 可以时用Smart的缓存机制生成静态HTML缓存文件
- $smart->cache_dir = $ROOT."/cache";//缓存目录
- $smart->caching = true;//是否开启缓存
- $smart->cache_lifetime = "3600";//缓存时间
- $smart->display(string template[, string cache_id [, string compile_id]])
- $smart->clear_all_cache();//清除所有缓存
- $smart->clear_cache('file.html');//清除指定的缓存
- $smart->clear_cache('article.html', $art_id);//清除同一个模板下的指定缓存号的缓存
- 利用ob系列函数
- ob_start():打开输出控制缓冲
- ob_get_contents():返回输出缓冲区内容
- ob_clean():清空输出缓冲区
- ob_end_flush():冲刷出(送出)输出缓冲区内容并关闭缓冲
- 可以判断文件的inode修改时间,判断是否过期使用filectime函数
三十四、动态语言层的并发处理
- 什么是进程、线程、协程
- 进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程是一个“执行中的程序”。
- 进程的三态模型:多道程序系统中,进程在处理器上交替运行,状态不断地发生变化
- 运行、就绪、阻塞
- 运行:当一个进程在处理机上运行时,则称该进程处于运行状态。处于此状态的进程的数目小于等于处理器的数目,对于单处理机系统,处于运行状态的进程只有一个,在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。
- 就绪:当一个进程获得了除处理机以外的一切所有资源,一旦得到处理机即可运行,则称此进程处于就绪状态。就绪进程可以按多个优先级来划分队列。例如:当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。
- 阻塞:也成为等待或睡眠状态,一个进程正在等待某一事件发生(例如:请求I/O而等待I/O完成等)而暂时停止运行,这时即使把处理机分配给进程也无法运行,故称该进程处于阻塞状态。
- 进程的五态模型:对于一个实际的系统,进程的状态及其转换更为复杂
- 新建态、活跃就绪/静止就绪、运行、活跃阻塞/静止阻塞、终止态
- 新建态:对应于进程刚刚被创建时没有被提交的状态,并等待系统完成创建进程的所有必要信息
- 终止态:进程已结束运行,回收除进程控制块之外的其他资源,并让其他进程从进程控制块中收集有关信息
- 活跃就绪:是指进程在主内存并且可被调度的状态
- 静止就绪(挂起就绪):是指进程被对换到辅存时的就绪状态,是不能被直接调度的状态,只有当主存中没有活跃就绪态进程,或者是挂起就绪态进程具有更高的优先级,系统将把挂起就绪态进程调回主存并转换为活跃就绪
- 活跃阻塞:是指进程已在主存,一旦等待的事件产生变进入活跃就绪状态
- 静止阻塞:进程对换到辅存时的阻塞状态,一旦等待的事件产生便进入静止就绪状态
- 由于用户的并发请求,为每一个请求都创建一个进程显然是行不通的,从系统资源开销方面或是响应用户请求的效率方面来看。因此操作系统中的线程的概念便被引进了
- 线程,有时被称为轻量级进程(Lightweight Process,LWP)是程序执行流的最小单元
- 线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。
- 一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行
- 线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元、是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位
- 在单个程序中同时运行多个线程完成不同的工作,称为多线程
- 每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身
- 线程的状态:就绪、阻塞、运行
- 就绪状态:线程具备运行的所有条件,逻辑上可以运行,在等待处理机
- 运行状态:线程占有处理机正在运行。
- 阻塞状态:线程在等待一个事件(如某个信号量),逻辑上不可执行
- 协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常块
- 线程与进程的区别
- 线程是进程内的一个执行单元,进程内至少呦一个线程,它们共享进程的地址弓箭,而进程有自己独立的地址空间
- 进程十资源分配和拥有的单位,同一个进程内的线程共享进程的资源
- 线程是处理器调度的基本单位,但进程不是
- 二者均可并发执行
- 每个独立的线程有一个程序运行的入口,顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
- 线程与协程的区别
- 一个线程可以多个协程,一个进程也可以单独拥有多个协程
- 线程进程都是同步机制,而协程则是异步
- 协程能保留上一次调用的状态,每次过程重入时,就相当于进入上一次调用的状态
- 什么是多进程、多线程
- 多进程:同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这就是多进程
- 边看慕课网视频,边使用WebIDE
- 多开一个进程,多分配一份资源,进程间通讯不方便
- 多线程:线程就是把一个进程分为很多片,每一片都可以是一个独立的流程,与多进程的区别是只会使用一个进程的资源,线程间可以直接通信
-
- 例子
- 单进程单线程:一个人在一个桌子上吃菜
- 单进程多线程:多个人在同一个桌子上一起吃菜
- 多进程单线程:多个人每个人在自己的桌子上吃菜
- 同步阻塞模型
- 多进程:最早的服务器端程序都是通过多进程、多线程来解决并发IO的问题
- 一个请求创建一个进程,然后子进程进入循环同步堵塞地与客户端连接进行交互,收发处理数据
- 多线程:用多线程模式实现非常简单,线程中可以直接向某一个客户端连接发送数据
- 步骤:
- 创建一个socket
- 进入while循环,阻塞在进程accept操作上,等待客户端连接进入
- 主进程在多进程模型下通过fork创建子进程
- 多线程模型下可以创建子线程
- 子进程/线程创建成功后进入while循环,阻塞在recv调用上,等待客户端向服务器发送数据
- 收到数据后服务器程序进行处理然后使用send向客户端发送响应
- 当客户端连接关闭时,子进程/线程退出并销毁所有资源。主进程/线程会回收掉此子进程/线程
- 异步非阻塞模型
- 现在各种高并发异步IO的服务器程序都是基于epoll实现的
- IO复用异步非阻塞程序使用经典的Reactor模型,Reactor顾名思义就是反应堆的意思,它本身不处理任何数据收发。只是可以监视一个socket句柄的事件变化
-
- Reactor有4个核心的操作
- add添加socket监听道reactor
- set修改事件监听,可以设置监听的类型,如可读、可写
- del从reactor中移除,不再监听事件
- callback,事件发生后对应的处理逻辑,一般在add/set时制定
- Nginx:多线程Reactor
- Swoole:多线程Reactor + 多进程Worker
- PHP并发编程实践
- PHP的Swoole扩展
- PHP的异步、并行、高性能网络通信引擎,使用纯C语言编写,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步Readis,数据库连接池,Async Task,消息队列,毫秒定时器,异步文件读写,异步DNS查询
- 除了异步IO的支持之外,Swoole为PHP多进程的模式设计了多个并发数据结构和IPC通信机制,可以大大简化多进程并发编程的工作
- 消息队列
- 场景说明:用户注册后,需要发注册邮件和注册短信
- 串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信
- 并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信
- 消息队列方式:将注册信息写入数据库成功后,将成功信息写入队列,此时直接返回成功给用户,写入队列的时间非常短,可以忽略不计,然后异步发送邮件和短信
- 应用解藕
- 场景说明:用户下单后,订单系统需要通知库存系统。假如库存系统无法访问,则订单减库存将失败,从而导致订单失败
- 订单系统与库存系统耦合
- 引用队列:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
- 订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作
- 流量销锋
- 应用场景:秒杀活动,流量瞬间激增,服务器压力大。
- 用户发起请求,服务器接收后,先写入消息队列。假如消息队列长度超过最大值,则直接报错或提示用户
- 后续程序读取消息队列再做处理
- 控制请求量
- 缓解高流量
- 日志处理
- 应用场景:解决大量日志的传输
- 日志采集程序将程序写入消息队列,然后通过日志处理程序的订阅消费日志
- 消息通讯
- 应用场景:聊天室
- 多个客户端订阅同一主题,进行消息发布和接收
- 常见消息队列产品
- Kafka、ActiveMQ、ZeroMQ、RabbitMQ、Redis等
- 接口的并发请求
- curl_multi_init
三十五:数据库缓存
- 什么是数据库缓存
- MySQL等一些常见的关系型数据库的数据都存储在磁盘当中,在高并发场景下,业务应用对MySQL产生的增、删、改、查的操作造成巨大的I/O开销和查询压力,这无疑对数据库和服务器都是一种巨大的压力,为了解决此类问题,缓存数据的概念应运而生
- 极大地解决数据库服务器的压力
- 提高应用数据的响应速度
- 常见的缓存形式:内存缓存(考虑I/O开销),文件缓存
- 为什么要使用缓存
- 缓存数据是为了让客户端很少甚至不访问数据库服务器进行数据的查询,高并发,能最大程度地降低对数据库服务器的访问压力
- 用户请求-->数据查询-->连接数据库服务器并查询数据-->将数据缓存起来(html、内存、json序列化数据)-->显示给客户端
- 用户再次请求或者新用户访问-->数据查询-->直接从缓存中获取数据-->显示给客户端
- 缓存需要考虑的内容
- 缓存方式选择
- 缓存场景的选择
- 缓存数据的实时性
- 缓存数据的稳定性
- 使用MySQL查询缓存
- 启用MySQL查询缓存:
- query_cache_type
- 查询缓存类型,有0、1、2三个取值,0则不使用查询缓存,1表示始终使用查询缓存,2表示按需要使用查询缓存
- query_cache_type为1时,也可关闭查询缓存
- SELECT SQL_NO_CACHE * FROM my_table WHERE condition;
- query_cache_type为2时,可按需使用查询缓存
- SELECT SQL_CACHE * FROM my_table WHERE condition;
- query_cache size
- 默认情况下 query_cache_size为0,表示为查询缓存预留的内存为0,则无法使用查询缓存
- SET GLOBAL query_cache_size = 1348282;
- 查询缓存可以看做是SQL文本和查询结果的映射
- 第二次查询的SQL和第一次查询的SQL完全相同,则会使用缓存
- SHOW STATUS LIKE 'Qcache_hiss';查看命中次数
- 表的结构或数据发生改变时,查询缓存中的数据不再有效
- 清理缓存
- FLUSH QUERY CACHE ; // 清理查询缓存内存碎片
- RESET QUERY CACHE ; // 从查询缓存中移除所有查询
- FLUSH TABLES ; // 关闭所有打开的表,同时该操作将会清空查询缓存中的内容
- 使用Memcache缓存查询数据
- 对于大型站点,如果没有中间缓存层,当流量打入数据层时,即便有之前的几层为我们挡住一部分流量,但是在大并发的情况下,还是会有大量请求涌入数据库层,这样对于数据库服务器的压力冲击很大,响应速度也会下降,因此添加中间缓存层很有必要。
- memecache是一套分布式的高度缓存系统,由LiveJournal的Brad Fitzpatrick开发,但目前被许多网站使用以提升网站的访问速度,尤其对于一些大型的、需要频繁访问数据库的网站访问速度提升效果十分显著
- 工作原理:memecache是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等,简单的说就是将数据调用到内存,然后从内存中读取,从而大大提高读取速度
- 工作流程:先检查客户端的请求数据是否在memecahced中,如有,直接把请求数据返回,不再对数据库进行任何操作;如果请求的数据不在memecached中,就去查数据库,把从数据库获取的数据返回给客户端,同时把数据缓存一份放到memecached中
- 通用缓存机制:用查询的方法名+参数作为铲鲟时的key value对中的key值
-
- 方法:
- 获取:get(key)
- 设置:set(key, val, expire)
- 删除:delete(key)
- 使用Redis缓存
- 与Memcache的区别
- 性能相差不大
- Redis在2.0版本后增加了自己的VM特性,突破物理内存的限制,Memcache可以修改最大可用内存,采用LRU算法
- Redis,依赖客户端来实现分布式读写
- Memcache本身没有数据冗余机制
- Redis支持(快照、AOF),依赖快照进行持久化,AOF增强了可靠性的同时,对性能有所影响
- Memcache不支持持久化,通常做缓存,提升性能;
- Memcache在并发场景下,用cas保证一致性,redis事务支持比较弱,只能保证事务中的每个操作连续执行
- Redis支持多种类的数据类型
- Redis用于数据量较小的高性能操作和运算上
- Memecache用于动态系统中减少数据库负载,提升性能;适合做缓存,提高性能
- 缓存其他数据
- Session
- session_set_save_handler
三十六、MySQL数据库层的优化
优化方向
- 数据表数据类型优化
- 字段使用什么样的数据类型更合适
- 字段使用什么样的数据类型能更快
- tinyint(0-255)、smallint(几百,几千)、bigint(上千万的数字)
- 考虑空间的问题、考虑范围的问题
- char(手机号、MD5加密)、varchar(用户名、邮箱)
- enum:特性、固定的分类可以使用enum存储,效率更快
- IP地址存储成整型
- 索引优化(会影响写操作速度,占用磁盘空间)
- 建立合适的索引
- 索引在什么场景下效率最高
- 索引的创建原则
- 索引创建的注意事项
- 索引不是越多越好,在合适的字段上创建合适的索引
- 复合索引的前缀原则
- like 查询%的问题(使用like查询,索引会失效)
- 全表扫描优化
- or条件索引使用情况(前面条件使用索引,后面没有使用索引,索引会失效)
- 字符串类型索引失效的问题(字符串存的数据是整型,查询时没有使用引号,索引会失效)
- SQL语句优化
- 优化查询过程中的数据访问
- 使用Limit
- 返回列不用*
- 优化长难句的查询语句
- 变复杂为简单
- 切分查询(分次删除数据)
- 分解关联查询
- 优化特定类型的查询语句
- 优化count(*)(没有where条件的count(*)是最快的)
- 优化关联查询
- 优化子查询
- 优化Group by 和 distinct
- 优化limit和union
- 存储引擎优化
- 尽量使用InnoDB存储引擎
- 数据表结构设计的优化
- 分区操作:通过特定的策略对数据表进行物理拆分
- 对用户透明
- partition by
-
- 分库分表
- 水平拆分
- 垂直拆分
- 数据库服务器架构的优化
- 主从复制
- 读写分离
- 双主热备
-
- 负载均衡
- 通过LVS的三种基本模式实现负载均衡
- MyCat数据库中间件实现负载均衡