分类目录归档:服务器

MySQL秒杀优化

今天学习了楼方鑫先生《基于SQL的秒杀解决方案》,讲解了如何定位和优化秒杀业务中问题。
首先介绍了库存业务,库存可以分为前端库存,后端库存,实体库存。秒杀时,存在的主要问题

  • 库存数据不准确,下单、付款后,得知零库存;超卖或少卖
  • 废单较多,只下单不付款,转化率低
  • 热点商品,拖垮整个站

秒杀过程中,需要解决的技术点包括

  • 余额减一
  • 操作明细,方便追溯对账,防止一个帐号多次参与
  • 完整事务,保障记录明细与扣减库存同时完成
  • 数据落地,内存数据不可靠

针对库存技术要求,做了多个库存解决方案,比如Mysql + Read /Write Cache 。Read Cache方案不足是读有延迟影响用户体验;Write Cache方案存在多个APP写数据不一致性。Mysql + Cache + NoSQL方案则太复杂未实现。

于是又重新回到优化Mysql上。Mysql优势在于事务机制成熟,程序稳定。存在技术难点:单行并发,热点商品,瞬间压力,前一分钟,千万用户,容易堵塞,拖垮网站。于是从以下几个方面进行优化

  • 事务优化,单行更新
  • 并发优化,最大并发数
  • 排队优化,抢同一商品

分析秒杀时的处理逻辑,扫描系统代码,发现大部分程序都在等待确认Update记录数,才提交事务。

  • 开启事务
  • Insert库存明细
  • Update库存余额
  • 提交事务

在良好设计下,Mysql的Insert操作,不使用自增列是不会阻塞请求。但是Mysql的Update同一条记录是串行的,需要等远程客户端发送提交命令后才能释放锁,让其他会话继续。简单的更新操作,不考虑IO和锁冲突,一条语句执行的时间大约是0.1ms,一般条件下的网络时延为0.4-0.8ms,即等待事务提交通知的时间比真正SQL执行的时间长数倍。
于是扩展了SQL语法(OneSQL),指定在Update执行完后自动提交,不需要等待客户端发送提交命令,从而节约这一个网络来回的事务等待时间,提升网络性能。

秒杀时如果遇到大量请求需要进行排队,以免太多的请求拖垮Mysql

  • 在应用层排队的缺点,应用需要改造,使用统一框架(需要考虑跨语言),应用集群扩容时,控制不准确(连接数分配)
  • 在Mysql排队的优点,应用改造极少,只需修改少量SQL语句,无需统一框架,排队精确,发挥InnoDB性能。

于是开发了兼容Mysql的分布式数据访问层(OneProxy),为并发请求进行排队。

另外,还对热点商品进行独立数据库拆分和优化。目前,双十一前商品便已挂出,用户可以收藏或预购,对于商家而言可以准备更多商品;对于平台而言可以预先发现热点商品做优化。

总结,对于业务优化,需要循序渐进,深入了解业务逻辑和技术点,比较不同的解决方案,就算是平常的update操作也有优化空间;同时需要从其他方面进行特定优化,如高并发排队,热点数据分离等。

除了后端数据库优化,对于秒杀抽奖业务,问题的解决核心就是控制单位时间内的流量,使其不超过后端的处理能力。前端的做法包括

  • 分批次(少量多次)进行秒杀
  • 先玩游戏再抢购,如抽奖
  • 随机过滤掉部分请求,仅部分进入系统,如1/10
  • 阈值控制,一旦达到阈值,不再接收新请求
  • 预约排号,未排号用户返回失败(用户分类)
  • 验证码验证

另外,OneProxy 提供的连接池功能对于PHP非常有用。PHP运行在CGI下面,每一个请求到来便需要重新创建一个数据库连接与Mysql进行交互,并发量大量的情况下便会出现:too many connetion,乃至拖垮数据库:mysql server has gone away,影响其他业务。因此Mysql连接池,对于PHP显得非常重要。

更新:小米网在开发抢购系统的时候,最早使用PHP + Mysql碰到了一些问题,例如并发性能,数据一致性,在OneSQL上面都已经做了改进优化,只是小米自己使用Go语言重构,开发大秒系统(BigTap)。

参考链接:
限量秒杀等高并发活动的正确性如何保证?
MySQL 5.6.17/Percona5.6.16/MariaDB 10.0.11/OneSQL 5.6.16 TpmC测试
由12306.cn谈谈网站性能技术
“米粉节”背后的故事——小米网抢购系统开发实践
Web系统大规模并发——电商秒杀与抢购
OneProxy : 如何给PHP页面以及其他Ruby/Python/Go程序添加连接池功能?
基于Swoole实现的Mysql连接池

PHP性能分析之xhprof

xdebug讲到了使用xdebug对php程序进行性能分析,这里再介绍另外一个工具:xhprof,facebook出品。xhprof是一个函数级别的分层性能报告工具,包括调用次数,阻塞时间,CPU时间和内存使用情况。
首先,下载并安装xhprof扩展

tar -zxvf xhprof-0.9.4.tgz 
cd xhprof-0.9.4
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make
make install
#拷贝扩展
cp /usr/local/php/lib/php/extensions/no-debug-non-zts-20100525/xhprof.so /usr/local/php/lib/php/extensions/

mkdir -p /tmp/xhprof
chmod 755 /tmp/xhprof
chown www:www /tmp/xhprof

xhprof自带的界面工具比较简单,这里推荐使用另外一个UI:XHProf.io。下载解压到web目录下面,重命名/xhprof/includes/目录下的config.inc.sample.php为/xhprof/includes/config.inc.php,并更改文件:

return array(
	'url_base' => 'http://192.168.84.2:8502/', //本地的XHProf.io站点
	'url_static' => null, // When undefined, it defaults to $config['url_base'] . 'public/'. This should be absolute URL.
	'pdo' => new PDO('mysql:dbname=test;host=192.168.84.3;charset=utf8', 'root', 'root') //本地的数据库配置,只支持PDO
);

在test数据库上运行/xhprof/setup/database.sql,创建性能检测相关的表结构。
由于XHProf会去github上检测版本,国内访问较慢,建议不要检测。更改/xhprof/includes/bootstrap.inc.php如下:

	curl_setopt_array($ch, array(
		CURLOPT_URL => 'http://192.168.84.2:8502/version.json', //本地的XHProf.io站点下面
		CURLOPT_HEADER => FALSE,
		CURLOPT_RETURNTRANSFER => TRUE
	));

然后更改php.ini配置,在最后加上以下内容:

;xhprof
[xhprof]
extension=xhprof.so;
xhprof.output_dir=/tmp/xhprof

; Automatically add files before PHP document.
; XHProf.io站点下的prepend.php
auto_prepend_file = /usr/local/nginx/xhprof/inc/prepend.php

; Automatically add files after PHP document.
; XHProf.io站点下的append.php
auto_append_file = /usr/local/nginx/xhprof/inc/append.php

auto_prepend_file为每次php脚本运行前,自动加载并运行的文件;auto_append_file为每次php脚本运行后,自动加载并运行的文件。这样可以省去每次在需要检测的php文件里面写xhprof_enable/xhprof_disable等调用代码,不必更改原有代码(无侵入)。
注意:如果你访问了php页面却收集不到情况,可能是你的代码里面写了exit/die终止了程序执行,导致auto_append_file未加载执行,去掉exit/die就可以了。另外,如果提示:Unexpected system behaviour,可能是数据库连接不上,PDO、mbstring扩展未安装,响应数据里面带有错误信息的等等。

重启php-fpm:

#重启php-fpm
kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`

xhprof1

访问该服务器上php页面,即可以在http://192.168.84.2:8502/看到检测情况。
xhprof5
xhprof4
xhprof6

参数说明
Inclusive Time 包括子函数所有执行时间。
Exclusive Time/Self Time 函数执行本身花费的时间,不包括子树执行时间。
Wall Time 花去了的时间或挂钟时间。
CPU Time 用户耗的时间+内核耗的时间
Inclusive CPU 包括子函数一起所占用的CPU
Exclusive CPU 函数自身所占用的CPU

参考链接:
给CentOS6.3 + PHP5.3 安装PHP性能测试工具 XHProf-0.9.2
xhprof.io/INSTALL.md
php auto_append_file

开启Firephp时Nginx PHP-FPM下502错误解决

在自己的电脑上调试的好好的,部署到Linux Nginx环境的时候却发现有些PHP页面在Firefox下面会返回502,在IE下面却是正常的,甚至在chrome下面也会出现502。顺着Nginx,Firefox这两个关键字,终于找到原因:原来是开启Firephp(chrome装了webug)时,Firephp的调试信息会写入的请求头里面,导致Nginx的FastCGI缓冲区超出,从而返回502。在自己的电脑上之所以不会是因为使用的是Apache。
于是顺着其他人的解答修改了nginx.conf中fastcig相关的参数:

fastcgi_buffer_size 1024k;
fastcgi_buffers 8 512k;
fastcgi_busy_buffers_size 1024k;
fastcgi_temp_file_write_size 1024k;

一开始这些参数是256,后来改成了512还是有部分页面会出现502,再改成1024终于好了。 继续阅读

Nginx做简单的反向代理

Nginx是一个HTTP和反向代理服务器,可做为反向代理实现负载均衡的例子,也可以作为代理服务器,也经常作为web服务器使用。如果一台服务器装有多个web服务器则必须监听多个端口,对于用户的访问将是不友好,则需要一个统一的前端来进行分发。本文只是简单的利用Nginx作为前端代理服务器,代理不同的后端服务器:IIS和Apache,实现不同的域名解析至不同的服务器。

首先更改IIS的监听端口为8000,Apache的监听端口为9000,保证Nginx占有80端口和443端口。然后更改nginx.conf添加以下内容 继续阅读