Cet article vous apporte des connaissances pertinentes sur PHP, qui introduit principalement des problèmes liés à la prévention des stocks de marchandises survendus dans des conditions de concurrence élevée, résolvant principalement la pression de la concurrence élevée sur la base de données et comment résoudre le problème de la concurrence. d'inventaire de produits survendus, j'espère que cela sera utile à tout le monde.

Vous pouvez consulter les cas de test basés sur cet article via "Test de haute concurrence PHP : étude de cas sur la prévention de la survente des stocks". [Apprentissage recommandé : "Tutoriel PHP"]
Dans le système du centre commercial, les ventes précipitées et les ventes flash sont des scénarios marketing très courants. Au cours d'une certaine période de temps, un grand nombre d'utilisateurs visitent le centre commercial pour y passer des commandes. Il y a deux problèmes principaux qui doivent être résolus :
Pour la première question, utiliser la mise en cache est utilisée pour éviter d'exploiter directement la base de données, comme en utilisant Redis. Comment résoudre le problème de stock de produits survendu dans une situation concurrentielle
Pour la deuxième question, il faut se concentrer sur l'explication. Méthode d'écriture conventionnelle : interrogez l'inventaire du produit correspondant, déterminez si la quantité d'inventaire est supérieure à 0, puis effectuez des opérations telles que la génération d'une commande. Cependant, lors de la détermination si l'inventaire est supérieur à 0, il y aura des problèmes. dans des conditions de concurrence élevée, ce qui entraîne une quantité de stock négative.
Test table sql
Importez les données de la table suivante dans la base de données
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | <strong>
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for products
-- ----------------------------
DROP TABLE IF EXISTS `products`;
CREATE TABLE `products` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`title` varchar(50) DEFAULT NULL COMMENT '货品名称',
`store` int(11) DEFAULT '0' COMMENT '货品库存',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='货品表';
-- ----------------------------
-- Records of products
-- ----------------------------
INSERT INTO `products` VALUES ('1', '稻花香大米', '20');
-- ----------------------------
-- Table structure for order_log
-- ----------------------------
DROP TABLE IF EXISTS `order_log`;
CREATE TABLE `order_log` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`content` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '日志内容',
`c_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for order
-- ----------------------------
DROP TABLE IF EXISTS `order`;
CREATE TABLE `order` (
`oid` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '订单号',
`product_id` int(11) DEFAULT '0' COMMENT '商品ID',
`number` int(11) DEFAULT '0' COMMENT '购买数量',
`c_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`oid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='订单表';</strong>
|
Copier après la connexion
Code de traitement des commandes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | <?php
db();
global $con ;
$product_id = 1;
$buy_num = 1;
$sql = "select * from products where id={$product_id}" ;
$result = mysqli_query( $con , $sql );
$row = mysqli_fetch_assoc( $result );
if ( $row ['store'] > 0) {
sleep(1);
$sql = "update products set store=store-{$buy_num} where id={$product_id}" ;
if (mysqli_query( $con , $sql )) {
echo "更新成功" ;
$oid = build_order_no();
create_order( $oid , $product_id , $buy_num );
insertLog('库存减少成功,下单成功');
} else {
echo "更新失败" ;
insertLog('库存减少失败');
}
} else {
echo "没有库存" ;
insertLog('库存不够');
}
function db()
{
global $con ;
$con = new mysqli('localhost','root','root','test');
if (! $con ) {
echo "数据库连接失败" ;
}
}
function build_order_no()
{
return date ('Ymd') . str_pad (mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
}
function create_order( $oid , $product_id , $number )
{
global $con ;
$sql = "INSERT INTO `order` (oid, product_id, number) values('$oid', '$product_id', '$number')" ;
mysqli_query( $con , $sql );
}
function insertLog( $content )
{
global $con ;
$sql = "INSERT INTO `order_log` (content) values('$content')" ;
mysqli_query( $con , $sql );
}
|
Copier après la connexion
Définissez le champ du champ d'inventaire sur non signé
Parce que les champs d'inventaire ne peuvent pas être un nombre négatif. Lors de la mise à jour de l'inventaire du produit après avoir passé une commande, si un nombre négatif se produit, false sera renvoyé
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | <strong><?php
db();
global $con ;
$product_id = 1;
$buy_num = 1;
$sql = "select * from products where id={$product_id} for UPDATE" ;
$result = mysqli_query( $con , $sql );
$row = mysqli_fetch_assoc( $result );
if ( $row ['store'] > 0) {
sleep(1);
$sql = "update products set store=store-{$buy_num} where id={$product_id}" ;
if (mysqli_query( $con , $sql )) {
echo "更新成功" ;
$oid = build_order_no();
create_order( $oid , $product_id , $buy_num );
insertLog('库存减少成功,下单成功');
} else {
echo "更新失败" ;
insertLog('库存减少失败');
}
} else {
echo "没有库存" ;
insertLog('库存不够');
}
function db()
{
global $con ;
$con = new mysqli('localhost','root','root','test');
if (! $con ) {
echo "数据库连接失败" ;
}
}
function build_order_no()
{
return date ('Ymd') . str_pad (mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
}
function create_order( $oid , $product_id , $number )
{
global $con ;
$sql = "INSERT INTO `order` (oid, product_id, number) values('$oid', '$product_id', '$number')" ;
mysqli_query( $con , $sql );
}
function insertLog( $content )
{
global $con ;
$sql = "INSERT INTO `order_log` (content) values('$content')" ;
mysqli_query( $con , $sql );
}</strong>
|
Copier après la connexion
Utilisez la transaction mysql pour verrouiller la ligne de l'opération
Pendant le processus de traitement de la commande, la transaction en utilisant MySQL, vous passerez la commande Verrouillage des données de la ligne de produits
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | <strong><?php
db();
global $con ;
$product_id = 1;
$buy_num = 1;
mysqli_query( $con , "BEGIN" );
$sql = "select * from products where id={$product_id} for UPDATE" ;
$result = mysqli_query( $con , $sql );
$row = mysqli_fetch_assoc( $result );
if ( $row ['store'] > 0) {
sleep(1);
$sql = "update products set store=store-{$buy_num} where id={$product_id}" ;
if (mysqli_query( $con , $sql )) {
echo "更新成功" ;
$oid = build_order_no();
create_order( $oid , $product_id , $buy_num );
insertLog('库存减少成功,下单成功');
mysqli_query( $con , "COMMIT" );
} else {
echo "更新失败" ;
insertLog('库存减少失败');
mysqli_query( $con , "ROLLBACK" );
}
} else {
echo "没有库存" ;
insertLog('库存不够');
mysqli_query( $con , "ROLLBACK" );
}
function db()
{
global $con ;
$con = new mysqli('localhost','root','root','test');
if (! $con ) {
echo "数据库连接失败" ;
}
}
function build_order_no()
{
return date ('Ymd') . str_pad (mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
}
function create_order( $oid , $product_id , $number )
{
global $con ;
$sql = "INSERT INTO `order` (oid, product_id, number) values('$oid', '$product_id', '$number')" ;
mysqli_query( $con , $sql );
}
function insertLog( $content )
{
global $con ;
$sql = "INSERT INTO `order_log` (content) values('$content')" ;
mysqli_query( $con , $sql );
}</strong>
|
Copier après la connexion
Utiliser le verrouillage exclusif de fichier non bloquant
Lors du traitement d'une demande de commande, utilisez flock pour verrouiller un fichier. Si le verrouillage échoue, cela signifie que d'autres commandes. sont en cours de traitement, vous pouvez soit attendre, soit directement. L'utilisateur est informé que "le serveur est occupé" et le compteur stocke le nombre de produits achetés pour éviter d'interroger la base de données. Mode de blocage (attente) : pendant la simultanéité, lorsqu'il y a une deuxième demande d'utilisateur, le programme attendra que la première demande d'utilisateur se termine, libérera le verrou et obtiendra le verrouillage du fichier avant que le programme continue de s'exécuter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | <strong><?php
db();
global $con ;
$product_id = 1;
$buy_num = 1;
$fp = fopen ('lock.txt', 'w');
if ( flock ( $fp , LOCK_EX)) {
$sql = "select * from products where id={$product_id}" ;
$result = mysqli_query( $con , $sql );
$row = mysqli_fetch_assoc( $result );
if ( $row ['store'] > 0) {
sleep(1);
$sql = "update products set store=store-{$buy_num} where id={$product_id}" ;
if (mysqli_query( $con , $sql )) {
echo "更新成功" ;
$oid = build_order_no();
create_order( $oid , $product_id , $buy_num );
insertLog('库存减少成功,下单成功');
} else {
echo "更新失败" ;
insertLog('库存减少失败');
}
} else {
echo "没有库存" ;
insertLog('库存不够');
}
flock ( $fp , LOCK_UN);
}
fclose( $fp );
function db()
{
global $con ;
$con = new mysqli('localhost','root','root','test');
if (! $con ) {
echo "数据库连接失败" ;
}
}
function build_order_no()
{
return date ('Ymd') . str_pad (mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
}
function create_order( $oid , $product_id , $number )
{
global $con ;
$sql = "INSERT INTO `order` (oid, product_id, number) values('$oid', '$product_id', '$number')" ;
mysqli_query( $con , $sql );
}
function insertLog( $content )
{
global $con ;
$sql = "INSERT INTO `order_log` (content) values('$content')" ;
mysqli_query( $con , $sql );
}</strong>
|
Copier après la connexion
Mode non bloquant : lors de la concurrence, le premier utilisateur demande et obtient le verrouillage du fichier. Les utilisateurs qui demandent plus tard reviendront directement au système étant occupé, veuillez réessayer plus tard
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | <?php
db();
global $con ;
$product_id = 1;
$buy_num = 1;
$fp = fopen ('lock.txt', 'w');
if ( flock ( $fp , LOCK_EX|LOCK_NB)) {
$sql = "select * from products where id={$product_id}" ;
$result = mysqli_query( $con , $sql );
$row = mysqli_fetch_assoc( $result );
if ( $row ['store'] > 0) {
sleep(1);
$sql = "update products set store=store-{$buy_num} where id={$product_id}" ;
if (mysqli_query( $con , $sql )) {
echo "更新成功" ;
$oid = build_order_no();
create_order( $oid , $product_id , $buy_num );
insertLog('库存减少成功,下单成功');
} else {
echo "更新失败" ;
insertLog('库存减少失败');
}
} else {
echo "没有库存" ;
insertLog('库存不够');
}
flock ( $fp , LOCK_UN);
} else {
echo "系统繁忙,请稍后再试" ;
insertLog('系统繁忙,请稍后再试');
}
fclose( $fp );
function db()
{
global $con ;
$con = new mysqli('localhost','root','root','test');
if (! $con ) {
echo "数据库连接失败" ;
}
}
function build_order_no()
{
return date ('Ymd') . str_pad (mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
}
function create_order( $oid , $product_id , $number )
{
global $con ;
$sql = "INSERT INTO `order` (oid, product_id, number) values('$oid', '$product_id', '$number')" ;
mysqli_query( $con , $sql );
}
function insertLog( $content )
{
global $con ;
$sql = "INSERT INTO `order_log` (content) values('$content')" ;
mysqli_query( $con , $sql );
}
|
Copier après la connexion
Utiliser la file d'attente redis
Parce que l'opération pop est atomique, même si de nombreux utilisateurs arrivent en même temps, ils le feront être exécuté dans l'ordre. Il est recommandé d'utiliser
redis prévention du verrouillage optimiste Survente
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <?php
$redis = new Redis();
$redis ->connect( "127.0.0.1" , 6379);
$redis ->watch('sales');
$sales = $redis ->get('sales');
$n = 20;
if ( $sales >= $n ) {
exit ('秒杀结束');
}
$redis ->multi();
$redis ->incr('sales');
$res = $redis -> exec ();
if ( $res ) {
$con = new mysqli('localhost','root','root','test');
if (! $con ) {
echo "数据库连接失败" ;
}
$product_id = 1;
$buy_num = 1;
sleep(1);
$sql = "update products set store=store-{$buy_num} where id={$product_id}" ;
if (mysqli_query( $con , $sql )) {
echo "秒杀完成" ;
}
} else {
exit ('抢购失败');
}
|
Copier après la connexion
Apprentissage recommandé : "Tutoriel vidéo PHP"
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!