高并发

Posted two-bees

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高并发相关的知识,希望对你有一定的参考价值。

一.介绍

在短时间之内对数据表(库)的集中访问,就称为“高并发”。
高并发 在使用的时候容易出现问题,
在短时间之内对数据表有大量的集中修改操作,如果不做控制,数据表的修改容易出现重复。
比如:
多个人操作获得的剩余量(95)是一致的(操作的时间点是同一个)
操作完毕对剩余量做减少操作,多人减少的数额(94)也是一样的
这样数据库剩余的数据量就不准确

新建数据表:

create table gbf_hot_goods(
    id MEDIUMINT UNSIGNED not null auto_increment comment 主键id,
    number TINYINT UNSIGNED not null default 0 comment 库存,
    name varchar(32) not null default ‘‘ comment 名称,
    price decimal(10,2) not null default 0 comment 价格,
    primary key (id)
)engine=Innodb charset=utf8;

insert into  gbf_hot_goods values(null,100,HUAWEI手机,5450);

正常整理库存:

<?php 

$pdo = new PDO(mysql:host=127.0.0.1;dbname=demo,root,‘‘);
$pdo->query(set names utf8);//设置字符集

//模拟购买商品,购买前先判断库存,购买后库存做减少操作
$sql = select number from gbf_hot_goods;
$qry = $pdo->query($sql);
$rst = $qry->fetch();//取出值
$num = $rst[number];
$num = $num - 3;//每次购买3个

$sql2 = update gbf_hot_goods set number=.$num;
$pdo->exec($sql2);

?>

  结果:

技术图片

二.模拟高并发处理

使用apache自带的小工具ab.exe进行模拟:

windows的cmd下apache/bin下> ab.exe -c 人数  -n 总请求数目 地址

ab.exe -c 10 -n 10 http://local-demo.cn/note/php/demo/high/demo.php

  执行

This is ApacheBench, Version 2.3 <$Revision: 1748469 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking local-demo.cn (be patient).....done


Server Software:        Apache/2.4.23
Server Hostname:        local-demo.cn
Server Port:            80

Document Path:          /note/php/demo/high/demo.php
Document Length:        0 bytes

Concurrency Level:      10
Time taken for tests:   2.030 seconds
Complete requests:      10
Failed requests:        0
Total transferred:      2220 bytes
html transferred:       0 bytes
Requests per second:    4.93 [#/sec] (mean)
Time per request:       2030.309 [ms] (mean)
Time per request:       203.031 [ms] (mean, across all concurrent requests)
Transfer rate:          1.07 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    1   0.5      1       2
Processing:    15  524 698.3     44    1989
Waiting:       13  523 698.2     43    1988
Total:         16  526 698.2     45    1990

Percentage of the requests served within a certain time (ms)
  50%     45
  66%   1025
  75%   1031
  80%   1034
  90%   1990
  95%   1990
  98%   1990
  99%   1990
 100%   1990 (longest request)

技术图片

请求10次后,number=73.正确应该等于70,所以已经出现了并发性问题。

注意,若等于70的话,可以多试几次,本地环境有可能受性能影响造成数据不准确。

高并发容易造成多个客户端同时处理程序,这样获得的数据结果有重复(错误)

解决:虽然是高并发,但是程序文件必须一个一个执行。

  设置锁即可。

锁有两种类型,php的锁、mysql的锁,

定义描述: 

  共享锁:又叫做读锁,所有人可以读同一个资源,但只有获取锁的人可以对其进行写操作。

  排它锁:只有获得锁的对象可以操作资源,在其释放锁之前其他人不能进行任何操作,读都不可以。

策略描述:

  悲观锁:在读取数据时锁住那几行,其他对这几行的更新需要等到悲观锁结束时才能继续 。
  乐观锁:读取数据时不锁,更新时检查是否数据已经被更新过,如果是则取消当前更新,一般在悲观锁的等待时间过长而不能接受时我们才会选择乐观锁。

mysql表锁:

<?php 

$pdo = new PDO(mysql:host=127.0.0.1;dbname=demo,root,‘‘);
$pdo->query(set names utf8);//设置字符集

//查询前先上锁:
$pdo->query(LOCK TABLES `gbf_hot_goods` WRITE);//也可以锁多个表,以,间隔,如:a READ,b WRITE,c READ,d WRITE

//模拟购买商品,购买前先判断库存,购买后库存做减少操作
$sql = select number from gbf_hot_goods;
$qry = $pdo->query($sql);
$rst = $qry->fetch();//取出值
$num = $rst[number];
$num = $num - 3;//每次购买3个

$sql2 = update gbf_hot_goods set number=.$num;
$pdo->exec($sql2);

//执行完后释放锁,即解锁
$pdo->query(UNLOCK TABLES);

?>

表锁的缺点是:因为同一时间只能有一人对表进行操作,所以会出现阻塞,如果同时锁多张表的话,还会影响整个网站相关表的加载。

mysql表锁:

  共享锁:SELECT `id` FROM  table WHERE id in(1,2)   LOCK IN SHARE MODE

  排它锁:SELECT `id` FROM mk_user WHERE id=1 FOR UPDATE。//须开启事务才有效,且要是innodb类型引擎,明确主键id,并有数据

<?php 

$pdo = new PDO(mysql:host=127.0.0.1;dbname=demo,root,‘‘);
$pdo->query(set names utf8);//设置字符集

//mysql行级锁,必须开启事务才有效
$pdo->beginTransaction();//开启事务
$sql = select number from gbf_hot_goods id = 1 FOR UPDATE;
$qry = $pdo->query($sql);
$rst = $qry->fetch();//取出值
$num = $rst[number];
if($num == 997){
    $pdo->rollback();//回滚的同时也会释放锁
}else{
    $num = $num - 3;//每次购买3个

$sql2 = update gbf_hot_goods set number=.$num. where id = 1;
$pdo->exec($sql2);
$pdo->commit();//提交的同时也会释放锁
}

?>

 

php锁:

  特点:当调用flock锁一个文件时,如果没有获取锁,直接返回FALSE,不会出现阻塞。

故,flock时判断是否等于false

  排它锁:flock($fp,LOCK_EX);

  共享锁:flock($fp,LOCK_SH);

  释放锁:flock($fp,LOCK_UN);

<?php 

$pdo = new PDO(mysql:host=127.0.0.1;dbname=demo,root,‘‘);
$pdo->query(set names utf8);//设置字符集

//查询前先上锁:
//$pdo->query(‘LOCK TABLES `gbf_hot_goods` WRITE‘);

//php的锁
$fp = fopen(01.php,r);//打开一个实际存在的物理文件,文件可以是空的
flock($fp,LOCK_EX);//上锁

//模拟购买商品,购买前先判断库存,购买后库存做减少操作
$sql = select number from gbf_hot_goods;
$qry = $pdo->query($sql);
$rst = $qry->fetch();//取出值
$num = $rst[number];
$num = $num - 3;//每次购买3个

$sql2 = update gbf_hot_goods set number=.$num;
$pdo->exec($sql2);

//执行完后释放锁,即解锁
//$pdo->query(‘UNLOCK TABLES‘);

//释放php锁
flock($fp,LOCK_UN);//解锁
fclose($fp);//释放锁的资源

?>

高并发进行上锁,可以确保所有人都是排队依次对程序进行访问。

避免负数:

$pdo->query(UPDATE warehouse SET `number` = `number` -1 WHERE `number` > 0);  //可以避免库存为负数

三.死锁

    `id`  主键索引

    `name` index 索引

    `age`  普通字段

死锁产生的根本原因:

  是两个以上的进程都要求对方释放资源,以至于进程都一直等待。在代码上是因为两个或者以上的事务都要求另一个释放资源。

死锁产生的四个必要条件:

  互斥条件、环路条件、请求保持、不可剥夺,缺一不可,相对应的只要破坏其中一种条件死锁就不会产生。

例如下面两条语句:

  第一条语句会优先使用`name`索引,因为name不是主键索引,还会用到主键索引。

  第二条语句是首先使用主键索引,再使用name索引。

  如果两条语句同时执行,第一条语句执行了name索引等待第二条释放主键索引,第二条执行了主键索引等待第一条的name索引,这样就造成了死锁。

 

  解决方法:改造第一条语句 使其根据主键值进行更新

#①
update user set name =1 where `name`=‘tom;
#②
update user set name=12  where id=12;
//改造后
update user set name=1 where id=(select id from user where name=tom );

 

以上是关于高并发的主要内容,如果未能解决你的问题,请参考以下文章

如何从设置中获取数据并发送到此片段

Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题

Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题

golang代码片段(摘抄)

全栈编程系列SpringBoot整合Shiro(含KickoutSessionControlFilter并发在线人数控制以及不生效问题配置启动异常No SecurityManager...)(代码片段

并发与高并发-并发模拟代码