高并发优化思路笔记
一,web资源防盗链
通过Referer 或者签名,检测目标来源。
1,nginx模块ngx_http_referer_module用于阻挡来源非法的域名请求
例:
location ~.*\.(gif|jpg|png|flv|swf|rar|zip)$ {
valid_referers none blocked xxx.com *.xxx.com;
if ($invalid_referer) {
rewrite ^/http://www.xxx.com/403.jpg;
}
}
此方法是可以被伪造http头信息referer**。
2,使用加密签名
第三方模块HttpAccessKeyModule实现nginx防盗链
location ~.*\.(gif|jpg|png|flv|swf|rar|zip)$ {
accesskey on;
accesskey_hashmethod md5;
accesskey_arg "key"; #GET参数名
accesskey_signature "mypass$remote_addr"; #加密规则
}
<?php
// md5 (mypass.ip)
$key = md5('mypass'.$_SERVER['REMOTE_ADDR']);
echo '<img src="xxx.jpg?key=$key">';
二,减少页面大小,启用nginx gzip压缩
gzip on;
gzip_min_length 1k; #小于1k不进行压缩
gzip_buffers 4 16k;
gzip_comp_level 6;
gzip_types text/plain application/javascript text/css application/xml;
重复内容越多,压缩效率越好!
三,减少资源请求数量,合并压缩静态文件。
1,图片地图 合并图片
将n个图片合并为一张图片,以位置信息定位超链接。
<map><area></area></map>
2,css sprite 合并图片
调整background-position。
3,图片使用Base64编码减少页面请求数。
<img src="data:image/gif;base64,/xx/xxxxxxxxxxxx.....">
4,Minify 把CSS和JS压缩和削减,以及把多个CSS,JS文件合并到一个文件里(减少资源请求数量)。https://tool.css-js.com/;图片压缩https://tinypng.com/。
四,设置浏览器缓存。
1,nginx配置
location ~.*\.(js|css|jpg|jpeg|gif|png)$ {
expires 7d; #浏览器过期时间
}
2,HTTP缓存
200from cache(本地缓存),304Not Modified(服务器里取缓存,协商缓存),200OK(没用到缓存)。
本地缓存:设置http头信息缓存 ,缓存的优先级 Pragma > Cache-Control > Expires ;
协商缓存:header Last-Modified If-Modified-Since提交到服务器做检查,如果没有修改,返回304。E-tag通过"指纹"判断
五,CDN加速
六,图片服务器
NFS 或者 FTP 同步图片(多台图片服务器)
七,动态语言静态化及代码逻辑优化(秒杀为例)
1,实时性不高的页面(ob_系列方法)如index.php
2,快速终止的逻辑放在前面(跳出循环亦是如此),把用不到数据库缓存的代码往前放。
3,秒杀接口:如5W库存产品,1亿次请求,放50W请求进来,后续的请求就请求静态化后的接口(结合CDN)。
#原理nginx处理静态文件要比动态文件效率高太多
#可以挡住大部分流量
#验证活动和商品的状态
#这个接口对应着 /astatus/{aid}_{gid}.js 静态文件的动态实现(也方便SEO)
#例如: /astatus/1_1.js
#不能秒杀的时候,静态文件才会存在
#活动开始前,静态文件存在
#互动进行中,会统一把静态文件删除,则nginx的rewrite失效,进入到这个动态文件
#预估的抢完库存的流量满后也可以生成静态文件,防止后面流量进来导致负载过大
#nginx配置(文件不存在的时候走rewrite到动态文件)
if (!-e $request_filename) {
rewrite ^([^\.]*)/astatus/([0-9]+)_([0-9]+).js$ $1/astatus.php?aid=$2&gid=$3 last;
}
#例如: /astatus/1_2.js
#文件如果存在,则nginx直接返回静态文件的内容
#如果不存在,则把参数赋值给动态接口 /astatus.php?aid=1&gid=2
4,增加冗余的数据结构。比如 活动-商品 存入redis 减轻mysql压力
/**
* astatus.php
* 秒杀接口
*/
<?php
$redis = \Predis::getRedis('instance1');
$data = $redis->mget(array(
'status_a_' . $aid,
'status_g_' . $gid,
));
if($data && $data[0] && $data[1]) {
// 活动状态和商品状态都是正常状态,才可以返回一个正确的验证码(返回验证码而不是直接返回成功状态为了防止机器拿到返回结果直接刷下一个逻辑,下一个逻辑必须要正确的验证码才可以继续)
$info = array(
'now' => time(),
'ip' => getClientIp(),
'user_id' => $login_userinfo['uid'],
);
$str = signQuestion($info);
echo json_encode(array('user_sign' => $str));
}else {
$result = array('code' => '-1', 'msg' => '活动商品已下架或者已售完', 'data' => array());
echo json_encode($result);
}
<?php
//非法用户不能绕过astatus
$status_check = false;
$str_sign_data = unsignQuestion($sign_data);
$sign_data_info = json_decode($str_sign_data, true);
// 时间不能超过当前时间2分钟,IP和用户保持不变
if ($sign_data_info
&& $sign_data_info['now'] < $now
&& $sign_data_info['now'] > $now - 120
&& $sign_data_info['ip'] == $client_ip
&& $sign_data_info['uid'] == $uid
) {
$status_check = true;
}
if (!$status_check) {
$result = array('code' => '-1', 'msg' => '用户校验值验证没有通过', 'data' => array());
show_result($result);
}
扣减库存操作要用表达式,redis用原子操作,防止高并发时候出现脏数据。
栗子:
<?php
$left = changeLeftNumCached($goods_id, 0-$goods_num);
$res = false;
if ($left >= 0) {
$res = changeLeftNum($goods_id, 0-$goods_num);
} else {
// 扣除商品库存失败
//改变商品状态,生成'astuatus/'.$active_id .'_'.$goods_id.'.js'静态文件,下次客户从源头就进不来(此商品nginx不会访问动态程序),减轻服务器压力
}
function changeLeftNumCached($id, $num) {
$key = 'info_g_' . $id;
$redis = \Preds::getRedis('instance1');
$left = $redis->hincrby($key, 'num_left', $num);
return $left; //这里返回的是操作后的库存
}
function changeLeftNum($id, $num) {
$params = array('id' => $id);
$sql = "UPDATE " . $this->table . " SET num_left=num_left" . ($num > 0 ? '+' : '') . "$num WHERE id=:id";
return $this->getDb()->query($sql, $params);
}
5,防机器人,防黄牛。
各逻辑之间要有所关联,不容易被机器人**;
问答题(接口交互要加密,提交验证数据可以把问题和对应的答案也传过去,这样验证的时候可以不需要查数据库);
点触式验证(持续时间长的活动),但是性能差;
6,事后用户行为分析
用户访问页面路径;
页面停留时间,点击位置和时间;
用户基本信息,用户ip,浏览信息等等;
7,减少数据库依赖
数据库尽量只处理,插入更新,数据库依赖降到最低。
8,减少数据规模
订单表,创建一次活动,创建一张新的订单表,活动结束后 汇总 原先订单表。
八,PHP并发编程
1,swoole 2,消息队列 (rabbitmq......)3,curl_multi_add_handle等
九,数据库缓存层的优化
redis,memcached。
十,数据库层的优化
1,数据类型优化
2,索引的优化
3,SQL优化
3,表结构设计
4,服务器架构优化
十一,分布式
依赖session不一致问题;统一放在一台机子memcache里面
依赖本地文件不一致问题;放到共享磁盘里面
扩展性要好,随时可以增减服务器
多个机房,每个机房部署一个集群,每个集群一个LVS;智能DNS为不同网络不同地域的用户解析到不同LVS,部分接口引入CDN
=============================================================================================
多Web单数据中心
优点:数据好管理,操作方便。
缺点:数据处理成为瓶颈,跨机房网络问题。
多Web多数据中心
优点:每一组服务器都有数据中心,局域网完成。
缺点:数据同步,数据一致性。(引入中控服务器来解决,如秒杀库存,可以平衡库存,散列分配;5万库存放入10个集群,每个集群5千个库存,每个群体只负责卖自己的,互不相关,如果出现不同群体同一个用户由于网络变更导致重复下单,活动结束后,汇总10台集群订单,用户排重找到重复下单用户,然后联系用户沟通解决这个问题,业务层面解决,技术层面放过;中控服务器分配策略:1,直接平均分发下去,然后事实监控各个集群库存,调度库存多的去库存少的集群;2,先分配一部分库存下去给各个集群,预留一部分库存用动态调度,可防止程序BUG导致超卖)
===============================================================================================
大规模时候使用LVS
http://www.cnblogs.com/liwei0526vip/p/6370103.html
智能DNS 可以解析域名 跳入哪个LVS群。
==============================================================================================
服务器访问量规模预估
数据量,访问量(秒杀时候用户体验要好数据更新要及时,不然访问量会加倍增加,用户不停的刷,这时候就需要在访问第一次的没库存时候在用户浏览器植入一个cookie,判断有这个cookie就返回没库存,根本就不需要发起请求了,防止用户不停刷浏览器),资源量。
==========================================================================================
中控服务器 平衡调度 库存。每个中控服务器都有一个接口和发起接口请求的程序,根据策略自动平衡库存。
推荐阅读
-
深入浅析Random类在高并发下的缺陷及JUC对其的优化
-
大数据量高并发的数据库优化详解
-
使用google-perftools优化nginx在高并发时的性能的教程(完整版)
-
MySQL在大数据、高并发场景下的SQL语句优化和"最佳实践"
-
大数据量高并发的数据库优化,sql查询优化
-
荐 Java秒杀系统方案优化 高性能高并发实战,学习手记(三)
-
04 整合IDEA+Maven+SSM框架的高并发的商品秒杀项目之高并发优化
-
高并发 强一致数据库业务系统设计的一个思路
-
无聊猜想:高并发,更新要求高的解决思路 lucene数据结构Blog
-
无聊猜想:高并发,更新要求高的解决思路 lucene数据结构Blog