Inhaltsverzeichnis
1. Auf MySQL basierende Lösung time Der Prozess liest den Wert von num. Nachdem die Transaktion festgeschrieben oder zurückgesetzt wurde, wird die Sperre aufgehoben und andere Prozesse können sie lesen.
2. Redis-basierte Lösung
Heim Backend-Entwicklung PHP-Tutorial Wie kann das Problem der hohen Parallelität (Produkt-Flash-Verkäufe) in PHP gelöst werden? Zwei gemeinsame Lösungen

Wie kann das Problem der hohen Parallelität (Produkt-Flash-Verkäufe) in PHP gelöst werden? Zwei gemeinsame Lösungen

Feb 23, 2022 am 10:52 AM
php 高并发

Wie kann das Problem der hohen Parallelität (Produkt-Flash-Verkäufe) in PHP gelöst werden? Im folgenden Artikel werden zwei Lösungen vorgestellt (basierend auf MySQL oder basierend auf Redis). Ich hoffe, dass er Ihnen hilfreich sein wird.

Wie kann das Problem der hohen Parallelität (Produkt-Flash-Verkäufe) in PHP gelöst werden? Zwei gemeinsame Lösungen

Das zweite Töten führt zu einer sofortigen hohen Parallelität. Die Verwendung der Datenbank erhöht den Zugriffsdruck auf die Datenbank und verringert die Zugriffsgeschwindigkeit. Daher sollten wir Caching verwenden, um den Zugriffsdruck auf die Datenbank zu verringern

Sie können sehen Der Vorgang hier unterscheidet sich von der ursprünglichen Bestellung: Die generierte Flash-Sale-Vorbestellung wird nicht sofort in die Datenbank geschrieben, sondern zuerst in den Cache. Wenn der Benutzer erfolgreich bezahlt, wird der Status geändert und geschrieben die Datenbank.

Angenommen, num ist ein in der Datenbank gespeichertes Feld, in dem die verbleibende Menge des Flash-getöteten Produkts gespeichert ist.

if($num > 0){
  //用户抢购成功,记录用户信息
  $num--;
}
Nach dem Login kopieren

Angenommen, in einem Szenario mit hoher Parallelität ist der Wert von num in der Datenbank 1, und mehrere Prozesse lesen möglicherweise gleichzeitig, dass num 1 ist. Das Programm stellt fest, dass die Bedingungen erfüllt sind erfolgreich, und num wird um eins reduziert.

Dies führt zu einer Überlieferung an Produkten. Ursprünglich konnten nur 10 Produkte geschnappt werden, aber zu diesem Zeitpunkt wird die Anzahl nach Abschluss des Ansturms negativ sein.

Es gibt viele Lösungen für dieses Problem, die einfach in Lösungen basierend auf MySQL und Redis unterteilt werden können. Die Leistung von Redis ist auf MySQL zurückzuführen, sodass eine höhere Parallelität möglich ist Single MySQL und Redis erfordern eine höhere Parallelität verteilter Lösungen, die in diesem Artikel nicht behandelt werden.

1. Auf MySQL basierende Lösung time Der Prozess liest den Wert von num. Nachdem die Transaktion festgeschrieben oder zurückgesetzt wurde, wird die Sperre aufgehoben und andere Prozesse können sie lesen.

Diese Lösung ist die einfachste und am leichtesten zu verstehende Lösung. Sie können diese Lösung direkt verwenden, wenn die Leistungsanforderungen nicht hoch sind. Es ist zu beachten, dass SELECT … FOR UPDATE so viele Indizes wie möglich verwenden sollte, um so wenige Zeilen wie möglich zu sperren.

Exklusive Sperren werden erst freigegeben, nachdem die Transaktionsausführung abgeschlossen ist, nicht nachdem der Lesevorgang abgeschlossen ist Es wird später freigegeben, daher sollte die verwendete Transaktion so früh wie möglich festgeschrieben oder zurückgesetzt werden, um die exklusive Sperre früher aufzuheben.

CREATE TABLE `goods` (
 `id` int(11) NOT NULL,
 `num` int(11) DEFAULT NULL,
 `version` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Nach dem Login kopieren

②Optimistische Sperre

Die optimistische Sperrlösung fügt beim Lesen von Daten keine exklusive Sperre hinzu, sondern löst sie durch ein Versionsfeld, das bei jeder Aktualisierung automatisch erhöht wird Dann kann das Problem erfolgreich aktualisiert werden. Wenn jeder Prozess num liest, liest er auch den Wert von version. Beim Aktualisieren von num aktualisiert er auch die Version und fügt beim Aktualisieren eine Äquivalenzbeurteilung zur Version hinzu.

Angenommen, 10 Prozesse haben gelesen, dass der Wert von num 1 und der Wert von version 9 ist. Dann lauten die von diesen 10 Prozessen ausgeführten Aktualisierungsanweisungen alle UPDATE waren SET num=num-1, version=version+ 1 WHERE version=9, SELECT … FOR UPDATE要尽可能的使用索引,以便锁定尽可能少的行数;

排他锁是在事务执行结束之后才释放的,不是读取完成之后就释放,因此使用的事务应该尽可能的早些提交或回滚,以便早些释放排它锁。

CREATE TABLE `log` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `good_id` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Nach dem Login kopieren

②乐观锁

乐观锁的方案在读取数据是并没有加排他锁,而是通过一个每次更新都会自增的version字段来解决,多个进程读取到相同num,然后都能更新成功的问题。在每个进程读取num的同时,也读取version的值,并且在更新num的同时也更新version,并在更新时加上对version的等值判断。

假设有10个进程都读取到了num的值为1,version值为9,则这10个进程执行的更新语句都是UPDATE goods SET num=num-1,version=version+1 WHERE version=9

Wenn jedoch einer der Prozesse erfolgreich ausgeführt wird, wird der Versionswert in der Datenbank zu 10 und die verbleibenden 9 Prozesse werden nicht erfolgreich ausgeführt, wodurch sichergestellt wird, dass das Produkt erfolgreich ausgeführt wird nicht überliefert werden, der Wert von num wird nicht kleiner als 0 sein, aber dies führt auch zu einem Problem, das heißt, Benutzer, die zuvor eine Snap-up-Anfrage gestellt haben, können diese möglicherweise nicht abrufen, werden aber erfasst durch spätere Anfragen.

$this->mysqli->begin_transaction();
$result = $this->mysqli->query("SELECT num FROM goods WHERE id=1 LIMIT 1 FOR UPDATE");
$row = $result->fetch_assoc();
$num = intval($row['num']);
if($num > 0){
  usleep(100);
  $this->mysqli->query("UPDATE goods SET num=num-1");
  $affected_rows = $this->mysqli->affected_rows;
  if($affected_rows == 1){
    $this->mysqli->query("INSERT INTO log(good_id) VALUES({$num})");
    $affected_rows = $this->mysqli->affected_rows;
    if($affected_rows == 1){
      $this->mysqli->commit();
      echo "success:".$num;
    }else{
      $this->mysqli->rollback();
      echo "fail1:".$num;
    }
  }else{
    $this->mysqli->rollback();
    echo "fail2:".$num;
  }
}else{
  $this->mysqli->commit();
  echo "fail3:".$num;
}
Nach dem Login kopieren

③wo Bedingung (atomare Operation)

Das pessimistische Sperrschema stellt sicher, dass der Wert von num in der Datenbank nur von einem Prozess gleichzeitig gelesen und verarbeitet werden kann, d. h. gleichzeitige Leseprozesse müssen dies tun Warteschlange hier oben. Nacheinander ausführen.

Optimistisches Sperrschema Obwohl der Wert von num von mehreren Prozessen gleichzeitig gelesen werden kann, kann durch die Äquivalenzbeurteilung der Version im Aktualisierungsvorgang sichergestellt werden, dass nur ein Update gleichzeitiger Aktualisierungsvorgänge gleichzeitig erfolgreich sein kann.

Es gibt auch eine einfachere Lösung, die darin besteht, während des Aktualisierungsvorgangs nur die bedingte Einschränkung von num>0 hinzuzufügen. Obwohl die durch die Where-Bedingung eingeschränkte Lösung der optimistischen Sperrlösung ähnlich zu sein scheint und das Auftreten von Überausgabeproblemen verhindern kann, ist die Leistung bei großer Anzahl immer noch sehr unterschiedlich.

Angenommen, num ist zu diesem Zeitpunkt 10 und 5 Prozesse lesen gleichzeitig num=10. Aufgrund der Gleichheitsbeurteilung des Versionsfelds wird nur einer dieser 5 Prozesse erfolgreich aktualisiert Diese 5 Prozesse werden nach Abschluss ausgeführt.

Für die Where-Bedingungslösung beträgt die Anzahl nach Abschluss der Ausführung dieser 5 Prozesse 5, solange Anzahl> 0 erfolgreich aktualisiert werden kann.

$result = $this->mysqli->query("SELECT num,version FROM goods WHERE id=1 LIMIT 1");
$row = $result->fetch_assoc();
$num = intval($row['num']);
$version = intval($row['version']);
if($num > 0){
  usleep(100);
  $this->mysqli->begin_transaction();
  $this->mysqli->query("UPDATE goods SET num=num-1,version=version+1 WHERE version={$version}");
  $affected_rows = $this->mysqli->affected_rows;
  if($affected_rows == 1){
    $this->mysqli->query("INSERT INTO log(good_id) VALUES({$num})");
    $affected_rows = $this->mysqli->affected_rows;
    if($affected_rows == 1){
      $this->mysqli->commit();
      echo "success:".$num;
    }else{
      $this->mysqli->rollback();
      echo "fail1:".$num;
    }
  }else{
    $this->mysqli->rollback();
    echo "fail2:".$num;
  }
}else{
  echo "fail3:".$num;
}
Nach dem Login kopieren

2. Redis-basierte Lösung

① Watch-basiertes optimistisches Sperrschema

watch wird verwendet, um einen (oder mehrere) Schlüssel zu überwachen. Wenn dieser (oder diese) Schlüssel vor der Transaktion vorhanden sind ausgeführt. Bei Änderung durch andere Befehle wird die Transaktion unterbrochen.

Diese Lösung ähnelt der optimistischen Sperrlösung in MySQL und die spezifische Leistung ist dieselbe.

$result = $this->mysqli->query("SELECT num FROM goods WHERE id=1 LIMIT 1");
$row = $result->fetch_assoc();
$num = intval($row['num']);
if($num > 0){
  usleep(100);
  $this->mysqli->begin_transaction();
  $this->mysqli->query("UPDATE goods SET num=num-1 WHERE num>0");
  $affected_rows = $this->mysqli->affected_rows;
  if($affected_rows == 1){
    $this->mysqli->query("INSERT INTO log(good_id) VALUES({$num})");
    $affected_rows = $this->mysqli->affected_rows;
    if($affected_rows == 1){
      $this->mysqli->commit();
      echo "success:".$num;
    }else{
      $this->mysqli->rollback();
      echo "fail1:".$num;
    }
  }else{
    $this->mysqli->rollback();
    echo "fail2:".$num;
  }
}else{
  echo "fail3:".$num;
}
Nach dem Login kopieren

②Listenbasierte Warteschlangenlösung

基于队列的方案利用了redis出队操作的原子性,抢购开始之前首先将商品编号放入响应的队列中,在抢购时依次从队列中弹出操作,这样可以保证每个商品只能被一个进程获取并操作,不存在超发的情况。

该方案的优点是理解和实现起来都比较简单,缺点是当商品数量较多是,需要将大量的数据存入到队列中,并且不同的商品需要存入到不同的消息队列中。

public function init(){
  $this->redis->del('goods');
  for($i=1;$i<=10;$i++){
    $this->redis->lPush(&#39;goods&#39;,$i);
  }
  $this->redis->del(&#39;result&#39;);
  echo &#39;init done&#39;;
}
public function run(){
  $goods_id = $this->redis->rPop(&#39;goods&#39;);
  usleep(100);
  if($goods_id == false) {
    echo "fail1";
  }else{
    $res = $this->redis->lPush(&#39;result&#39;,$goods_id);
    if($res == false){
      echo "writelog:".$goods_id;
    }else{
      echo "success".$goods_id;
    }
  }
}
Nach dem Login kopieren

③基于decr返回值的方案

如果我们将剩余量num设置为一个键值类型,每次先get之后判断,然后再decr是不能解决超发问题的。

但是redis中的decr操作会返回执行后的结果,可以解决超发问题。我们首先get到num的值进行第一步判断,避免每次都去更新num的值,然后再对num执行decr操作,并判断decr的返回值,如果返回值不小于0,这说明decr之前是大于0的,用户抢购成功。

public function run(){
  $num = $this->redis->get(&#39;num&#39;);
  if($num > 0) {
    usleep(100);
    $retNum = $this->redis->decr(&#39;num&#39;);
    if($retNum >= 0){
      $res = $this->redis->lPush(&#39;result&#39;,$retNum);
      if($res == false){
        echo "writeLog:".$retNum;
      }else{
        echo "success:".$retNum;
      }
    }else{
      echo "fail1";
    }
  }else{
    echo "fail2";
  }
}
Nach dem Login kopieren

④基于setnx的排它锁方案

redis没有像mysql中的排它锁,但是可以通过一些方式实现排它锁的功能,就类似php使用文件锁实现排它锁一样。

setnx实现了exists和set两个指令的功能,若给定的key已存在,则setnx不做任何动作,返回0;若key不存在,则执行类似set的操作,返回1。

我们设置一个超时时间timeout,每隔一定时间尝试setnx操作,如果设置成功就是获得了相应的锁,执行num的decr操作,操作完成删除相应的key,模拟释放锁的操作。

public function run(){
  do {
    $res = $this->redis->setnx("numKey",1);
    $this->timeout -= 100;
    usleep(100);
  }while($res == 0 && $this->timeout>0);
  if($res == 0){
    echo &#39;fail1&#39;;
  }else{
    $num = $this->redis->get(&#39;num&#39;);
    if($num > 0) {
      $this->redis->decr(&#39;num&#39;);
      usleep(100);
      $res = $this->redis->lPush(&#39;result&#39;,$num);
      if($res == false){
        echo "fail2";
      }else{
        echo "success:".$num;
      }
    }else{
      echo "fail3";
    }
    $this->redis->del("numKey");
  }
}
Nach dem Login kopieren

推荐学习:《PHP视频教程

Das obige ist der detaillierte Inhalt vonWie kann das Problem der hohen Parallelität (Produkt-Flash-Verkäufe) in PHP gelöst werden? Zwei gemeinsame Lösungen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Wie man alles in Myrise freischaltet
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

PHP 8.4 Installations- und Upgrade-Anleitung für Ubuntu und Debian PHP 8.4 Installations- und Upgrade-Anleitung für Ubuntu und Debian Dec 24, 2024 pm 04:42 PM

PHP 8.4 bringt mehrere neue Funktionen, Sicherheitsverbesserungen und Leistungsverbesserungen mit einer beträchtlichen Menge an veralteten und entfernten Funktionen. In dieser Anleitung wird erklärt, wie Sie PHP 8.4 installieren oder auf PHP 8.4 auf Ubuntu, Debian oder deren Derivaten aktualisieren. Obwohl es möglich ist, PHP aus dem Quellcode zu kompilieren, ist die Installation aus einem APT-Repository wie unten erläutert oft schneller und sicherer, da diese Repositorys in Zukunft die neuesten Fehlerbehebungen und Sicherheitsupdates bereitstellen.

CakePHP Datum und Uhrzeit CakePHP Datum und Uhrzeit Sep 10, 2024 pm 05:27 PM

Um in cakephp4 mit Datum und Uhrzeit zu arbeiten, verwenden wir die verfügbare FrozenTime-Klasse.

Besprechen Sie CakePHP Besprechen Sie CakePHP Sep 10, 2024 pm 05:28 PM

CakePHP ist ein Open-Source-Framework für PHP. Es soll die Entwicklung, Bereitstellung und Wartung von Anwendungen erheblich vereinfachen. CakePHP basiert auf einer MVC-ähnlichen Architektur, die sowohl leistungsstark als auch leicht zu verstehen ist. Modelle, Ansichten und Controller gu

CakePHP-Datei hochladen CakePHP-Datei hochladen Sep 10, 2024 pm 05:27 PM

Um am Datei-Upload zu arbeiten, verwenden wir den Formular-Helfer. Hier ist ein Beispiel für den Datei-Upload.

CakePHP erstellt Validatoren CakePHP erstellt Validatoren Sep 10, 2024 pm 05:26 PM

Der Validator kann durch Hinzufügen der folgenden zwei Zeilen im Controller erstellt werden.

So richten Sie Visual Studio-Code (VS-Code) für die PHP-Entwicklung ein So richten Sie Visual Studio-Code (VS-Code) für die PHP-Entwicklung ein Dec 20, 2024 am 11:31 AM

Visual Studio Code, auch bekannt als VS Code, ist ein kostenloser Quellcode-Editor – oder eine integrierte Entwicklungsumgebung (IDE) –, die für alle gängigen Betriebssysteme verfügbar ist. Mit einer großen Sammlung von Erweiterungen für viele Programmiersprachen kann VS Code c

CakePHP-Kurzanleitung CakePHP-Kurzanleitung Sep 10, 2024 pm 05:27 PM

CakePHP ist ein Open-Source-MVC-Framework. Es erleichtert die Entwicklung, Bereitstellung und Wartung von Anwendungen erheblich. CakePHP verfügt über eine Reihe von Bibliotheken, um die Überlastung der häufigsten Aufgaben zu reduzieren.

Wie analysiert und verarbeitet man HTML/XML in PHP? Wie analysiert und verarbeitet man HTML/XML in PHP? Feb 07, 2025 am 11:57 AM

Dieses Tutorial zeigt, wie XML -Dokumente mit PHP effizient verarbeitet werden. XML (Extensible Markup-Sprache) ist eine vielseitige textbasierte Markup-Sprache, die sowohl für die Lesbarkeit des Menschen als auch für die Analyse von Maschinen entwickelt wurde. Es wird üblicherweise für die Datenspeicherung ein verwendet und wird häufig verwendet

See all articles