特点
- 人多商品少
- 时间短流量高
- 外挂机器
技术分析
- 瞬时高并发的处理能力
- 多层次的分布式处理能力
- 人机交互与对抗
CDN + LVS
数据库封装类
connect
在connect函数的首尾都调用microtime来记录当前时间,用于后续运行效率分析
同理可以应用到所有执行语句上
private function Connect($name = 'master')
{
global $config;
$mtime1 = microtime();
$this->settings = $config['db'][$name];
$dsn = 'mysql:dbname=' . $this->settings["dbname"] . ';host=' . $this->settings["host"] . '';
try {
# Read settings from INI file, set UTF8
$this->pdo = new \PDO($dsn, $this->settings["user"], $this->settings["password"], array(\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8;"));
# We can now log any exceptions on Fatal error.
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
# Disable emulation of prepared statements, use REAL prepared statements instead.
$this->pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true);
# Connection succeeded, set the boolean to true.
$this->bConnected = true;
} catch (\PDOException $e) {
# Write into log
print_r($e);
echo $this->ExceptionLog($e->getMessage());
die();
}
$mtime2 = microtime();
\common\DebugLog::_mysql('connect', null, array('host' => $this->settings['host'], 'dbname' => $this->settings['dbname']), $mtime1, $mtime2, null);
}
调试封装类
- time性能探针,计算运行的步骤以及每一步的执行效率
- log日志记录
- http 接口调用记录以及耗时的汇总统计(数目及时间)
- redis 调用记录以及耗时的汇总统计
- mysql 调用记录以及耗时的汇总统计
- cache 调用记录以及耗时的汇总统计
思考:日志切换开关,见2-8
/**
* 是否有可视化界面输出,HTML代码直接返回到浏览器
*/
public static function _is_show_view() {
if (self::$instance && isset($_SERVER['HTTP_USER_AGENT'])) {
return true;
} else {
return false;
}
}
在url里面指定参数,在页面生成debug信息单商品秒杀
万次秒杀:不需要太多优化,单机mysql即可支持
百万次秒杀: web服务器集群,redis缓存
逻辑
扣除商品剩余数量
库存变化,传递+num或者-num,传递变化量,而不是变化结果。redis用incrby。防止脏数据。
读允许脏数据,写绝对不允许脏数据。
// 8. 扣除商品剩余数量
// left为减少后的返回值
// cache, redis incrby
$left = $goods_model->changeLeftNumCached($goods_id, 0 - $goods_num);
$ok = false;
// 如果存在并发,比如只剩最后一台手机,但是有两个人同时下单,这时候$left为-1, 下单并不成功
// 只有left >= 0时,才能成功下单
if ($left >= 0) {
// 数据库正常update
$ok = $goods_model->changeLeftNum($goods_id, 0 - $goods_num);
} else {
// 扣除商品库存失败
// 更改商品状态为售完
// 引导后续用户到静态页面
$goods_model->changeStatusCached($goods_id, 0);
$result = array('error_no' => '108', 'error_msg' => '商品剩余数量不足');
show_result($result);
}
优化单机性能
提高页面访问速度
- 减少页面大小,启用gzip压缩:nginx配置
- 减少资源请求数量, 合并和压缩css,js等: minify
- 设置浏览器缓存,利用CDN加速
nginx设置浏览器缓存
location ~.*\.(jpg|png|jpeg)$ { #指定缓存文件类型
expires 7d; #设置浏览器过期时间
}
提高秒杀接口速度
- 将接口静态化
- 例子:一共有5万商品,假设涌入1亿人,前50万涌入的人一定能秒杀完,后面的50万到1亿直接访问静态接口,用CDN就能解决
- 假设一个页面每次访问会产生 50 到 100 次查询(这很常见),但是它又不需要每次都去取最新数据(譬如 app 的首页),这时候就可以用静态化的接口。比如你可以用一个定时器去查询首页需要的数据,然后把查询到的数据组装成一个 JSON 文件保存起来,客户端每次都去查询这个静态的 JSON 文件,这样就可以省下很多次 API 请求。
- 比如秒杀情况,在秒杀开始前,所有的访问信息都会是错误提示,这个时候我们就可以把后端API的结果用JSON保存起来,客户端每次访问直接返回这个JSON文件。等秒杀开始前删除掉这个JSON文件,返回动态接口。同理可以应用参与秒杀后面的50万到1亿人。
- 快速终止的逻辑放在前面
- 增加冗余的定制化的数据,保证程序更快速
提高数据处理速度
- 数据库索引,一定不能少,更不能乱
- 减少数据规模
- 如订单数据表,由于历史订单的存在,数据表的规模会越来越大,会减缓查找速度。所以可以考虑专门建一张表用于处理当前活动的订单。结束后再合并到历史订单
- 将数据放到redis缓存中