In diesem Artikel geben wir Ihnen eine detaillierte Erläuterung der MySQL-Transaktionen und der Datenkonsistenzverarbeitung. Bei der Arbeit stoßen wir oft auf solche Probleme. Wenn wir den verfügbaren Bestand abfragen und ihn ändern, kann es sein, dass andere Benutzer die Bestandsdaten geändert haben Schauen wir uns die folgenden Lösungen an.
In MySQLs InnoDB ist die Standardisolationsstufe von Tansaction REPEATABLE READ (wieder lesbar)
Wenn Sie dasselbe Formular nach SELECT AKTUALISIEREN möchten, verwenden Sie am besten SELECT ... UPDATE.
Zum Beispiel:
Gehen Sie davon aus, dass im Produktformular eine Menge zur Lagerung der Warenmenge vorhanden ist. Bevor die Bestellung erstellt wird, muss festgestellt werden, ob die Warenmenge ausreicht (. Menge>0) und dann ist die Menge Die Menge wird auf 1 aktualisiert. Der Code lautet wie folgt:
SELECT quantity FROM products WHERE id=3; UPDATE products SET quantity = 1 WHERE id=3;
In einigen wenigen Fällen gibt es möglicherweise kein Problem, aber bei einer großen Menge an Datenzugriffen wird dies „definitiv“ der Fall sein. Probleme verursachen. Wenn wir den Lagerbestand abziehen müssen, wenn die Menge > 0 ist, gehen wir davon aus, dass die vom Programm in der ersten SELECT-Zeile gelesene Menge 2 ist. Die Zahl scheint korrekt zu sein, aber wenn MySQL sich auf die Aktualisierung vorbereitet, hat möglicherweise bereits jemand den Lagerbestand abgezogen . Es wurde 0, aber das Programm wusste es nicht und setzte das falsche UPDATE fort. Daher muss ein Transaktionsmechanismus verwendet werden, um sicherzustellen, dass die gelesenen und übermittelten Daten korrekt sind.
Damit wir es so in MySQL testen können, lautet der Code wie folgt:
SET AUTOCOMMIT=0; BEGIN WORK; SELECT quantity FROM products WHERE id=3 FOR UPDATE;
Zu diesem Zeitpunkt sind die Daten mit der ID=3 in den Produktdaten gesperrt (Hinweis 3). ), und andere Transaktionen müssen darauf warten
SELECT * FROM products WHERE id=3 FOR UPDATE
können erst ausgeführt werden, nachdem die Transaktion übermittelt wurde. Dadurch wird sichergestellt, dass die in anderen Transaktionen gelesenen Zahlen korrekt sind.
UPDATE products SET quantity = '1' WHERE id=3 ; COMMIT WORK;
Commit wird in die Datenbank geschrieben und Produkte werden entsperrt.
Hinweis 1: BEGIN/COMMIT ist der Start- und Endpunkt der Transaktion. Sie können mehr als zwei MySQL-Befehlsfenster verwenden, um den Sperrstatus interaktiv zu beobachten.
Hinweis 2: Während der Transaktion warten nur SELECT ... FOR UPDATE
oder LOCK IN SHARE MODE mit denselben Daten auf das Ende anderer Transaktionen, bevor sie ausgeführt werden. Im Allgemeinen ist SELECT... davon nicht betroffen.
Hinweis 3: Da InnoDB standardmäßig die Sperre auf Zeilenebene verwendet, lesen Sie bitte diesen Artikel zum Sperren von Datenspalten.
Hinweis 4: Versuchen Sie, den Befehl LOCK TABLES nicht in InnoDB-Formularen zu verwenden. Wenn Sie ihn verwenden müssen, lesen Sie bitte zuerst die offiziellen Anweisungen zur Verwendung von LOCK TABLES in InnoDB, um häufige Deadlocks im System zu vermeiden.
Wenn wir zuerst die Daten abfragen und dann aktualisieren müssen, ist es am besten, die Anweisung wie folgt zu verwenden:
UPDATE products SET quantity = '1' WHERE id=3 AND quantity > 0;
Auf diese Weise Es besteht keine Notwendigkeit, Dinge hinzuzufügen, mit denen man sich befassen muss.
Ich habe einen sehr guten Artikel gesehen und leite ihn hiermit zum Lernen weiter.
Heute hat Herr Wang uns eine weitere Lektion erteilt. Tatsächlich hat Herr Wang letztes Jahr bereits das Problem erwähnt, dass MySQL mit hoher Parallelität umgeht und überverkaufte Bestände verhindert Ich verstehe, aber in der tatsächlichen Entwicklung ist mir dieser Aspekt noch nicht bewusst. Heute werde ich einige meiner Erkenntnisse zu diesem Thema klären und hoffe, dass es in Zukunft weitere Kurse dieser Art geben wird.
Beschreiben wir zunächst das Problem überverkaufter Bestände: Im Allgemeinen kommt es auf E-Commerce-Websites zu Aktivitäten wie Gruppenkäufen, Flash-Sales und Sonderangeboten Online-Verkäufe. Tausende oder sogar Zehntausende Menschen beeilen sich, ein Produkt zu kaufen. Da es sich jedoch um ein aktives Gut handelt, ist die Kontrolle des Lagerbestands zur Vermeidung von Überkäufen und unnötigen Verlusten für viele E-Commerce-Website-Programmierer ein Problem.
Aus technischer Sicht werden viele Menschen sicherlich an Transaktionen denken, aber Transaktionen sind eine notwendige Bedingung für die Kontrolle überverkaufter Bestände, aber keine notwendige und hinreichende Bedingung.
Beispiel:
Gesamtbestand: 4 Artikel
Anforderer: a, 1 Artikel b, 2 Artikel c, 3 Artikel
Das Programm sieht wie folgt aus :
beginTranse(开启事务) try{ $result = $dbca->query('select amount from s_store where postID = 12345'); if(result->amount > 0){ //quantity为请求减掉的库存数量 $dbca->query('update s_store set amount = amount - quantity where postID = 12345'); } }catch($e Exception){ rollBack(回滚) } commit(提交事务)
Der obige Code ist der Code, den wir normalerweise in das Kontrollinventar schreiben. Es scheint, dass es kein großes Problem gibt, aber tatsächlich birgt er große Lücken. Beim Datenbankzugriff handelt es sich tatsächlich um den Zugriff auf Festplattendateien. Bei den Tabellen in der Datenbank handelt es sich tatsächlich um auf der Festplatte gespeicherte Dateien, und sogar eine Datei enthält mehrere Tabellen. Aufgrund der hohen Parallelität haben beispielsweise derzeit drei Benutzer a, b und c diese Transaktion eingegeben. Zu diesem Zeitpunkt wird eine gemeinsame Sperre generiert, sodass bei der Auswahl die von diesen drei Benutzern gefundenen Inventarmengen alle 4 sind. Beachten Sie außerdem, dass die von mysql innodb gefundenen Ergebnisse einer Versionskontrolle unterliegen (d. h. bevor eine neue Version generiert wird), die vom aktuellen Benutzer gefundenen Ergebnisse immer noch dieselben sind > Dann aktualisieren: Wenn diese drei Benutzer gleichzeitig bei Update eintreffen, serialisiert die Update-Anweisung die Parallelität zu diesem Zeitpunkt, dh sortiert die drei gleichzeitig eintreffenden Benutzer, führt sie einzeln aus und generiert exklusive Sperren, bevor die aktuelle Aktualisierungsanweisung festgeschrieben wird, warten andere Benutzer auf die Ausführung. Nach der Festschreibung muss eine neue Version generiert werden. Wenn wir den Code jedoch ändern, tritt das Überkauft-Phänomen gemäß der obigen Beschreibung nicht auf. Der Code lautet wie folgt:
Außerdem eine prägnantere Methode:beginTranse(开启事务) try{ //quantity为请求减掉的库存数量 $dbca->query('update s_store set amount = amount - quantity where postID = 12345'); $result = $dbca->query('select amount from s_store where postID = 12345'); if(result->amount < 0){ throw new Exception('库存不足'); } }catch($e Exception){ rollBack(回滚) } commit(提交事务)
beginTranse(开启事务) try{ //quantity为请求减掉的库存数量 $dbca->query('update s_store set amount = amount - quantity where amount>=quantity and postID = 12345'); }catch($e Exception){ rollBack(回滚) } commit(提交事务)
=====================================================================================
1、在秒杀的情况下,肯定不能如此高频率的去读写数据库,会严重造成性能问题的
必须使用缓存,将需要秒杀的商品放入缓存中,并使用锁来处理其并发情况。当接到用户秒杀提交订单的情况下,先将商品数量递减(加锁/解锁)后再进行其他方面的处理,处理失败在将数据递增1(加锁/解锁),否则表示交易成功。
当商品数量递减到0时,表示商品秒杀完毕,拒绝其他用户的请求。
2、这个肯定不能直接操作数据库的,会挂的。直接读库写库对数据库压力太大,要用缓存。
把你要卖出的商品比如10个商品放到缓存中;然后在memcache里设置一个计数器来记录请求数,这个请求书你可以以你要秒杀卖出的商品数为基数,比如你想卖出10个商品,只允许100个请求进来。那当计数器达到100的时候,后面进来的就显示秒杀结束,这样可以减轻你的服务器的压力。然后根据这100个请求,先付款的先得后付款的提示商品以秒杀完。
3、首先,多用户并发修改同一条记录时,肯定是后提交的用户将覆盖掉前者提交的结果了。
这个直接可以使用加锁机制去解决,乐观锁或者悲观锁。乐观锁:
,就是在数据库设计一个版本号的字段,每次修改都使其+1,这样在提交时比对提交前的版本号就知道是不是并发提交了,但是有个缺点就是只能是应用中控制,如果有跨应用修改同一条数据乐观锁就没办法了,这个时候可以考虑悲观锁。
悲观锁:
,就是直接在数据库层面将数据锁死,类似于oralce中使用select xxxxx from xxxx where xx=xx for update
,这样其他线程将无法提交数据。
除了加锁的方式也可以使用接收锁定的方式,思路是在数据库中设计一个状态标识位,用户在对数据进行修改前,将状态标识位标识为正在编辑的状态,这样其他用户要编辑此条记录时系统将发现有其他用户正在编辑,则拒绝其编辑的请求,类似于你在操作系统中某文件正在执行,然后你要修改该文件时,系统会提醒你该文件不可编辑或删除。
4、不建议在数据库层面加锁,建议通过服务端的内存锁(锁主键)。当某个用户要修改某个id的数据时,把要修改的id存入memcache,若其他用户触发修改此id的数据时,读到memcache有这个id的值时,就阻止那个用户修改。
5、实际应用中,并不是让mysql去直面大并发读写,会借助“外力”,比如缓存、利用主从库实现读写分离、分表、使用队列写入等方法来降低并发读写。
首先,多用户并发修改同一条记录时,肯定是后提交的用户将覆盖掉前者提交的结果了。这个直接可以使用加锁机制去解决,乐观锁或者悲观锁。
悲观锁(Pessimistic Lock)
, 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁(Optimistic Lock)
, 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
两种锁各有优缺点,不能单纯的定义哪个好于哪个。乐观锁比较适合数据修改比较少,读取比较频繁的场景,即使出现了少量的冲突,这样也省去了大量的锁的开销,故而提高了系统的吞吐量。但是如果经常发生冲突(写数据比较多的情况下),上层应用不不断的retry,这样反而降低了性能,对于这种情况使用悲观锁就更合适。
对这个表的 amount 进行修改,开两个命令行窗口
第一个窗口A;
SET AUTOCOMMIT=0; BEGIN WORK; SELECT * FROM order_tbl WHERE order_id='124' FOR UPDATE;
第二个窗口B:
# 更新订单ID 124 的库存数量 UPDATE `order_tbl` SET amount = 1 WHERE order_id = 124;
我们可以看到窗口A加了事物,锁住了这条数据,窗口B执行时会出现这样的问题:
第一个窗口完整的提交事物:
SET AUTOCOMMIT=0; BEGIN WORK; SELECT * FROM order_tbl WHERE order_id='124' FOR UPDATE; UPDATE `order_tbl` SET amount = 10 WHERE order_id = 124; COMMIT WORK;
相关推荐:
Das obige ist der detaillierte Inhalt vonAusführliche Erläuterung der MySQL-Transaktionen und der Datenkonsistenzverarbeitung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!