müssen Sie bei der Bestellung zwei Punkte beurteilen: 1. Ob der Flash-Sale beginnt oder endet 2. Ob der Lagerbestand ausreicht
Unsere Geschäftslogik lautet also wie folgt folgt
1. Erhalten Sie Coupon-Informationen über die Coupon-ID
2. Stellen Sie fest, ob der Flash-Sale begonnen hat, wenn keine Fehlermeldung zurückgegeben wird. 3. Stellen Sie fest, ob der Flash-Sale beendet ist, und geben Sie eine Fehlermeldung zurück, wenn er beendet ist
4. Wenn es innerhalb der Flash-Sale-Zeit ist, ermitteln Sie, ob der Lagerbestand ausreichend ist.
6.1 Bestell-ID speichern 6.2 Benutzer-ID speichern Der obige Code unter hoher Parallelität. (Mit dem JMX-Tool)
Das Bild unten zeigt, dass zweihundert Threads erstellt und sofort eine Gutscheinanfrage gestellt wurden
Aber wenn wir uns den Aggregationsbericht ansehen, stellen wir fest, dass der Ausreißerwert nur 45,5 % beträgt , was logischerweise 50 % sein sollte. (Da 100 Einheiten auf Lager sind, wurden hier 200 Anfragen gesendet)Wenn man sich die Inventarnummer ansieht, gute Leute, ist sie -9
Die Bestellung hat auch wurden auf 109 Einheiten erhöht, was offensichtlich zu einem Überverkaufsproblem führte.
Schauen Sie sich die Bilder an und sprechen Sie:
Folgen Sie unserem normalen Prozess, das heißt, Thread 1 hat den Bestand überprüft und dann den Bestand abgezogen. Zu diesem Zeitpunkt überprüft Thread 2 den Bestand erneut und zieht den Bestand ab Problem.
Das Problem mit überverkauft liegt darin, dass nach der Überprüfung des Lagerbestands durch Bestellung 1 festgestellt wurde, dass es 1 war, aber bevor der Abzug vorgenommen wurde, überprüfte Thread 2 auch den Lagerbestand und stellte fest, dass es ebenfalls 1 war, und a Es wurde ein Abzug vorgenommen (in Szenarien mit hoher Parallelität)
Dies führt zum Problem des Überverkaufs.
Für diese Art von Problemen mit hoher Parallelität ist die häufigste Lösung: Sperre ~
Aber Sperren umfassen pessimistische Sperren und optimistische Sperren. Pessimistisches Sperren bedeutet einfach: Ich denke, der Thread wird definitiv passieren, und dann nimmt jeder die Sperre vor der Operation. Nachdem Sie mit der Ausführung fertig sind, ist der nächste an der Reihe (serielle Ausführung)Optimistisches Sperren: Es ist optimistisch (Es wird davon ausgegangen, dass Thread-Sicherheit niemals auftritt.) Solange vor jeder Datenänderung beurteilt wird, ob andere Threads die Daten geändert haben, um die Thread-Sicherheit sicherzustellen.Pessimistisches Sperren ist relativ einfach, hier wird optimistisches Sperren implementiert.
Der Schlüssel zum optimistischen Sperren besteht darin, festzustellen, ob die durch die vorherige Abfrage erhaltenen Daten geändert wurden. Warme Erinnerung: Die Daten in der Tabelle links sind alle Daten nach der Ausführung des Threads 1~1. Versionsnummer Die Methode
besteht darin, nach jeder Änderung der Daten eine Versionsnummer hinzuzufügen und am Ende eine Where-Bedingung hinzuzufügen Die Versionsnummer stimmt mit der Version vor der Änderung überein. Auf diese Weise wird die Thread-Sicherheit erreicht Der Bestand ist der Bestand vor der Änderung
Code-Implementierung zur Lösung des Überverkaufsproblems: In der letzten Analyse fügen wir beim Abzug des Bestandes eine Where-Bedingung hinzu, um zu bestimmen, ob der Bestand größer als 0 istpackage com.hmdp.service.impl; import com.hmdp.dto.Result; import com.hmdp.entity.SeckillVoucher; import com.hmdp.entity.VoucherOrder; import com.hmdp.mapper.VoucherOrderMapper; import com.hmdp.service.ISeckillVoucherService; import com.hmdp.service.IVoucherOrderService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.hmdp.utils.RedisIdWorker; import com.hmdp.utils.UserHolder; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.time.LocalDateTime; /** * <p> * 服务实现类 * </p> * * @author 虎哥 * @since 2021-12-22 */ @Service public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService { @Resource private ISeckillVoucherService iSeckillVoucherService; @Resource private RedisIdWorker redisIdWorker; @Override public Result seckillVoucher(Long voucherId) { //1.获取优惠券信息 SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId); //2.判断是否已经开始 if (voucher.getBeginTime().isAfter(LocalDateTime.now())){ Result.fail("秒杀尚未开始!"); } //3.判断是否已经结束 if (voucher.getEndTime().isBefore(LocalDateTime.now())){ Result.fail("秒杀已经结束了!"); } //4.判断库存是否充足 if (voucher.getStock() < 1) { Result.fail("库存不充足!"); } //5.扣减库存 boolean success = iSeckillVoucherService.update() .setSql("stock = stock-1").eq("voucher_id",voucherId) .update(); if (!success){ Result.fail("库存不充足!"); } //6. 创建订单 VoucherOrder voucherOrder = new VoucherOrder(); //6.1添加订单id Long orderId = redisIdWorker.nextId("order"); voucherOrder.setId(orderId); //6.2添加用户id Long userId = UserHolder.getUser().getId(); voucherOrder.setUserId(userId); //6.3添加优惠券id voucherOrder.setVoucherId(voucherId); save(voucherOrder); //7.返回订单id return Result.ok(orderId); } }
//5.1扣减库存 boolean success = iSeckillVoucherService.update() .setSql("stock = stock-1").eq("voucher_id" , voucherId).gt("stock" ,0) .update();
Das obige ist der detaillierte Inhalt vonSo lösen Sie das Redis-Coupon-Flash-Sale-Problem. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!