Heim >Datenbank >Redis >Wie man Go und Lua verwendet, um die Lager- und Überverkaufsprobleme im Redis-Flash-Sale zu lösen

Wie man Go und Lua verwendet, um die Lager- und Überverkaufsprobleme im Redis-Flash-Sale zu lösen

王林
王林nach vorne
2023-05-26 15:57:411080Durchsuche

0, Einführung

  • Go-Sprache verbindet go-redis, um eine Verbindung zur Datenbank herzustellen. Wenn Sie diesen Teil noch nicht verstehen, wird dies empfohlen Lernen Sie diesen Teil des Wissens zuerst.

  • Darüber hinaus löst dieser Flash-Sale hauptsächlich zwei Probleme, das erste ist das Überverkaufsproblem und das andere das Lagerbestandsproblem.

  • Es gibt keine spezielle Seite zur Simulation der Parallelität. Wir verwenden Gorountine direkt und bleiben 10 Sekunden lang, bevor wir die Anfrage aufrufen.

  • Um das Überverkaufsproblem anzugehen, führen Sie einfach go-redis watch mit Transaktionsverarbeitung ein [äquivalent zu optimistischem Sperren].

Das Inventarproblem ist etwas problematischer. Sie müssen Lua verwenden, um das Skript zu bearbeiten, aber Sie müssen die Lua-Kompilierungsumgebung nicht selbst herunterladen Go bietet die entsprechende Unterstützung. Für diesen Teil keine Panik, die Grundstruktur ist wie folgt:

Wie man Go und Lua verwendet, um die Lager- und Überverkaufsprobleme im Redis-Flash-Sale zu lösen

1. Einfache Version

Für den Fall In der Redis-Datenbank können Parallelität, Super Sell und negative Werte auftreten. Auch wenn Sie vor dem Betrieb eine Beurteilung der Daten vornehmen.

func MsCode(uuid, prodid string) bool {  
   // 1、对uuid和prodid进行非空判断  
   if uuid == "" || prodid == "" {  
      return false  
   }  
  
   //2、获取连接  
   rdb := DB  
  
   //3、拼接key  
   kcKey := "kc:" + prodid + ":qt"  
   userKey := "sk:" + prodid + ":user"  
  
   //4、获取库存  
   str, err := rdb.Get(ctx, kcKey).Result()  
   if err != nil {  
      fmt.Println(err)  
      fmt.Println("秒杀还未开始.......")  
      return false  
   }  
  
   // 5、判断用户是否重复秒杀操作  
   flag, err := rdb.SIsMember(ctx, userKey, userKey).Result()  
   if err != nil {  
      fmt.Println(err)  
   }  
   if flag {  
      fmt.Println("你已经参加了秒杀,无法再次参加。。。。")  
      return false  
   }  
  
   // 6、判断商品数量,如果库存数量小于1,秒杀结束  
   str, err = rdb.Get(ctx, kcKey).Result()  
   if err != nil {  
      fmt.Println(err)  
   }  
   n, err := strconv.Atoi(str)  
   if err != nil {  
      fmt.Println(err)  
   }  
   if n < 1 {  
      fmt.Println("秒杀结束,请下次再来吧。。。。")  
      return false  
   }  
  
   // 7、秒杀过程  
   // 7.1、库存减1  
   num, err := rdb.Decr(ctx, kcKey).Result()  
   if err != nil {  
      fmt.Println(err)  
   }  
   if num != 0 {  
      // 7.2、添加用户  
      rdb.SAdd(ctx, userKey, uuid)  
   }  
   return true  
}

func main() {
    // 并发的版本
    for i := 0; i < 20; i++ {
        go func() {
            uuid := GenerateUUID()
            prodid := "1023"
            time.Sleep(10 * time.Second)
            MsCode(uuid, prodid)
        }()
    }
    time.Sleep(15 * time.Second)
}

2. Überverkauft

Verwenden Sie die Uhr, um den Schlüssel zu überwachen. Diese Situation wird jedoch ein Problem mit sich bringen. Selbst wenn noch Restbestände vorhanden sind, wird es Leute geben, die diese nicht kaufen können.

err = rdb.Watch(ctx, func(tx *redis.Tx) error {  
   n, err := tx.Get(ctx, kcKey).Int()  
   if err != nil && err != redis.Nil {  
      return err  
   }  
   if n <= 0 {  
      return fmt.Errorf("抢购结束了!请下次早点来。。。。")  
   }  
   _, err = tx.TxPipelined(ctx, func(pipeliner redis.Pipeliner) error {  
      err := pipeliner.Decr(ctx, kcKey).Err()  
      if err != nil {  
         return err  
      }  
      err = pipeliner.SAdd(ctx, userKey, uuid).Err()  
      if err != nil {  
         return err  
      }  
      return nil  
   })  
   return err  
}, kcKey)

3. Lösen Sie das Inventarproblem Lua

Lua kann dieses Problem besser lösen, indem es Redis betreibt. Um Bestandsprobleme zu vermeiden, die durch pessimistische Sperren in Redis verursacht werden können, sollten Sie die Verwendung optimistischer Sperren in Betracht ziehen. Da Redis nicht über eine integrierte Unterstützung für optimistisches Sperren verfügt, müssen wir Lua verwenden, um relevante Skripte zu schreiben. Es hat hauptsächlich die folgenden Vorteile:

  • Schreiben Sie komplexe oder mehrstufige Redis-Vorgänge als Skript und senden Sie es zur gleichzeitigen Ausführung an Redis, wodurch die Anzahl der Wiederholungen verringert wird Redis-Verbindungen. Verbessern Sie die Leistung.

  • Luan-Skript ähnelt Redis-Transaktionen, weist eine gewisse Atomizität auf, wird nicht von anderen Befehlen in die Warteschlange gestellt und kann einige Redis-Transaktionsvorgänge abschließen.

  • Die Lua-Skriptfunktion von Redis kann nur in Versionen über Redis2.6 verwendet werden.

  • Verwenden Sie Lua-Skripte, um Benutzer zu eliminieren und das Problem der Überverkäufe zu lösen.

  • Nach Version 2.6 von Redis wird das Konfliktproblem durch Lua-Skript gelöst. Tatsächlich verwendet Redis seine Single-Threaded-Funktion, um Multitasking-Parallelitätsprobleme mithilfe von Tasks zu lösen Warteschlangen.

import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
    "net"
    "time"
)

func useLua(userid, prodid string) bool {
    //编写脚本 - 检查数值,是否够用,够用再减,否则返回减掉后的结果
    var luaScript = redis.NewScript(`
        local userid=KEYS[1];
        local prodid=KEYS[2];
        local qtKey="sk:"..prodid..":qt";
        local userKey="sk:"..prodid..":user";
        local userExists=redis.call("sismember",userKey,userid);
        if tonumber(userExists)==1 then
         return 2;
        end
        local num=redis.call("get",qtKey);
        if tonumber(num)<=0 then
         return 0;
        else
         redis.call("decr",qtKey);
         redis.call("SAdd",userKey,userid);
        end
        return 1;
    `)
    //执行脚本
    n, err := luaScript.Run(ctx, DB, []string{userid, prodid}).Result()
    if err != nil {
        return false
    }
    switch n {
    case int64(0):
        fmt.Println("抢购结束")
        return false
    case int64(1):
        fmt.Println(userid, ":抢购成功")
        return true
    case int64(2):
        fmt.Println(userid, ":已经抢购了")
        return false
    default:
        fmt.Println("发生未知错误!")
        return false
    }
    return true
}

func main() {
    // 并发的版本
    for i := 0; i < 20; i++ {
        go func() {
            uuid := GenerateUUID()
            prodid := "1023"
            time.Sleep(10 * time.Second)
            useLua(uuid, prodid)
        }()
    }
    time.Sleep(15 * time.Second)
}

Das obige ist der detaillierte Inhalt vonWie man Go und Lua verwendet, um die Lager- und Überverkaufsprobleme im Redis-Flash-Sale zu lösen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen