innodb引擎
默认事务型引擎,最重要最广泛的存储引擎,性能非常优秀
数据存储在共享表空间,可通过配置分开
对主键查询的性能高于其他类型的存储引擎
内部做了很多优化,从磁盘读取数据时自动在内存构建hash索引,插入数据时自动构建插入缓冲区
通过一些机制和工具支持真正的热备份
支持崩溃后的安全恢复
支持行级锁
支持外键
MyISAM引擎
5.1版本前是默认引擎
拥有全文索引、压缩、空间函数
不支持事务和行级锁,不支持奔溃后安全恢复
表存储在两个文件,MYD和MYI
设计简单,某些场景下性能很好
其他引擎:
Archive、Blackhole、CSV、Memory
MySQL锁机制
当多个查询同一时刻进行数据修改时,会产生并发控制的问题
- 共享锁(读锁)
- 排他锁(写锁)
锁粒度
表锁:系统性能开销最小,会锁定整张表,myisam使用表锁
行锁:最大程度的支持并发处理,但是也带来了最大的锁开销,innodb实现行级锁
char与varchar
char
代码语言:javascript复制char是定长的,根据定义的字符串长度分配足量空间
char会根据需要采用空格进行填充以方便比较
char适合存储很短的字符串,或者所有值都接近同一个长度
char长度超过设定的长度,会被截断
varchar
代码语言:javascript复制varchar用于存储可变长字符串,比定长类型更加节省空间
varchar使用1或2个额外字节记录字符串的长度,列长度小于255字节用1个字节表示,否则用2个
varchar长度超过设定的长度,会被截断
比较
代码语言:javascript复制对于经常变更的数据,char比varchar更好,char不容易产生碎片
对于非常短的列,char比varchar在存储空间上更有效率
只分配真正需要的空间,更长的列会消耗更多的内存
索引
- 大大减少服务器需要扫描的数据量
- 帮助服务器避免排序和临时表
- 将随机I/O变顺序I/O
- 大大提高查询速度,降低写的速度,占用磁盘空间
索引类型
- 普通索引
- 主键索引
- 唯一索引
- 组合索引
- 外键索引
- 全文索引
索引创建原则
- 最适合索引的列是出现在where子句的列,或连接子句中的列,而不是出现在select的关键字后的列
- 索引列的基数越大,索引效果越好
- 对字符串进行索引,应指定一个前缀长度,可以节省大量的索引空间
- 根据情况创建复合索引,复合索引可以提高查询效率
- 避免创建过多索引,索引会额外占用磁盘空间,减低写操作效率
- 主键尽可能选择较短的数据类型,可以有效减少索引的磁盘占用,提高效率
索引的注意事项
- 复合索引遵循前缀原则
- like查询,%不能在前,可以使用全文索引
- column is null 可以使用索引
- 如果MySQL估计使用索引比全表扫描更慢,会放弃使用索引
mysql优化
查询速度慢的原因
- 打开慢查询日志,通过pt-query-dugest分析
- show profile,通过
set profiling=1;
开启,服务器上执行的所有语句消耗时间都会记录到临时表。show profile for query QUERY_ID
查询指定查询 - show status,查询一些计数器,猜出哪些代价高或消耗时间多
- show processlist,查询线程状态进行分析
- explain,分析单个SQL语句查询
优化查询过程中的数据访问
- 访问数据太多导致性能下降
- 确定应用程序是否检索大量超过需要的数据,可能是太多列或者行
- 确定mysql是否分析大量不必要的数据行
- 查询不需要的记录,使用limit限制
- 夺标关联返回全部列指定A.id,A.name
- 总数取出全部列,select * 会让优化器无法完成所有覆盖扫码的优化
- 重复查询相同的数据,可以缓存数据
- 改变数据库和表的结构,修改数据表范式
- 重写SQL语句,让优化器可以更优的执行
优化长难得查询语句
- MySQL内部每秒能扫描内存中上百万行数据,相比之下,响应数据给客户端就要慢得多
- 使用尽可能少的查询是好的,但是有时将一个大的查询分解为多个小的查询是很有必要的
- 分解关联查询,将一个关联查询分解为多个sql来执行,让缓存效率更高,执行单个查询可以减少锁的竞争,在应用层做关联可以更容易对数据库进行拆分,查询效率会有大幅提升,较少冗余记录的查询
优化特定类型的查询语句
- 优化count()查询,
count(*)
会忽略所有列,直接统计所有列数,因此不要用count(列名)
- 优化关联查询,确定ON或者USING子句的列上有索引;确保
GROUP BY
和ORDER BY
中只有一个表的列,这样MySQL才有可能使用索引 - 优化子查询 建议使用关联查询替代
- 优化
GROUP BY
和DISTINCT
,建立索引进行优化 - 优化
LIMIT
分页,可以通过记录上次查询的最大ID,如果根据id排序时,下次查询根据该ID来查询(如:ID > maxID) - 优化
UNION
查询,UNION ALL
性能比UNION
高
MySQL提升(高可扩展和高可用)
分区表
工作原理
对用户而言,分区表是一个独立的逻辑表,但是底层MySQL将其分成了多个物理子表,对于用户来说是透明的,每一个分区表都会使用一个独立的表文件。
创建表的时候使用 partition by
子句定义每个分区存放的数据,执行查询时,优化器会根据分区定义过滤那些没有我们需要数据的分区,这样查询只需要查询所需数据在的分区即可
分区的主要目的是将数据按照一个较粗的粒度分在不同的表中,这样可以将相关的数据存放在一起,而且如果想一次性删除整个分区的数据也很方便
适用场景
- 表非常大,无法全部存在内容,或者只有表的最后有热点数据,其他都是历史数据
- 分区表的数据更易维护,可以对独立的分区进行独立操作
- 分区表的数据可以分布在不同机器上,从而高效使用资源
- 可以使用分区表来避免某些特殊瓶颈
- 可以备份和恢复独立分区
限制
- 一个表最多只能有1024个分区
- 5.1版本中,分区表表达式必须是整数,5.5可以使用列分区
- 分区字段中如果有主见和唯一索引列,那么主键和唯一列都必须包含进来
- 分区表中无法使用外键约束
- 需要对现有表的结构进行改变
- 所有分区都必须使用相同的存储引擎
- 分区函数中可以使用的函数和表达式会有一些限制
- 某些存储引擎不支持分区
- 对于MyISAM的分区表,不能使用load index into cache
- 对于MyISAM表,使用分区表时需要打开更多的文件描述符
分库分表
工作原理:
通过一些HASH算法或者工具实现将一张数据表垂直或者水平物理切分
适用场景
- 单表记录条数达到百万到千万级别时
- 解决表锁的问题
分别方式
- 水平切分:表很大,分割后可以减低在查询时需要读的数据和索引的页数,同时也减低了索引的层数,提高查询速度
使用场景:
1. 表中数据本身就有独立性,例如表中分别记录各个地区的数据或者不同时期的数据,特别是有些数据常用,有些不常用
2. 需要把数据存放在多个介质
缺点:
1. 给应用增加复杂度,通常查询时需要多个表名,查询所有数据都需要UNION操作
2. 在许多数据库应用中,这种复杂性会超过他带来的优点,查询时会增加读一个索引层的磁盘次数
2. 垂直分表:把主键和一些列放在一个表,然后把主键和另外的列放在另一张表中
代码语言:javascript复制使用场景:
1. 如果一个表中某些列常用,而另外一些列不常用
2. 可以使数据行变小,一个数据页能存储更多数据,查询时减少I/O次数
缺点:
1. 管理冗余列,查询所有数据需要JOIN操作
2. 有些分表的策略基于应用层的逻辑算法,一旦逻辑算法改变,整个分表逻辑都会改变,扩展性较差
3. 对于应用层来说,逻辑算法无疑增加开发成本
主从复制
工作原理
- 在主库上把数据更改记录到二进制日志
- 从库将主库的日志复制到自己的中继日志
- 从库读取中继日志中的事件,将其重放到从库数据中
解决问题
- 数据分布:随意停止或开始复制,并在不同地理位置分布数据备份
- 负载均衡:减低单个服务器压力
- 高可用和故障切换:帮助应用程序避免单点失败
- 升级测试:可以使用更高版本的MySQL作为从库
MySQL安全
安全操作
- 使用预处理语句防SQL这几日
- 写入数据库的数据要进行特殊字符转移
- 查询错误信息不要返回给用户,将错误记录到日志
安全设置
- 定期做数据备份
- 不给查询用户root权限,合理分配权限
- 关闭远程访问数据库权限
- 修改root口令,不用默认口令,使用较复杂的口令
- 删除多余的用户
- 改变root用户的名称
- 限制一般用户浏览其他库
- 限制用户对数据文件的访问权限
MVC
MVC工作原理
- model 数据模型操作层,是应用程序中用于处理应用程序数据逻辑的部分
- view 视图层,是应用程序中处理数据显示的部分。
- controller 业务处理层,是应用程序中处理用户交互的部分。
单一入口
工作原理
用一个处理程序文件处理所有的HTTP请求,根据请求时的参数的不同区分不同的模块和操作请求
优势
- 可以进行统一的安全性检查
- 集中处理程序
劣势
- URL不美观(解决方法:URL重写)
- 处理效率会降低(可忽略)
模板引擎
PHP是一种HTML内嵌式在服务端执行的脚本语言,但是PHP又很多可以使PHP代码和HTML代码分开的模板引擎,例如:smarty
工作原理
模板引擎就是庞大的完善的正则表达式替换库
算法
排序算法
冒泡排序
原理:两两相邻的数进行比较,如果反序就交换,否则不交换
时间复杂度:最坏(O(n^2)), 平均(O(n^2))
空间复杂度:O(1)
快速排序
原理:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据和另一部分的所有数据都要笑,然后按照此方法对这两部分数据分别进行快速排序,整个排序过程可以递归完成
时间复杂度:最坏(O(n^2)), 平均(O(nlog2n))
空间复杂度:最差(O(n)),平均(O(log2n))
直接插入排序
原理:每次从无序表中取出第一个元素,把他插入到有序表的合适位置,使有序表仍然有序
时间复杂度:最坏(O(n^2)), 平均(O(n^2))
空间复杂度:O(1)
选择排序
原理:每次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,知道全部待排序的数据元素排外
时间复杂度:最坏(O(n^2)), 平均(O(n^2))
空间复杂度:O(1)
希尔排序
原理:把待排序的数据根据增量分成几个子序列,对子序列进行插入排序,知道增量为1,直接插入进行插入排序;增量的排序,一般是数组的长度的一半,再变为原来增量的一半,直到增量为1
时间复杂度:最坏(O(n^2)), 平均(O(nlog2n))
空间复杂度:O(1)
堆排序
原理:把待排序的元素按照大小在二叉树位置上排序,排序好的元素要满足:父节点的元素要大于子节点;这个过程叫做堆化过程,如果根节点存放的最大的数,则叫做大根堆,如果是最小,就叫小跟堆,可以把根节点拿出来,然后再堆化,循环到最后一个节点
时间复杂度:最坏(O(nlog2n)), 平均(O(nlog2n))
空间复杂度:O(1)
归并排序
原理:将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个有序的子序列,再把有序的子序列合并为整体有序序列
时间复杂度:最坏(O(nlog2n)), 平均(O(nlog2n))
空间复杂度:O(n)
查找算法
二分查找
原理:从数组的中间元素开始,如果中间元素正好是要查找的元素,搜索结果,如果某一个特定元素大于或者小于中间元素的那一半中查找,而且跟开始一样从中间开始比较,如果某一步骤数组为空,代表找不到
时间复杂度:最坏(O(nlog2n)), 平均(O(nlog2n))
空间复杂度:迭代(O(1)), 递归(O(log2n))
顺序查找
原理:按一定的顺序检查数组中每一个元素,直到要找到锁要寻找的特定指为止
时间复杂度:最坏(O(n)), 平均(O(n))
空间复杂度:O(1)
优化
高并发和大流量解决方案
高并发的问题,应关注
- QPS:每秒钟请求或查询数量,在互联网领域指每秒响应的请求数(指HTTP请求)
- 吞吐量:单位时间内处理的请求数量(通常由QPS和并发数决定)
- 响应时间:从请求发出到收到响应花费时间
- PV:综合浏览量(Page View),即页面浏览量或者点击量,一个访客在24小时内访问的页面数量。同一个人浏览你的网站同一个页面,只记作一次PV
- UV:独立访客(UniQue Visitor),即一定时间范围内相同访客多次访问网站,只能计算为1个独立访客
- 带宽:计算带宽大小需关注两个指标,峰值流量和页面的平均大小
- 日网站带宽=PV/统计时间(秒) 平均页面大小(KB) 8
- 峰值一般是平均值的倍数
- QPS不等于并发并发连接数。QPS是每秒HTTP请求数量,并发连接数是系统同时处理的请求数量
- 二八定律(80%的访问量集中在20%的时间):(总PV数 80%)/(6小时秒速 20%)=峰值每秒请求数(QPS)
- 压力测试:能承受最大的并发数和最大承受的QPS值
常用性能测试工具
ab,wrk,Apache JMeter, http_load, Web Bench, Siege
ab
使用方法:
代码语言:javascript复制# 模拟并发请求100次,总请求5000次
ab -c 100 -n 5000 http://example.com
注意事项:
- 测试机器与被测机器分开
- 不要对线上服务做压力测试
- 观察测试工具所在机器,以及被测试的前端机的CPU、内存、网络等都不超过最高限度的75%
QPS指标
- QPS达到50,可以称之为小型网站,一般服务器都可以应付
- QPS达到100;瓶颈:MySQL查询达到瓶颈;优化方案:数据库缓存层,数据库负载均衡
- QPS达到800;瓶颈:带宽速度达到瓶颈;优化方案:CDN加速,负载均衡
- QPS达到1000;瓶颈:缓存服务器的带宽达到瓶颈;优化方案:静态HTML缓存
- QPS达到2000;瓶颈:文件系统访问锁成为灾难;优化方案:做业务分离,分布式存储
高并发优化方案
流量优化
- 防盗链处理
前端优化
- 减少HTTP请求
- 添加异步请求
- 启用浏览器缓存和文件压缩
- CDN加速
- 建立独立的图片服务器
服务端优化
- 页面静态化
- 并发处理
数据库优化
- 数据库缓存
- 分库分表、分区操作
- 读写分离
- 负载均衡
web服务器优化
- 负载均衡
web资源防盗链
盗链定义
- 倒链是指在自己的页面上展示一些并不在服务器上的内容
- 获得他人服务器上的资源地址,绕过别人的资源展示页面,直接在自己的页面上向最终用户提供此内容
- 常见的是小站盗用大站的图片、音乐、视频、软件等资源
- 倒链可以减轻自己的服务器负担
防盗链定义
防止别人通过一些技术手段绕过本站的资源展示页面,盗用本站的资源,让绕开本站资源展示页面的资源链接失效,可以大大减轻服务器及带宽的压力
防盗链的工作原理
- 通过Referer或者计算签名,网站可以检测目标网页访问的来源网页,如果是资源文件,则可以跟踪到显示他的网页地址
- 一旦检测到来源不是本站即进行阻止或返回指定的页面
防盗链实现方法
Referer
- NGINX模块ngx_http_referer_module用来阻挡来源非法的域名请求
- NGINX指令valid_referers,全局变量$invalid_referer
配置:
代码语言:javascript复制valid_referers none|blocked|server_names|string...;
- none: Referer来源头部为空的情况,比如直接打开
- blocked: Referer来源头部不为空,但是里面的值被代理或者防火墙删除了,这些值都不以 http://或者https ://开头
- server_names: Referer来源头部包含当前的server_names
配置例子:
代码语言:javascript复制location ~.*.(gif|jpg|png|flv|swf|rar}zip)$
{
valid_referers none blocked imooc.com *.imooc.com;
if ($invalid_referer)
{
#return 403;
rewrite ^/ https://img.yuanmabao.com/zijie/pic/2021/07/20/aebd4wttd2c.jpg;
}
}
减少HTTP请求
HTTP连接产生的开销
- 域名解析
- TCP连接
- 发送请求
- 等待
- 下载资源
- 解析
解决方案
- 减少组件的数量,并由此减少HTTP请求的数量
- 图片地图:图片地图允许你在一个图片上关联多个URL。目标URL的选择取决于用户蛋鸡了图片上的哪个位置
- CSS Sprites:css 精灵,通过使用合并图片,通过指定css的background-image和background-position来显示元素
- 合并脚本和样式表适
- 图片使用base64编码减少页面请求数
浏览器缓存和数据压缩
HTTP缓存机制分类
- 200 from cache:直接从本地缓存中获取响应,最快速,最省流量,因为根本没有向服务器发送请求
- 304 Not Modified:协商缓存,浏览器在本地没有命中的情况下,请求头中发送一定的校验数据到服务端,如果服务端的数据没有改变,浏览器从本地缓存响应,返回304。特点:快速,发送的数据很少,只返回一些基本的响应头信息,数据量很小,不发送实际响应体
- 200 OK:以上两种缓存全部失败,服务器返回完整响应。没有用到缓存,相对最慢
header设置HTTP缓存机制
- pragma:HTTP1.0时代的遗留产物,该字段被设置为no-cache时,会告知浏览器禁用本地缓存,即每次都向服务器发送请求
- Expires:HTTP1.0时代用来启用本地缓存的字段,设置值如‘Thu, 31 Dec 2037 23:55:55 GMT’的格林威治的时间。但浏览器与服务器的时间无法保持一致,如果差距大就会影响缓存结果
- Cache-Control:HTTP1.1针对Expires时间不一致的解决方案,运用Cache-Control告知浏览器缓存过期的时间间隔而不是时刻,即使具体时间不一致,也不影响缓存的管理
优先级:Pragma > Cache-Control > Expires
Cache-Control配置
- no-store:禁止浏览器缓存响应
- no-cache:不允许直接使用本地缓存,先发起请求和服务器协商
- max-age=delta-seconds:告知浏览器该响应本缓存的有效的最长期限,以秒为单位
协商缓存
- 当浏览器没有命中本地缓存,如本地缓存过期或者响应中声名不允许直接使用本地缓存,那么浏览器肯定会发起服务端请求
- 服务端会验证数据是否修改,如果没有就通知浏览器使用本地缓存
header设置协商缓存
- Last-Modified:通知浏览器资源的最后修改时间,设置值如‘Thu, 31 Dec 2037 23:55:55 GMT’的格林威治的时间
- If-Modified-Since:得到资源的最后修改时间后,会将这个信息通过If-Modified-Since提交到服务器做检查,如果没有修改,返回304状态码,设置值如‘Thu, 31 Dec 2037 23:55:55 GMT’的格林威治的时间
- ETag:HTTP1.1推出,文件的指纹标识符,如果文件内容修改,指纹也会改变,设置值如‘5a643fc7-38a3’
- If-None-Match:本地缓存失效,会携带此值去请求服务端,服务端判断该资源是否改变,如果没有改变,直接使用本地缓存,返回304
缓存策略的选择
适合缓存的内容
- 不变的图像,如logo,图标等
- js、css静态文件
- 可下载的内容,媒体文件
适合协商缓存
- HTML文件
- 经常替换的图片
- 经常修改的js、css文件,js、css文件的加载可以加入文件的签名来拒绝缓存,如‘index.css?签名’,‘index.签名.js’
不建议缓存的内容
- 用户隐私等敏感数据
- 经常改变的API数据接口
NGINX配置缓存策略
本地缓存配置
代码语言:javascript复制add_header name value [always];
expires time;
etag on|off
前端代码和资源压缩
优势
- 让资源文件更小,加快文件在网络中的传输,让网页更快的展现,降低带宽和流量的开销
压缩方式
- js、css、图片、html代码的压缩
- gzip压缩
gzip配置
代码语言:javascript复制gzip on|off; #是否开启gzip
gzip_buffers 32 4K|16 8K; #缓冲(在内存中缓存几块?每块多大)
gzip_comp_level [1-9] #推荐6,压缩级别(级别越高,压得越小,越浪费CPU计算资源)
gzip_disable #正则匹配UA,什么样的Uri不进行gzip
gzip_min_length 200 #开始压缩的最小长度
gzip_http_version 1.0|1.1 #开始压缩的http协议版本
gzip_proxied #设置请求者代理服务器,该如何缓存内容
gzip_types text/plain application/xml image/png #对哪些类型的文件压缩,如txt、xml、css
gzip_vary on|off #是否传输gzip压缩标志
CDN加速
定义
- CDN的全称content delivery network,内容分发网络
- 尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定
- 在网络各处放置节点服务器所构成的有的互联网基础之上的一层智能虚拟网络
- CDN系统能够实现地根据网络流量和各节点的连接、负载状况以及到用户距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上
优势
- 本地cache加速,提高了企业站点(尤其含有大量图片和静态页面站点)的访问速度
- 跨运营商的网络加速,保证不同网络的用户都能得到良好的访问质量
- 远程访问用户根据DNS负载均衡技术只能选择cache服务器
- 自动生成服务器的远程Mirror(镜像)cache服务器,远程用户访问时从cache服务器上读取数据,减少远程访问的带宽、分担网络流量、减轻原站点web服务器负载等功能
- 广泛分布的cdn节点加上节点之间的智能冗余机制,可以有效地预防黑客入侵
工作原理
- 用户发起请求
- 智能DNS的解析(根据IP判断地理位置、接入网类型、选择路由最短和负载最轻的服务器)
- 取得缓存服务器ip
- 把内容返回给用户(如果缓存中有,没有就执行5、6、7)
- 向源站发起请求
- 将结果返回给用户
- 将结果存入缓存服务器
适用场景
- 站点或者应用中大量静态资源的加速分发,例如css、js、图片和HTML
- 大文件下载
- 直播网站
独立图片服务器
必要性
- 分担web服务器的I/O负载,将耗费资源的图片服务器分离出来,提高服务器的性能和稳定性
- 能够专门对图片服务器进行优化,为图片服务器设置针对性的缓存方案,减少带宽成本,提高访问速度
- 提高网站的可扩展性,通过增加图片服务器,提高图片吞吐能力
采用独立域名
原因:
- 同一域名下浏览器的并发连接数有限制,突破浏览器连接数的限制
- 由于cookie的原因,对缓存不利,大部分web cache都只缓存不带cookie的请求,导致每次的图片请求都不能命中cache
如何图片上传和同步
- NFS共享方式
- 利用FTP同步
动态语言静态化
将现有的PHP等动态语言的逻辑代码生成为静态的HTML文件,用户访问动态脚本重定向到静态HTML文件的过程。对实时性要求不高
原因:
- 动态脚本通过会做逻辑计算和数据查询,访问量越大,服务器压力越大
- 访问量大时可能会造成CPU负载过高,数据库服务器压力过大
- 静态化可以减低逻辑处理压力,降低数据库服务器查询压力
实现方法
- 使用模板引擎
- 利用ob系列函数
ob_start();//打开输出控制缓冲
ob_get_content();//返回输出缓冲区内容
ob_clean();//清空输出缓冲区
ob_end_flush();//冲刷出(送出)输出缓冲区内容并关闭缓冲