Heim > Backend-Entwicklung > PHP-Tutorial > Exklusive Sperre in der PHP-Parallelitätskontrolle

Exklusive Sperre in der PHP-Parallelitätskontrolle

高洛峰
Freigeben: 2016-10-20 13:57:51
Original
1333 Leute haben es durchsucht

1. Parallelitätsprobleme
Jeder weiß, was Parallelität ist. Hier geht es um mehrere gleichzeitige Anfragen zur Belegung derselben Ressource.

Anfrage: index.php?mod =a&action =b&taskid=6 Verarbeitung:

$key = "a_b::".$uid.'_'.$taskid;
$v = $redis->get($key);
if($v == 1){
    $redis->setex($key,10,1);
    //处理逻辑省略
}
Nach dem Login kopieren

2. Analyse

Es stellt sich heraus, dass zwei identische Anfrageergebnisse in die Datenbank geschrieben wurden Die aufgezeichnete Zeit. Oh mein Gott! Ich habe Microtime (true) verwendet, um den Zeitunterschied zwischen den beiden Anfragen zu protokollieren. Das heißt, $redis ->setex($key,10,1); wurde nicht erfolgreich ausgeführt. Die zweite Anfrage hat das gleiche Ergebnis wie die erste Anfrage erhalten. Ist das nicht die legendäre gleichzeitige Ressourcenpräemption? Ich habe schon oft von dieser Situation gehört und sie während des Entwicklungsprozesses nicht absichtlich simuliert.

3. Lösung

Lösung 1: Die erste Reaktion besteht darin, dem Verarbeitungsprozess eine Transaktion hinzuzufügen (die Datenbank ist MySQL innoDB). Das Ergebnis des Hinzufügens der Transaktion ist, dass die erste Anfrage erfolgreich ist und die zweite Anfrage ist erfolgreich. Sie wird erst später ausgeführt und wenn festgestellt wird, dass sie schwer ist, wird sie zurückgesetzt. Tatsächlich sind MySQL-Transaktionen sehr gut darin, die Datenkonsistenz sicherzustellen, aber es ist zu teuer, die ausschließliche Nutzung eindeutiger Ressourcen durch Rollback sicherzustellen. Schüler, die MySQL-Transaktionstests durchgeführt haben, wissen, dass die Einfügung in die Transaktion bereits eingefügt wurde und ein Rollback durchgeführt wird Es wurde später gelöscht.

Option 2: Eine weitere Option ist die exklusive Dateisperre in PHP. Das heißt, in diesem Fall muss ich eine neue Datei mit der Anzahl der Benutzer * der Anzahl der Aufgaben erstellen, um die Exklusivität zu erreichen Jede angeforderte Ressource. Wenn die exklusive Ressource größer ist. Wenn es weniger gibt, optionale Lösungen:

    /**
     * 加锁
     */
    public function file_lock($filename){
        $fp_key = sha1($filename);
        $this->fps[$fp_key] = fopen($filename, 'w+');
        if($this->fps[$fp_key]){
            return flock($this->fps[$fp_key], LOCK_EX|LOCK_NB);
        }
        return false;
    }
    /**
     * 解锁
     */
    public function file_unlock($filename){
        $fp_key = sha1($filename);
        if($this->fps[$fp_key] ){
            flock($this->fps[$fp_key] , LOCK_UN);
            fclose($this->fps[$fp_key] );
        }
    }
Nach dem Login kopieren

Option 3: Es wurde festgestellt, dass $redis->setnx() den Status atomarer Operationen bereitstellen kann: derselbe Schlüssel ist nach der Ausführung von setnx nicht abgelaufen oder gelöscht und wird erneut ausgeführt. Geben Sie false zurück. Dadurch können mehr als zwei gleichzeitige Anforderungen gesteuert werden und die Sperre muss erfolgreich erworben werden, bevor sie fortgesetzt werden können.

    /**
     *  加锁
     */
    public function task_lock($taskid){
            $expire = 2;
             $lock_key ='task_get_reward_'.$this->uid.'_'.$taskid;
            $lock = $this->redis->setNX($lock_key , time());//设当前时间
            if($lock){
                $this->redis->expire($lock_key,  $expire); //如果没执行完 2s锁失效
            }
            if(!$lock){//如果获取锁失败 检查时间
                $time = $this->redis->get($lock_key);
                if(time() - $time  >=  $expire){//添加时间戳判断为了避免expire执行失败导致死锁 当然可以用redis自带的事务来保证
                    $this->redis->rm($lock_key);
                }
                $lock =  $this->redis->setNX($lock_key , time());
                if($lock){
                    $this->redis->expire($lock_key,  $expire); //如果没执行完 2s锁失效
                }
            }
            return $lock;
        }
        /**
         *  解锁
         */
        public function task_unlock($taskid){
            $this->set_redis();
            $lock_key = 'task_get_reward_'.$this->uid.'_'.$taskid;
            $this->redis->rm($lock_key);
        }
Nach dem Login kopieren

Erklären Sie, dass die beiden Vorgänge setNX und Expire tatsächlich Redis-Transaktionen verwenden können, um Konsistenz sicherzustellen

Verwandte Etiketten:
php
Quelle:php.cn
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
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage