ZAPYTANIA NIE ZAWSZE DZIAŁAJĄ RÓWNOLEGLE
To zależy od silnika bazy danych. Dzięki MyISAM prawie każde zapytanie uzyskuje blokadę na poziomie tabeli, co oznacza, że zapytania są uruchamiane sekwencyjnie jako kolejka. Z większością innych silników mogą działać równolegle.
echo_me mówi, że nothing happens at the exact same time and a CPU does not do everything at once
To nie do końca prawda. Możliwe, że DBMS może działać na maszynie z więcej niż jednym procesorem iz więcej niż jednym interfejsem sieciowym. Jest bardzo jest mało prawdopodobne, aby 2 zapytania mogły nadejść w tym samym czasie - ale nie niemożliwe, dlatego istnieje muteks zapewniający, że przejście parowania/wykonywania działa tylko jako pojedynczy wątek (wykonania - niekoniecznie ten sam lekki proces).
Istnieją dwa podejścia do rozwiązywania współbieżnego DML - albo użyć transakcji (gdzie każdy użytkownik skutecznie otrzymuje klon bazy danych), a kiedy zapytania zostaną zakończone, DBMS próbuje uzgodnić wszelkie zmiany - jeśli uzgadnianie się nie powiedzie, DBMS wycofuje jeden z zapytania i zgłasza to jako nieudane. Innym podejściem jest użycie blokowania na poziomie wiersza - DBMS identyfikuje wiersze, które będą aktualizowane przez zapytanie i oznacza je jako zarezerwowane do aktualizacji (inni użytkownicy mogą odczytać oryginalną wersję każdego wiersza, ale każda próba aktualizacji danych będzie zablokowane, dopóki wiersz nie będzie ponownie dostępny).
Twój problem polega na tym, że masz dwóch klientów mysql, z których każdy odzyskał informację, że pozostała jedna pozycja w magazynie. Jest to jeszcze bardziej skomplikowane ze względu na fakt, że (ponieważ wspominasz o PHP) stany magazynowe mogły zostać pobrane w innej sesji DBMS niż późniejsza korekta zapasów - nie możesz mieć transakcji obejmującej więcej niż żądanie HTTP. Dlatego musisz ponownie zweryfikować każdy fakt utrzymywany poza DBMS w ramach jednej transakcji.
Optymistyczne blokowanie może stworzyć pseudo - mechanizm kontroli transakcji - oznaczasz rekord, który zamierzasz zmodyfikować, znacznikiem czasu i identyfikatorem użytkownika (w przypadku PHP identyfikator sesji PHP jest dobrym wyborem) - jeśli chcesz go zmodyfikować, coś innego zmienił go, to twój kod wie, że dane, które pobrał wcześniej, są nieprawidłowe. Może to jednak prowadzić do innych komplikacji.