欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

PHP Nginx MySQL 高并发调优 小试

程序员文章站 2022-05-21 16:38:12
...
项目要求实现一个免费抢券的功能,涉及到高并发的问题,研究了几天,记录下来,欢迎工友们扔砖头~~

整个项目是PHP+Nginx+Mysql的架构,由于PHP是阻塞的单线程模型,不支持多线程,因此也没有Java那么好用的同步机制,我想到的办法就是在数据库级别做相应的同步互斥的控制,Mysql的锁机制我放在了Mysql数据库锁机制这篇博文当中。通过查看Mysql官方文档,我想到了两种解决方案:一、使用LOCK TABLE 或START TRANSACTION 写SQL 语句; 二、使用CREATE PROCEDURE 直接在数据库中创建存储过程,接下来我就分别试了这两种方法。

一、 使用锁机制

SET autocommit=0;LOCK TABLE test;select count(*) from test where value=1;COMMIT;
这是 查询当天中奖的用户(为了示意简化了业务逻辑),然后我用PHP做一个判断:是否中奖用户超过了当天的限额,没超过则该用户中奖,那么此时要UPDATE 一下数据库,若两个用户同时读取中奖用户总数,其中一个update了数据库,另一个用户读到的自然是脏数据,这也就是为什么我没有释放刚才那张表的锁,按照业务逻辑,是要跳出mysql用程序判断一下,然后update数据库再释放锁。

update test(name,value) values('Tomcat',1);COMMIT;UNLOCK TABLE;

这种方法的缺点在于使用了两次数据库连接,中间插入了PHP判断,必定会造成性能上的损失,好处是数据库不必插入业务逻辑,松耦合。


二、 使用存储过程

DELIMITER //DROP PROCEDURE IF EXISTS proc;CREATE PROCEDURE proc(IN cnt INT,IN user VARCHAR(32))BEGIN	DECLARE num INT;	DECLARE success INT;	select count(*) INTO num from test where value=1;	IF num
稍微解释一下代码(熟悉的工友请pass):1. 将mysql默认的分隔符分号重定义为// 避免mysql 只执行其中一句话;2. 创建存储过程传入参数cnt (中奖用户限额), user (此次抢票的用户); 3. 定义两个临时变量num (目前中奖用户数), success(是否中奖);4.查询当前中奖用户数目,未超额则插入用户状态1,反之0 ; 5. 返回中奖与否标志,恢复mysql的sql分隔符.

在php中调用此存储过程: $db->query("call proc(100,'hehe')");

此方法的缺点是在数据库引入了业务逻辑,程序修改不易,优点是只使用一次数据库连接,表的锁定时间大大减少,并发效率很高。


三、 奇葩windows环境下的PHP

在我满怀欣喜的开始模拟高并发用户访问的时候,问题来了。。。

先贴 java 写的多线程并发访问程序(php不支持多线程。。)

import java.util.concurrent.CyclicBarrier;import com.test.run.ThreadTest;public class Test {	public static void main(String[] args) {		CyclicBarrier cb=new CyclicBarrier(100);	//fork 100个线程		ThreadTest[] ttarray=new ThreadTest[100];	//待这些线程fork完毕,同时发起http请求		for (int i = 0; i